svg changes
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     },
1019     /**
1020      * equals
1021      * @param {Array} o The array to compare to
1022      * @returns {Boolean} true if the same
1023      */
1024     equals : function(b)
1025     {
1026         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1027         if (this === b) {
1028             return true;
1029          }
1030         if (b == null) {
1031             return false;
1032         }
1033         if (this.length !== b.length) {
1034             return false;
1035         }
1036       
1037         // sort?? a.sort().equals(b.sort());
1038       
1039         for (var i = 0; i < this.length; ++i) {
1040             if (this[i] !== b[i]) {
1041                 return false;
1042             }
1043         }
1044         return true;
1045     }
1046 });
1047
1048
1049  
1050 /*
1051  * Based on:
1052  * Ext JS Library 1.1.1
1053  * Copyright(c) 2006-2007, Ext JS, LLC.
1054  *
1055  * Originally Released Under LGPL - original licence link has changed is not relivant.
1056  *
1057  * Fork - LGPL
1058  * <script type="text/javascript">
1059  */
1060
1061 /**
1062  * @class Date
1063  *
1064  * The date parsing and format syntax is a subset of
1065  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1066  * supported will provide results equivalent to their PHP versions.
1067  *
1068  * Following is the list of all currently supported formats:
1069  *<pre>
1070 Sample date:
1071 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1072
1073 Format  Output      Description
1074 ------  ----------  --------------------------------------------------------------
1075   d      10         Day of the month, 2 digits with leading zeros
1076   D      Wed        A textual representation of a day, three letters
1077   j      10         Day of the month without leading zeros
1078   l      Wednesday  A full textual representation of the day of the week
1079   S      th         English ordinal day of month suffix, 2 chars (use with j)
1080   w      3          Numeric representation of the day of the week
1081   z      9          The julian date, or day of the year (0-365)
1082   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1083   F      January    A full textual representation of the month
1084   m      01         Numeric representation of a month, with leading zeros
1085   M      Jan        Month name abbreviation, three letters
1086   n      1          Numeric representation of a month, without leading zeros
1087   t      31         Number of days in the given month
1088   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1089   Y      2007       A full numeric representation of a year, 4 digits
1090   y      07         A two digit representation of a year
1091   a      pm         Lowercase Ante meridiem and Post meridiem
1092   A      PM         Uppercase Ante meridiem and Post meridiem
1093   g      3          12-hour format of an hour without leading zeros
1094   G      15         24-hour format of an hour without leading zeros
1095   h      03         12-hour format of an hour with leading zeros
1096   H      15         24-hour format of an hour with leading zeros
1097   i      05         Minutes with leading zeros
1098   s      01         Seconds, with leading zeros
1099   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1100   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1101   T      CST        Timezone setting of the machine running the code
1102   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1103 </pre>
1104  *
1105  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1106  * <pre><code>
1107 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1108 document.write(dt.format('Y-m-d'));                         //2007-01-10
1109 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1110 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
1111  </code></pre>
1112  *
1113  * Here are some standard date/time patterns that you might find helpful.  They
1114  * are not part of the source of Date.js, but to use them you can simply copy this
1115  * block of code into any script that is included after Date.js and they will also become
1116  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1117  * <pre><code>
1118 Date.patterns = {
1119     ISO8601Long:"Y-m-d H:i:s",
1120     ISO8601Short:"Y-m-d",
1121     ShortDate: "n/j/Y",
1122     LongDate: "l, F d, Y",
1123     FullDateTime: "l, F d, Y g:i:s A",
1124     MonthDay: "F d",
1125     ShortTime: "g:i A",
1126     LongTime: "g:i:s A",
1127     SortableDateTime: "Y-m-d\\TH:i:s",
1128     UniversalSortableDateTime: "Y-m-d H:i:sO",
1129     YearMonth: "F, Y"
1130 };
1131 </code></pre>
1132  *
1133  * Example usage:
1134  * <pre><code>
1135 var dt = new Date();
1136 document.write(dt.format(Date.patterns.ShortDate));
1137  </code></pre>
1138  */
1139
1140 /*
1141  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1142  * They generate precompiled functions from date formats instead of parsing and
1143  * processing the pattern every time you format a date.  These functions are available
1144  * on every Date object (any javascript function).
1145  *
1146  * The original article and download are here:
1147  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1148  *
1149  */
1150  
1151  
1152  // was in core
1153 /**
1154  Returns the number of milliseconds between this date and date
1155  @param {Date} date (optional) Defaults to now
1156  @return {Number} The diff in milliseconds
1157  @member Date getElapsed
1158  */
1159 Date.prototype.getElapsed = function(date) {
1160         return Math.abs((date || new Date()).getTime()-this.getTime());
1161 };
1162 // was in date file..
1163
1164
1165 // private
1166 Date.parseFunctions = {count:0};
1167 // private
1168 Date.parseRegexes = [];
1169 // private
1170 Date.formatFunctions = {count:0};
1171
1172 // private
1173 Date.prototype.dateFormat = function(format) {
1174     if (Date.formatFunctions[format] == null) {
1175         Date.createNewFormat(format);
1176     }
1177     var func = Date.formatFunctions[format];
1178     return this[func]();
1179 };
1180
1181
1182 /**
1183  * Formats a date given the supplied format string
1184  * @param {String} format The format string
1185  * @return {String} The formatted date
1186  * @method
1187  */
1188 Date.prototype.format = Date.prototype.dateFormat;
1189
1190 // private
1191 Date.createNewFormat = function(format) {
1192     var funcName = "format" + Date.formatFunctions.count++;
1193     Date.formatFunctions[format] = funcName;
1194     var code = "Date.prototype." + funcName + " = function(){return ";
1195     var special = false;
1196     var ch = '';
1197     for (var i = 0; i < format.length; ++i) {
1198         ch = format.charAt(i);
1199         if (!special && ch == "\\") {
1200             special = true;
1201         }
1202         else if (special) {
1203             special = false;
1204             code += "'" + String.escape(ch) + "' + ";
1205         }
1206         else {
1207             code += Date.getFormatCode(ch);
1208         }
1209     }
1210     /** eval:var:zzzzzzzzzzzzz */
1211     eval(code.substring(0, code.length - 3) + ";}");
1212 };
1213
1214 // private
1215 Date.getFormatCode = function(character) {
1216     switch (character) {
1217     case "d":
1218         return "String.leftPad(this.getDate(), 2, '0') + ";
1219     case "D":
1220         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1221     case "j":
1222         return "this.getDate() + ";
1223     case "l":
1224         return "Date.dayNames[this.getDay()] + ";
1225     case "S":
1226         return "this.getSuffix() + ";
1227     case "w":
1228         return "this.getDay() + ";
1229     case "z":
1230         return "this.getDayOfYear() + ";
1231     case "W":
1232         return "this.getWeekOfYear() + ";
1233     case "F":
1234         return "Date.monthNames[this.getMonth()] + ";
1235     case "m":
1236         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1237     case "M":
1238         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1239     case "n":
1240         return "(this.getMonth() + 1) + ";
1241     case "t":
1242         return "this.getDaysInMonth() + ";
1243     case "L":
1244         return "(this.isLeapYear() ? 1 : 0) + ";
1245     case "Y":
1246         return "this.getFullYear() + ";
1247     case "y":
1248         return "('' + this.getFullYear()).substring(2, 4) + ";
1249     case "a":
1250         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1251     case "A":
1252         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1253     case "g":
1254         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1255     case "G":
1256         return "this.getHours() + ";
1257     case "h":
1258         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1259     case "H":
1260         return "String.leftPad(this.getHours(), 2, '0') + ";
1261     case "i":
1262         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1263     case "s":
1264         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1265     case "O":
1266         return "this.getGMTOffset() + ";
1267     case "P":
1268         return "this.getGMTColonOffset() + ";
1269     case "T":
1270         return "this.getTimezone() + ";
1271     case "Z":
1272         return "(this.getTimezoneOffset() * -60) + ";
1273     default:
1274         return "'" + String.escape(character) + "' + ";
1275     }
1276 };
1277
1278 /**
1279  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1280  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1281  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1282  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1283  * string or the parse operation will fail.
1284  * Example Usage:
1285 <pre><code>
1286 //dt = Fri May 25 2007 (current date)
1287 var dt = new Date();
1288
1289 //dt = Thu May 25 2006 (today's month/day in 2006)
1290 dt = Date.parseDate("2006", "Y");
1291
1292 //dt = Sun Jan 15 2006 (all date parts specified)
1293 dt = Date.parseDate("2006-1-15", "Y-m-d");
1294
1295 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1296 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1297 </code></pre>
1298  * @param {String} input The unparsed date as a string
1299  * @param {String} format The format the date is in
1300  * @return {Date} The parsed date
1301  * @static
1302  */
1303 Date.parseDate = function(input, format) {
1304     if (Date.parseFunctions[format] == null) {
1305         Date.createParser(format);
1306     }
1307     var func = Date.parseFunctions[format];
1308     return Date[func](input);
1309 };
1310 /**
1311  * @private
1312  */
1313
1314 Date.createParser = function(format) {
1315     var funcName = "parse" + Date.parseFunctions.count++;
1316     var regexNum = Date.parseRegexes.length;
1317     var currentGroup = 1;
1318     Date.parseFunctions[format] = funcName;
1319
1320     var code = "Date." + funcName + " = function(input){\n"
1321         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1322         + "var d = new Date();\n"
1323         + "y = d.getFullYear();\n"
1324         + "m = d.getMonth();\n"
1325         + "d = d.getDate();\n"
1326         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1327         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1328         + "if (results && results.length > 0) {";
1329     var regex = "";
1330
1331     var special = false;
1332     var ch = '';
1333     for (var i = 0; i < format.length; ++i) {
1334         ch = format.charAt(i);
1335         if (!special && ch == "\\") {
1336             special = true;
1337         }
1338         else if (special) {
1339             special = false;
1340             regex += String.escape(ch);
1341         }
1342         else {
1343             var obj = Date.formatCodeToRegex(ch, currentGroup);
1344             currentGroup += obj.g;
1345             regex += obj.s;
1346             if (obj.g && obj.c) {
1347                 code += obj.c;
1348             }
1349         }
1350     }
1351
1352     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1353         + "{v = new Date(y, m, d, h, i, s);}\n"
1354         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1355         + "{v = new Date(y, m, d, h, i);}\n"
1356         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1357         + "{v = new Date(y, m, d, h);}\n"
1358         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1359         + "{v = new Date(y, m, d);}\n"
1360         + "else if (y >= 0 && m >= 0)\n"
1361         + "{v = new Date(y, m);}\n"
1362         + "else if (y >= 0)\n"
1363         + "{v = new Date(y);}\n"
1364         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1365         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1366         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1367         + ";}";
1368
1369     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1370     /** eval:var:zzzzzzzzzzzzz */
1371     eval(code);
1372 };
1373
1374 // private
1375 Date.formatCodeToRegex = function(character, currentGroup) {
1376     switch (character) {
1377     case "D":
1378         return {g:0,
1379         c:null,
1380         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1381     case "j":
1382         return {g:1,
1383             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // day of month without leading zeroes
1385     case "d":
1386         return {g:1,
1387             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{2})"}; // day of month with leading zeroes
1389     case "l":
1390         return {g:0,
1391             c:null,
1392             s:"(?:" + Date.dayNames.join("|") + ")"};
1393     case "S":
1394         return {g:0,
1395             c:null,
1396             s:"(?:st|nd|rd|th)"};
1397     case "w":
1398         return {g:0,
1399             c:null,
1400             s:"\\d"};
1401     case "z":
1402         return {g:0,
1403             c:null,
1404             s:"(?:\\d{1,3})"};
1405     case "W":
1406         return {g:0,
1407             c:null,
1408             s:"(?:\\d{2})"};
1409     case "F":
1410         return {g:1,
1411             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1412             s:"(" + Date.monthNames.join("|") + ")"};
1413     case "M":
1414         return {g:1,
1415             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1416             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1417     case "n":
1418         return {g:1,
1419             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1420             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1421     case "m":
1422         return {g:1,
1423             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1424             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1425     case "t":
1426         return {g:0,
1427             c:null,
1428             s:"\\d{1,2}"};
1429     case "L":
1430         return {g:0,
1431             c:null,
1432             s:"(?:1|0)"};
1433     case "Y":
1434         return {g:1,
1435             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{4})"};
1437     case "y":
1438         return {g:1,
1439             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1440                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1441             s:"(\\d{1,2})"};
1442     case "a":
1443         return {g:1,
1444             c:"if (results[" + currentGroup + "] == 'am') {\n"
1445                 + "if (h == 12) { h = 0; }\n"
1446                 + "} else { if (h < 12) { h += 12; }}",
1447             s:"(am|pm)"};
1448     case "A":
1449         return {g:1,
1450             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1451                 + "if (h == 12) { h = 0; }\n"
1452                 + "} else { if (h < 12) { h += 12; }}",
1453             s:"(AM|PM)"};
1454     case "g":
1455     case "G":
1456         return {g:1,
1457             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1458             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1459     case "h":
1460     case "H":
1461         return {g:1,
1462             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1464     case "i":
1465         return {g:1,
1466             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1467             s:"(\\d{2})"};
1468     case "s":
1469         return {g:1,
1470             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1471             s:"(\\d{2})"};
1472     case "O":
1473         return {g:1,
1474             c:[
1475                 "o = results[", currentGroup, "];\n",
1476                 "var sn = o.substring(0,1);\n", // get + / - sign
1477                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1478                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1479                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1480                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1481             ].join(""),
1482             s:"([+\-]\\d{2,4})"};
1483     
1484     
1485     case "P":
1486         return {g:1,
1487                 c:[
1488                    "o = results[", currentGroup, "];\n",
1489                    "var sn = o.substring(0,1);\n",
1490                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1491                    "var mn = o.substring(4,6) % 60;\n",
1492                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1493                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1494             ].join(""),
1495             s:"([+\-]\\d{4})"};
1496     case "T":
1497         return {g:0,
1498             c:null,
1499             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1500     case "Z":
1501         return {g:1,
1502             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1503                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1504             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1505     default:
1506         return {g:0,
1507             c:null,
1508             s:String.escape(character)};
1509     }
1510 };
1511
1512 /**
1513  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1514  * @return {String} The abbreviated timezone name (e.g. 'CST')
1515  */
1516 Date.prototype.getTimezone = function() {
1517     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1518 };
1519
1520 /**
1521  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1522  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1523  */
1524 Date.prototype.getGMTOffset = function() {
1525     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1526         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1527         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1528 };
1529
1530 /**
1531  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1532  * @return {String} 2-characters representing hours and 2-characters representing minutes
1533  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1534  */
1535 Date.prototype.getGMTColonOffset = function() {
1536         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538                 + ":"
1539                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1540 }
1541
1542 /**
1543  * Get the numeric day number of the year, adjusted for leap year.
1544  * @return {Number} 0 through 364 (365 in leap years)
1545  */
1546 Date.prototype.getDayOfYear = function() {
1547     var num = 0;
1548     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1549     for (var i = 0; i < this.getMonth(); ++i) {
1550         num += Date.daysInMonth[i];
1551     }
1552     return num + this.getDate() - 1;
1553 };
1554
1555 /**
1556  * Get the string representation of the numeric week number of the year
1557  * (equivalent to the format specifier 'W').
1558  * @return {String} '00' through '52'
1559  */
1560 Date.prototype.getWeekOfYear = function() {
1561     // Skip to Thursday of this week
1562     var now = this.getDayOfYear() + (4 - this.getDay());
1563     // Find the first Thursday of the year
1564     var jan1 = new Date(this.getFullYear(), 0, 1);
1565     var then = (7 - jan1.getDay() + 4);
1566     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1567 };
1568
1569 /**
1570  * Whether or not the current date is in a leap year.
1571  * @return {Boolean} True if the current date is in a leap year, else false
1572  */
1573 Date.prototype.isLeapYear = function() {
1574     var year = this.getFullYear();
1575     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1576 };
1577
1578 /**
1579  * Get the first day of the current month, adjusted for leap year.  The returned value
1580  * is the numeric day index within the week (0-6) which can be used in conjunction with
1581  * the {@link #monthNames} array to retrieve the textual day name.
1582  * Example:
1583  *<pre><code>
1584 var dt = new Date('1/10/2007');
1585 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1586 </code></pre>
1587  * @return {Number} The day number (0-6)
1588  */
1589 Date.prototype.getFirstDayOfMonth = function() {
1590     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1591     return (day < 0) ? (day + 7) : day;
1592 };
1593
1594 /**
1595  * Get the last day of the current month, adjusted for leap year.  The returned value
1596  * is the numeric day index within the week (0-6) which can be used in conjunction with
1597  * the {@link #monthNames} array to retrieve the textual day name.
1598  * Example:
1599  *<pre><code>
1600 var dt = new Date('1/10/2007');
1601 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1602 </code></pre>
1603  * @return {Number} The day number (0-6)
1604  */
1605 Date.prototype.getLastDayOfMonth = function() {
1606     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1607     return (day < 0) ? (day + 7) : day;
1608 };
1609
1610
1611 /**
1612  * Get the first date of this date's month
1613  * @return {Date}
1614  */
1615 Date.prototype.getFirstDateOfMonth = function() {
1616     return new Date(this.getFullYear(), this.getMonth(), 1);
1617 };
1618
1619 /**
1620  * Get the last date of this date's month
1621  * @return {Date}
1622  */
1623 Date.prototype.getLastDateOfMonth = function() {
1624     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1625 };
1626 /**
1627  * Get the number of days in the current month, adjusted for leap year.
1628  * @return {Number} The number of days in the month
1629  */
1630 Date.prototype.getDaysInMonth = function() {
1631     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1632     return Date.daysInMonth[this.getMonth()];
1633 };
1634
1635 /**
1636  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1637  * @return {String} 'st, 'nd', 'rd' or 'th'
1638  */
1639 Date.prototype.getSuffix = function() {
1640     switch (this.getDate()) {
1641         case 1:
1642         case 21:
1643         case 31:
1644             return "st";
1645         case 2:
1646         case 22:
1647             return "nd";
1648         case 3:
1649         case 23:
1650             return "rd";
1651         default:
1652             return "th";
1653     }
1654 };
1655
1656 // private
1657 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1658
1659 /**
1660  * An array of textual month names.
1661  * Override these values for international dates, for example...
1662  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1663  * @type Array
1664  * @static
1665  */
1666 Date.monthNames =
1667    ["January",
1668     "February",
1669     "March",
1670     "April",
1671     "May",
1672     "June",
1673     "July",
1674     "August",
1675     "September",
1676     "October",
1677     "November",
1678     "December"];
1679
1680 /**
1681  * An array of textual day names.
1682  * Override these values for international dates, for example...
1683  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1684  * @type Array
1685  * @static
1686  */
1687 Date.dayNames =
1688    ["Sunday",
1689     "Monday",
1690     "Tuesday",
1691     "Wednesday",
1692     "Thursday",
1693     "Friday",
1694     "Saturday"];
1695
1696 // private
1697 Date.y2kYear = 50;
1698 // private
1699 Date.monthNumbers = {
1700     Jan:0,
1701     Feb:1,
1702     Mar:2,
1703     Apr:3,
1704     May:4,
1705     Jun:5,
1706     Jul:6,
1707     Aug:7,
1708     Sep:8,
1709     Oct:9,
1710     Nov:10,
1711     Dec:11};
1712
1713 /**
1714  * Creates and returns a new Date instance with the exact same date value as the called instance.
1715  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1716  * variable will also be changed.  When the intention is to create a new variable that will not
1717  * modify the original instance, you should create a clone.
1718  *
1719  * Example of correctly cloning a date:
1720  * <pre><code>
1721 //wrong way:
1722 var orig = new Date('10/1/2006');
1723 var copy = orig;
1724 copy.setDate(5);
1725 document.write(orig);  //returns 'Thu Oct 05 2006'!
1726
1727 //correct way:
1728 var orig = new Date('10/1/2006');
1729 var copy = orig.clone();
1730 copy.setDate(5);
1731 document.write(orig);  //returns 'Thu Oct 01 2006'
1732 </code></pre>
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.clone = function() {
1736         return new Date(this.getTime());
1737 };
1738
1739 /**
1740  * Clears any time information from this date
1741  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1742  @return {Date} this or the clone
1743  */
1744 Date.prototype.clearTime = function(clone){
1745     if(clone){
1746         return this.clone().clearTime();
1747     }
1748     this.setHours(0);
1749     this.setMinutes(0);
1750     this.setSeconds(0);
1751     this.setMilliseconds(0);
1752     return this;
1753 };
1754
1755 // private
1756 // safari setMonth is broken -- check that this is only donw once...
1757 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1758     Date.brokenSetMonth = Date.prototype.setMonth;
1759         Date.prototype.setMonth = function(num){
1760                 if(num <= -1){
1761                         var n = Math.ceil(-num);
1762                         var back_year = Math.ceil(n/12);
1763                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1764                         this.setFullYear(this.getFullYear() - back_year);
1765                         return Date.brokenSetMonth.call(this, month);
1766                 } else {
1767                         return Date.brokenSetMonth.apply(this, arguments);
1768                 }
1769         };
1770 }
1771
1772 /** Date interval constant 
1773 * @static 
1774 * @type String */
1775 Date.MILLI = "ms";
1776 /** Date interval constant 
1777 * @static 
1778 * @type String */
1779 Date.SECOND = "s";
1780 /** Date interval constant 
1781 * @static 
1782 * @type String */
1783 Date.MINUTE = "mi";
1784 /** Date interval constant 
1785 * @static 
1786 * @type String */
1787 Date.HOUR = "h";
1788 /** Date interval constant 
1789 * @static 
1790 * @type String */
1791 Date.DAY = "d";
1792 /** Date interval constant 
1793 * @static 
1794 * @type String */
1795 Date.MONTH = "mo";
1796 /** Date interval constant 
1797 * @static 
1798 * @type String */
1799 Date.YEAR = "y";
1800
1801 /**
1802  * Provides a convenient method of performing basic date arithmetic.  This method
1803  * does not modify the Date instance being called - it creates and returns
1804  * a new Date instance containing the resulting date value.
1805  *
1806  * Examples:
1807  * <pre><code>
1808 //Basic usage:
1809 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1810 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1811
1812 //Negative values will subtract correctly:
1813 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1814 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1815
1816 //You can even chain several calls together in one line!
1817 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1818 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1819  </code></pre>
1820  *
1821  * @param {String} interval   A valid date interval enum value
1822  * @param {Number} value      The amount to add to the current date
1823  * @return {Date} The new Date instance
1824  */
1825 Date.prototype.add = function(interval, value){
1826   var d = this.clone();
1827   if (!interval || value === 0) { return d; }
1828   switch(interval.toLowerCase()){
1829     case Date.MILLI:
1830       d.setMilliseconds(this.getMilliseconds() + value);
1831       break;
1832     case Date.SECOND:
1833       d.setSeconds(this.getSeconds() + value);
1834       break;
1835     case Date.MINUTE:
1836       d.setMinutes(this.getMinutes() + value);
1837       break;
1838     case Date.HOUR:
1839       d.setHours(this.getHours() + value);
1840       break;
1841     case Date.DAY:
1842       d.setDate(this.getDate() + value);
1843       break;
1844     case Date.MONTH:
1845       var day = this.getDate();
1846       if(day > 28){
1847           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1848       }
1849       d.setDate(day);
1850       d.setMonth(this.getMonth() + value);
1851       break;
1852     case Date.YEAR:
1853       d.setFullYear(this.getFullYear() + value);
1854       break;
1855   }
1856   return d;
1857 };
1858 /*
1859  * Based on:
1860  * Ext JS Library 1.1.1
1861  * Copyright(c) 2006-2007, Ext JS, LLC.
1862  *
1863  * Originally Released Under LGPL - original licence link has changed is not relivant.
1864  *
1865  * Fork - LGPL
1866  * <script type="text/javascript">
1867  */
1868
1869 /**
1870  * @class Roo.lib.Dom
1871  * @static
1872  * 
1873  * Dom utils (from YIU afaik)
1874  * 
1875  **/
1876 Roo.lib.Dom = {
1877     /**
1878      * Get the view width
1879      * @param {Boolean} full True will get the full document, otherwise it's the view width
1880      * @return {Number} The width
1881      */
1882      
1883     getViewWidth : function(full) {
1884         return full ? this.getDocumentWidth() : this.getViewportWidth();
1885     },
1886     /**
1887      * Get the view height
1888      * @param {Boolean} full True will get the full document, otherwise it's the view height
1889      * @return {Number} The height
1890      */
1891     getViewHeight : function(full) {
1892         return full ? this.getDocumentHeight() : this.getViewportHeight();
1893     },
1894
1895     getDocumentHeight: function() {
1896         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1897         return Math.max(scrollHeight, this.getViewportHeight());
1898     },
1899
1900     getDocumentWidth: function() {
1901         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1902         return Math.max(scrollWidth, this.getViewportWidth());
1903     },
1904
1905     getViewportHeight: function() {
1906         var height = self.innerHeight;
1907         var mode = document.compatMode;
1908
1909         if ((mode || Roo.isIE) && !Roo.isOpera) {
1910             height = (mode == "CSS1Compat") ?
1911                      document.documentElement.clientHeight :
1912                      document.body.clientHeight;
1913         }
1914
1915         return height;
1916     },
1917
1918     getViewportWidth: function() {
1919         var width = self.innerWidth;
1920         var mode = document.compatMode;
1921
1922         if (mode || Roo.isIE) {
1923             width = (mode == "CSS1Compat") ?
1924                     document.documentElement.clientWidth :
1925                     document.body.clientWidth;
1926         }
1927         return width;
1928     },
1929
1930     isAncestor : function(p, c) {
1931         p = Roo.getDom(p);
1932         c = Roo.getDom(c);
1933         if (!p || !c) {
1934             return false;
1935         }
1936
1937         if (p.contains && !Roo.isSafari) {
1938             return p.contains(c);
1939         } else if (p.compareDocumentPosition) {
1940             return !!(p.compareDocumentPosition(c) & 16);
1941         } else {
1942             var parent = c.parentNode;
1943             while (parent) {
1944                 if (parent == p) {
1945                     return true;
1946                 }
1947                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1948                     return false;
1949                 }
1950                 parent = parent.parentNode;
1951             }
1952             return false;
1953         }
1954     },
1955
1956     getRegion : function(el) {
1957         return Roo.lib.Region.getRegion(el);
1958     },
1959
1960     getY : function(el) {
1961         return this.getXY(el)[1];
1962     },
1963
1964     getX : function(el) {
1965         return this.getXY(el)[0];
1966     },
1967
1968     getXY : function(el) {
1969         var p, pe, b, scroll, bd = document.body;
1970         el = Roo.getDom(el);
1971         var fly = Roo.lib.AnimBase.fly;
1972         if (el.getBoundingClientRect) {
1973             b = el.getBoundingClientRect();
1974             scroll = fly(document).getScroll();
1975             return [b.left + scroll.left, b.top + scroll.top];
1976         }
1977         var x = 0, y = 0;
1978
1979         p = el;
1980
1981         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1982
1983         while (p) {
1984
1985             x += p.offsetLeft;
1986             y += p.offsetTop;
1987
1988             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1989                 hasAbsolute = true;
1990             }
1991
1992             if (Roo.isGecko) {
1993                 pe = fly(p);
1994
1995                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1996                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1997
1998
1999                 x += bl;
2000                 y += bt;
2001
2002
2003                 if (p != el && pe.getStyle('overflow') != 'visible') {
2004                     x += bl;
2005                     y += bt;
2006                 }
2007             }
2008             p = p.offsetParent;
2009         }
2010
2011         if (Roo.isSafari && hasAbsolute) {
2012             x -= bd.offsetLeft;
2013             y -= bd.offsetTop;
2014         }
2015
2016         if (Roo.isGecko && !hasAbsolute) {
2017             var dbd = fly(bd);
2018             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2019             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2020         }
2021
2022         p = el.parentNode;
2023         while (p && p != bd) {
2024             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2025                 x -= p.scrollLeft;
2026                 y -= p.scrollTop;
2027             }
2028             p = p.parentNode;
2029         }
2030         return [x, y];
2031     },
2032  
2033   
2034
2035
2036     setXY : function(el, xy) {
2037         el = Roo.fly(el, '_setXY');
2038         el.position();
2039         var pts = el.translatePoints(xy);
2040         if (xy[0] !== false) {
2041             el.dom.style.left = pts.left + "px";
2042         }
2043         if (xy[1] !== false) {
2044             el.dom.style.top = pts.top + "px";
2045         }
2046     },
2047
2048     setX : function(el, x) {
2049         this.setXY(el, [x, false]);
2050     },
2051
2052     setY : function(el, y) {
2053         this.setXY(el, [false, y]);
2054     }
2055 };
2056 /*
2057  * Portions of this file are based on pieces of Yahoo User Interface Library
2058  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2059  * YUI licensed under the BSD License:
2060  * http://developer.yahoo.net/yui/license.txt
2061  * <script type="text/javascript">
2062  *
2063  */
2064
2065 Roo.lib.Event = function() {
2066     var loadComplete = false;
2067     var listeners = [];
2068     var unloadListeners = [];
2069     var retryCount = 0;
2070     var onAvailStack = [];
2071     var counter = 0;
2072     var lastError = null;
2073
2074     return {
2075         POLL_RETRYS: 200,
2076         POLL_INTERVAL: 20,
2077         EL: 0,
2078         TYPE: 1,
2079         FN: 2,
2080         WFN: 3,
2081         OBJ: 3,
2082         ADJ_SCOPE: 4,
2083         _interval: null,
2084
2085         startInterval: function() {
2086             if (!this._interval) {
2087                 var self = this;
2088                 var callback = function() {
2089                     self._tryPreloadAttach();
2090                 };
2091                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2092
2093             }
2094         },
2095
2096         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2097             onAvailStack.push({ id:         p_id,
2098                 fn:         p_fn,
2099                 obj:        p_obj,
2100                 override:   p_override,
2101                 checkReady: false    });
2102
2103             retryCount = this.POLL_RETRYS;
2104             this.startInterval();
2105         },
2106
2107
2108         addListener: function(el, eventName, fn) {
2109             el = Roo.getDom(el);
2110             if (!el || !fn) {
2111                 return false;
2112             }
2113
2114             if ("unload" == eventName) {
2115                 unloadListeners[unloadListeners.length] =
2116                 [el, eventName, fn];
2117                 return true;
2118             }
2119
2120             var wrappedFn = function(e) {
2121                 return fn(Roo.lib.Event.getEvent(e));
2122             };
2123
2124             var li = [el, eventName, fn, wrappedFn];
2125
2126             var index = listeners.length;
2127             listeners[index] = li;
2128
2129             this.doAdd(el, eventName, wrappedFn, false);
2130             return true;
2131
2132         },
2133
2134
2135         removeListener: function(el, eventName, fn) {
2136             var i, len;
2137
2138             el = Roo.getDom(el);
2139
2140             if(!fn) {
2141                 return this.purgeElement(el, false, eventName);
2142             }
2143
2144
2145             if ("unload" == eventName) {
2146
2147                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2148                     var li = unloadListeners[i];
2149                     if (li &&
2150                         li[0] == el &&
2151                         li[1] == eventName &&
2152                         li[2] == fn) {
2153                         unloadListeners.splice(i, 1);
2154                         return true;
2155                     }
2156                 }
2157
2158                 return false;
2159             }
2160
2161             var cacheItem = null;
2162
2163
2164             var index = arguments[3];
2165
2166             if ("undefined" == typeof index) {
2167                 index = this._getCacheIndex(el, eventName, fn);
2168             }
2169
2170             if (index >= 0) {
2171                 cacheItem = listeners[index];
2172             }
2173
2174             if (!el || !cacheItem) {
2175                 return false;
2176             }
2177
2178             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2179
2180             delete listeners[index][this.WFN];
2181             delete listeners[index][this.FN];
2182             listeners.splice(index, 1);
2183
2184             return true;
2185
2186         },
2187
2188
2189         getTarget: function(ev, resolveTextNode) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             var t = ev.target || ev.srcElement;
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         resolveTextNode: function(node) {
2198             if (Roo.isSafari && node && 3 == node.nodeType) {
2199                 return node.parentNode;
2200             } else {
2201                 return node;
2202             }
2203         },
2204
2205
2206         getPageX: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2209             var x = ev.pageX;
2210             if (!x && 0 !== x) {
2211                 x = ev.clientX || 0;
2212
2213                 if (Roo.isIE) {
2214                     x += this.getScroll()[1];
2215                 }
2216             }
2217
2218             return x;
2219         },
2220
2221
2222         getPageY: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2225             var y = ev.pageY;
2226             if (!y && 0 !== y) {
2227                 y = ev.clientY || 0;
2228
2229                 if (Roo.isIE) {
2230                     y += this.getScroll()[0];
2231                 }
2232             }
2233
2234
2235             return y;
2236         },
2237
2238
2239         getXY: function(ev) {
2240             ev = ev.browserEvent || ev;
2241             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2242             return [this.getPageX(ev), this.getPageY(ev)];
2243         },
2244
2245
2246         getRelatedTarget: function(ev) {
2247             ev = ev.browserEvent || ev;
2248             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2249             var t = ev.relatedTarget;
2250             if (!t) {
2251                 if (ev.type == "mouseout") {
2252                     t = ev.toElement;
2253                 } else if (ev.type == "mouseover") {
2254                     t = ev.fromElement;
2255                 }
2256             }
2257
2258             return this.resolveTextNode(t);
2259         },
2260
2261
2262         getTime: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             if (!ev.time) {
2266                 var t = new Date().getTime();
2267                 try {
2268                     ev.time = t;
2269                 } catch(ex) {
2270                     this.lastError = ex;
2271                     return t;
2272                 }
2273             }
2274
2275             return ev.time;
2276         },
2277
2278
2279         stopEvent: function(ev) {
2280             this.stopPropagation(ev);
2281             this.preventDefault(ev);
2282         },
2283
2284
2285         stopPropagation: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (ev.stopPropagation) {
2288                 ev.stopPropagation();
2289             } else {
2290                 ev.cancelBubble = true;
2291             }
2292         },
2293
2294
2295         preventDefault: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             if(ev.preventDefault) {
2298                 ev.preventDefault();
2299             } else {
2300                 ev.returnValue = false;
2301             }
2302         },
2303
2304
2305         getEvent: function(e) {
2306             var ev = e || window.event;
2307             if (!ev) {
2308                 var c = this.getEvent.caller;
2309                 while (c) {
2310                     ev = c.arguments[0];
2311                     if (ev && Event == ev.constructor) {
2312                         break;
2313                     }
2314                     c = c.caller;
2315                 }
2316             }
2317             return ev;
2318         },
2319
2320
2321         getCharCode: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             return ev.charCode || ev.keyCode || 0;
2324         },
2325
2326
2327         _getCacheIndex: function(el, eventName, fn) {
2328             for (var i = 0,len = listeners.length; i < len; ++i) {
2329                 var li = listeners[i];
2330                 if (li &&
2331                     li[this.FN] == fn &&
2332                     li[this.EL] == el &&
2333                     li[this.TYPE] == eventName) {
2334                     return i;
2335                 }
2336             }
2337
2338             return -1;
2339         },
2340
2341
2342         elCache: {},
2343
2344
2345         getEl: function(id) {
2346             return document.getElementById(id);
2347         },
2348
2349
2350         clearCache: function() {
2351         },
2352
2353
2354         _load: function(e) {
2355             loadComplete = true;
2356             var EU = Roo.lib.Event;
2357
2358
2359             if (Roo.isIE) {
2360                 EU.doRemove(window, "load", EU._load);
2361             }
2362         },
2363
2364
2365         _tryPreloadAttach: function() {
2366
2367             if (this.locked) {
2368                 return false;
2369             }
2370
2371             this.locked = true;
2372
2373
2374             var tryAgain = !loadComplete;
2375             if (!tryAgain) {
2376                 tryAgain = (retryCount > 0);
2377             }
2378
2379
2380             var notAvail = [];
2381             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2382                 var item = onAvailStack[i];
2383                 if (item) {
2384                     var el = this.getEl(item.id);
2385
2386                     if (el) {
2387                         if (!item.checkReady ||
2388                             loadComplete ||
2389                             el.nextSibling ||
2390                             (document && document.body)) {
2391
2392                             var scope = el;
2393                             if (item.override) {
2394                                 if (item.override === true) {
2395                                     scope = item.obj;
2396                                 } else {
2397                                     scope = item.override;
2398                                 }
2399                             }
2400                             item.fn.call(scope, item.obj);
2401                             onAvailStack[i] = null;
2402                         }
2403                     } else {
2404                         notAvail.push(item);
2405                     }
2406                 }
2407             }
2408
2409             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2410
2411             if (tryAgain) {
2412
2413                 this.startInterval();
2414             } else {
2415                 clearInterval(this._interval);
2416                 this._interval = null;
2417             }
2418
2419             this.locked = false;
2420
2421             return true;
2422
2423         },
2424
2425
2426         purgeElement: function(el, recurse, eventName) {
2427             var elListeners = this.getListeners(el, eventName);
2428             if (elListeners) {
2429                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2430                     var l = elListeners[i];
2431                     this.removeListener(el, l.type, l.fn);
2432                 }
2433             }
2434
2435             if (recurse && el && el.childNodes) {
2436                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2437                     this.purgeElement(el.childNodes[i], recurse, eventName);
2438                 }
2439             }
2440         },
2441
2442
2443         getListeners: function(el, eventName) {
2444             var results = [], searchLists;
2445             if (!eventName) {
2446                 searchLists = [listeners, unloadListeners];
2447             } else if (eventName == "unload") {
2448                 searchLists = [unloadListeners];
2449             } else {
2450                 searchLists = [listeners];
2451             }
2452
2453             for (var j = 0; j < searchLists.length; ++j) {
2454                 var searchList = searchLists[j];
2455                 if (searchList && searchList.length > 0) {
2456                     for (var i = 0,len = searchList.length; i < len; ++i) {
2457                         var l = searchList[i];
2458                         if (l && l[this.EL] === el &&
2459                             (!eventName || eventName === l[this.TYPE])) {
2460                             results.push({
2461                                 type:   l[this.TYPE],
2462                                 fn:     l[this.FN],
2463                                 obj:    l[this.OBJ],
2464                                 adjust: l[this.ADJ_SCOPE],
2465                                 index:  i
2466                             });
2467                         }
2468                     }
2469                 }
2470             }
2471
2472             return (results.length) ? results : null;
2473         },
2474
2475
2476         _unload: function(e) {
2477
2478             var EU = Roo.lib.Event, i, j, l, len, index;
2479
2480             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2481                 l = unloadListeners[i];
2482                 if (l) {
2483                     var scope = window;
2484                     if (l[EU.ADJ_SCOPE]) {
2485                         if (l[EU.ADJ_SCOPE] === true) {
2486                             scope = l[EU.OBJ];
2487                         } else {
2488                             scope = l[EU.ADJ_SCOPE];
2489                         }
2490                     }
2491                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2492                     unloadListeners[i] = null;
2493                     l = null;
2494                     scope = null;
2495                 }
2496             }
2497
2498             unloadListeners = null;
2499
2500             if (listeners && listeners.length > 0) {
2501                 j = listeners.length;
2502                 while (j) {
2503                     index = j - 1;
2504                     l = listeners[index];
2505                     if (l) {
2506                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2507                                 l[EU.FN], index);
2508                     }
2509                     j = j - 1;
2510                 }
2511                 l = null;
2512
2513                 EU.clearCache();
2514             }
2515
2516             EU.doRemove(window, "unload", EU._unload);
2517
2518         },
2519
2520
2521         getScroll: function() {
2522             var dd = document.documentElement, db = document.body;
2523             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2524                 return [dd.scrollTop, dd.scrollLeft];
2525             } else if (db) {
2526                 return [db.scrollTop, db.scrollLeft];
2527             } else {
2528                 return [0, 0];
2529             }
2530         },
2531
2532
2533         doAdd: function () {
2534             if (window.addEventListener) {
2535                 return function(el, eventName, fn, capture) {
2536                     el.addEventListener(eventName, fn, (capture));
2537                 };
2538             } else if (window.attachEvent) {
2539                 return function(el, eventName, fn, capture) {
2540                     el.attachEvent("on" + eventName, fn);
2541                 };
2542             } else {
2543                 return function() {
2544                 };
2545             }
2546         }(),
2547
2548
2549         doRemove: function() {
2550             if (window.removeEventListener) {
2551                 return function (el, eventName, fn, capture) {
2552                     el.removeEventListener(eventName, fn, (capture));
2553                 };
2554             } else if (window.detachEvent) {
2555                 return function (el, eventName, fn) {
2556                     el.detachEvent("on" + eventName, fn);
2557                 };
2558             } else {
2559                 return function() {
2560                 };
2561             }
2562         }()
2563     };
2564     
2565 }();
2566 (function() {     
2567    
2568     var E = Roo.lib.Event;
2569     E.on = E.addListener;
2570     E.un = E.removeListener;
2571
2572     if (document && document.body) {
2573         E._load();
2574     } else {
2575         E.doAdd(window, "load", E._load);
2576     }
2577     E.doAdd(window, "unload", E._unload);
2578     E._tryPreloadAttach();
2579 })();
2580
2581 /*
2582  * Portions of this file are based on pieces of Yahoo User Interface Library
2583  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2584  * YUI licensed under the BSD License:
2585  * http://developer.yahoo.net/yui/license.txt
2586  * <script type="text/javascript">
2587  *
2588  */
2589
2590 (function() {
2591     /**
2592      * @class Roo.lib.Ajax
2593      *
2594      */
2595     Roo.lib.Ajax = {
2596         /**
2597          * @static 
2598          */
2599         request : function(method, uri, cb, data, options) {
2600             if(options){
2601                 var hs = options.headers;
2602                 if(hs){
2603                     for(var h in hs){
2604                         if(hs.hasOwnProperty(h)){
2605                             this.initHeader(h, hs[h], false);
2606                         }
2607                     }
2608                 }
2609                 if(options.xmlData){
2610                     this.initHeader('Content-Type', 'text/xml', false);
2611                     method = 'POST';
2612                     data = options.xmlData;
2613                 }
2614             }
2615
2616             return this.asyncRequest(method, uri, cb, data);
2617         },
2618
2619         serializeForm : function(form) {
2620             if(typeof form == 'string') {
2621                 form = (document.getElementById(form) || document.forms[form]);
2622             }
2623
2624             var el, name, val, disabled, data = '', hasSubmit = false;
2625             for (var i = 0; i < form.elements.length; i++) {
2626                 el = form.elements[i];
2627                 disabled = form.elements[i].disabled;
2628                 name = form.elements[i].name;
2629                 val = form.elements[i].value;
2630
2631                 if (!disabled && name){
2632                     switch (el.type)
2633                             {
2634                         case 'select-one':
2635                         case 'select-multiple':
2636                             for (var j = 0; j < el.options.length; j++) {
2637                                 if (el.options[j].selected) {
2638                                     if (Roo.isIE) {
2639                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2640                                     }
2641                                     else {
2642                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2643                                     }
2644                                 }
2645                             }
2646                             break;
2647                         case 'radio':
2648                         case 'checkbox':
2649                             if (el.checked) {
2650                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2651                             }
2652                             break;
2653                         case 'file':
2654
2655                         case undefined:
2656
2657                         case 'reset':
2658
2659                         case 'button':
2660
2661                             break;
2662                         case 'submit':
2663                             if(hasSubmit == false) {
2664                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2665                                 hasSubmit = true;
2666                             }
2667                             break;
2668                         default:
2669                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2670                             break;
2671                     }
2672                 }
2673             }
2674             data = data.substr(0, data.length - 1);
2675             return data;
2676         },
2677
2678         headers:{},
2679
2680         hasHeaders:false,
2681
2682         useDefaultHeader:true,
2683
2684         defaultPostHeader:'application/x-www-form-urlencoded',
2685
2686         useDefaultXhrHeader:true,
2687
2688         defaultXhrHeader:'XMLHttpRequest',
2689
2690         hasDefaultHeaders:true,
2691
2692         defaultHeaders:{},
2693
2694         poll:{},
2695
2696         timeout:{},
2697
2698         pollInterval:50,
2699
2700         transactionId:0,
2701
2702         setProgId:function(id)
2703         {
2704             this.activeX.unshift(id);
2705         },
2706
2707         setDefaultPostHeader:function(b)
2708         {
2709             this.useDefaultHeader = b;
2710         },
2711
2712         setDefaultXhrHeader:function(b)
2713         {
2714             this.useDefaultXhrHeader = b;
2715         },
2716
2717         setPollingInterval:function(i)
2718         {
2719             if (typeof i == 'number' && isFinite(i)) {
2720                 this.pollInterval = i;
2721             }
2722         },
2723
2724         createXhrObject:function(transactionId)
2725         {
2726             var obj,http;
2727             try
2728             {
2729
2730                 http = new XMLHttpRequest();
2731
2732                 obj = { conn:http, tId:transactionId };
2733             }
2734             catch(e)
2735             {
2736                 for (var i = 0; i < this.activeX.length; ++i) {
2737                     try
2738                     {
2739
2740                         http = new ActiveXObject(this.activeX[i]);
2741
2742                         obj = { conn:http, tId:transactionId };
2743                         break;
2744                     }
2745                     catch(e) {
2746                     }
2747                 }
2748             }
2749             finally
2750             {
2751                 return obj;
2752             }
2753         },
2754
2755         getConnectionObject:function()
2756         {
2757             var o;
2758             var tId = this.transactionId;
2759
2760             try
2761             {
2762                 o = this.createXhrObject(tId);
2763                 if (o) {
2764                     this.transactionId++;
2765                 }
2766             }
2767             catch(e) {
2768             }
2769             finally
2770             {
2771                 return o;
2772             }
2773         },
2774
2775         asyncRequest:function(method, uri, callback, postData)
2776         {
2777             var o = this.getConnectionObject();
2778
2779             if (!o) {
2780                 return null;
2781             }
2782             else {
2783                 o.conn.open(method, uri, true);
2784
2785                 if (this.useDefaultXhrHeader) {
2786                     if (!this.defaultHeaders['X-Requested-With']) {
2787                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2788                     }
2789                 }
2790
2791                 if(postData && this.useDefaultHeader){
2792                     this.initHeader('Content-Type', this.defaultPostHeader);
2793                 }
2794
2795                  if (this.hasDefaultHeaders || this.hasHeaders) {
2796                     this.setHeader(o);
2797                 }
2798
2799                 this.handleReadyState(o, callback);
2800                 o.conn.send(postData || null);
2801
2802                 return o;
2803             }
2804         },
2805
2806         handleReadyState:function(o, callback)
2807         {
2808             var oConn = this;
2809
2810             if (callback && callback.timeout) {
2811                 
2812                 this.timeout[o.tId] = window.setTimeout(function() {
2813                     oConn.abort(o, callback, true);
2814                 }, callback.timeout);
2815             }
2816
2817             this.poll[o.tId] = window.setInterval(
2818                     function() {
2819                         if (o.conn && o.conn.readyState == 4) {
2820                             window.clearInterval(oConn.poll[o.tId]);
2821                             delete oConn.poll[o.tId];
2822
2823                             if(callback && callback.timeout) {
2824                                 window.clearTimeout(oConn.timeout[o.tId]);
2825                                 delete oConn.timeout[o.tId];
2826                             }
2827
2828                             oConn.handleTransactionResponse(o, callback);
2829                         }
2830                     }
2831                     , this.pollInterval);
2832         },
2833
2834         handleTransactionResponse:function(o, callback, isAbort)
2835         {
2836
2837             if (!callback) {
2838                 this.releaseObject(o);
2839                 return;
2840             }
2841
2842             var httpStatus, responseObject;
2843
2844             try
2845             {
2846                 if (o.conn.status !== undefined && o.conn.status != 0) {
2847                     httpStatus = o.conn.status;
2848                 }
2849                 else {
2850                     httpStatus = 13030;
2851                 }
2852             }
2853             catch(e) {
2854
2855
2856                 httpStatus = 13030;
2857             }
2858
2859             if (httpStatus >= 200 && httpStatus < 300) {
2860                 responseObject = this.createResponseObject(o, callback.argument);
2861                 if (callback.success) {
2862                     if (!callback.scope) {
2863                         callback.success(responseObject);
2864                     }
2865                     else {
2866
2867
2868                         callback.success.apply(callback.scope, [responseObject]);
2869                     }
2870                 }
2871             }
2872             else {
2873                 switch (httpStatus) {
2874
2875                     case 12002:
2876                     case 12029:
2877                     case 12030:
2878                     case 12031:
2879                     case 12152:
2880                     case 13030:
2881                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2882                         if (callback.failure) {
2883                             if (!callback.scope) {
2884                                 callback.failure(responseObject);
2885                             }
2886                             else {
2887                                 callback.failure.apply(callback.scope, [responseObject]);
2888                             }
2889                         }
2890                         break;
2891                     default:
2892                         responseObject = this.createResponseObject(o, callback.argument);
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                 }
2902             }
2903
2904             this.releaseObject(o);
2905             responseObject = null;
2906         },
2907
2908         createResponseObject:function(o, callbackArg)
2909         {
2910             var obj = {};
2911             var headerObj = {};
2912
2913             try
2914             {
2915                 var headerStr = o.conn.getAllResponseHeaders();
2916                 var header = headerStr.split('\n');
2917                 for (var i = 0; i < header.length; i++) {
2918                     var delimitPos = header[i].indexOf(':');
2919                     if (delimitPos != -1) {
2920                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2921                     }
2922                 }
2923             }
2924             catch(e) {
2925             }
2926
2927             obj.tId = o.tId;
2928             obj.status = o.conn.status;
2929             obj.statusText = o.conn.statusText;
2930             obj.getResponseHeader = headerObj;
2931             obj.getAllResponseHeaders = headerStr;
2932             obj.responseText = o.conn.responseText;
2933             obj.responseXML = o.conn.responseXML;
2934
2935             if (typeof callbackArg !== undefined) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         createExceptionObject:function(tId, callbackArg, isAbort)
2943         {
2944             var COMM_CODE = 0;
2945             var COMM_ERROR = 'communication failure';
2946             var ABORT_CODE = -1;
2947             var ABORT_ERROR = 'transaction aborted';
2948
2949             var obj = {};
2950
2951             obj.tId = tId;
2952             if (isAbort) {
2953                 obj.status = ABORT_CODE;
2954                 obj.statusText = ABORT_ERROR;
2955             }
2956             else {
2957                 obj.status = COMM_CODE;
2958                 obj.statusText = COMM_ERROR;
2959             }
2960
2961             if (callbackArg) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         initHeader:function(label, value, isDefault)
2969         {
2970             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2971
2972             if (headerObj[label] === undefined) {
2973                 headerObj[label] = value;
2974             }
2975             else {
2976
2977
2978                 headerObj[label] = value + "," + headerObj[label];
2979             }
2980
2981             if (isDefault) {
2982                 this.hasDefaultHeaders = true;
2983             }
2984             else {
2985                 this.hasHeaders = true;
2986             }
2987         },
2988
2989
2990         setHeader:function(o)
2991         {
2992             if (this.hasDefaultHeaders) {
2993                 for (var prop in this.defaultHeaders) {
2994                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2995                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2996                     }
2997                 }
2998             }
2999
3000             if (this.hasHeaders) {
3001                 for (var prop in this.headers) {
3002                     if (this.headers.hasOwnProperty(prop)) {
3003                         o.conn.setRequestHeader(prop, this.headers[prop]);
3004                     }
3005                 }
3006                 this.headers = {};
3007                 this.hasHeaders = false;
3008             }
3009         },
3010
3011         resetDefaultHeaders:function() {
3012             delete this.defaultHeaders;
3013             this.defaultHeaders = {};
3014             this.hasDefaultHeaders = false;
3015         },
3016
3017         abort:function(o, callback, isTimeout)
3018         {
3019             if(this.isCallInProgress(o)) {
3020                 o.conn.abort();
3021                 window.clearInterval(this.poll[o.tId]);
3022                 delete this.poll[o.tId];
3023                 if (isTimeout) {
3024                     delete this.timeout[o.tId];
3025                 }
3026
3027                 this.handleTransactionResponse(o, callback, true);
3028
3029                 return true;
3030             }
3031             else {
3032                 return false;
3033             }
3034         },
3035
3036
3037         isCallInProgress:function(o)
3038         {
3039             if (o && o.conn) {
3040                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3041             }
3042             else {
3043
3044                 return false;
3045             }
3046         },
3047
3048
3049         releaseObject:function(o)
3050         {
3051
3052             o.conn = null;
3053
3054             o = null;
3055         },
3056
3057         activeX:[
3058         'MSXML2.XMLHTTP.3.0',
3059         'MSXML2.XMLHTTP',
3060         'Microsoft.XMLHTTP'
3061         ]
3062
3063
3064     };
3065 })();/*
3066  * Portions of this file are based on pieces of Yahoo User Interface Library
3067  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3068  * YUI licensed under the BSD License:
3069  * http://developer.yahoo.net/yui/license.txt
3070  * <script type="text/javascript">
3071  *
3072  */
3073
3074 Roo.lib.Region = function(t, r, b, l) {
3075     this.top = t;
3076     this[1] = t;
3077     this.right = r;
3078     this.bottom = b;
3079     this.left = l;
3080     this[0] = l;
3081 };
3082
3083
3084 Roo.lib.Region.prototype = {
3085     contains : function(region) {
3086         return ( region.left >= this.left &&
3087                  region.right <= this.right &&
3088                  region.top >= this.top &&
3089                  region.bottom <= this.bottom    );
3090
3091     },
3092
3093     getArea : function() {
3094         return ( (this.bottom - this.top) * (this.right - this.left) );
3095     },
3096
3097     intersect : function(region) {
3098         var t = Math.max(this.top, region.top);
3099         var r = Math.min(this.right, region.right);
3100         var b = Math.min(this.bottom, region.bottom);
3101         var l = Math.max(this.left, region.left);
3102
3103         if (b >= t && r >= l) {
3104             return new Roo.lib.Region(t, r, b, l);
3105         } else {
3106             return null;
3107         }
3108     },
3109     union : function(region) {
3110         var t = Math.min(this.top, region.top);
3111         var r = Math.max(this.right, region.right);
3112         var b = Math.max(this.bottom, region.bottom);
3113         var l = Math.min(this.left, region.left);
3114
3115         return new Roo.lib.Region(t, r, b, l);
3116     },
3117
3118     adjust : function(t, l, b, r) {
3119         this.top += t;
3120         this.left += l;
3121         this.right += r;
3122         this.bottom += b;
3123         return this;
3124     }
3125 };
3126
3127 Roo.lib.Region.getRegion = function(el) {
3128     var p = Roo.lib.Dom.getXY(el);
3129
3130     var t = p[1];
3131     var r = p[0] + el.offsetWidth;
3132     var b = p[1] + el.offsetHeight;
3133     var l = p[0];
3134
3135     return new Roo.lib.Region(t, r, b, l);
3136 };
3137 /*
3138  * Portions of this file are based on pieces of Yahoo User Interface Library
3139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3140  * YUI licensed under the BSD License:
3141  * http://developer.yahoo.net/yui/license.txt
3142  * <script type="text/javascript">
3143  *
3144  */
3145 //@@dep Roo.lib.Region
3146
3147
3148 Roo.lib.Point = function(x, y) {
3149     if (x instanceof Array) {
3150         y = x[1];
3151         x = x[0];
3152     }
3153     this.x = this.right = this.left = this[0] = x;
3154     this.y = this.top = this.bottom = this[1] = y;
3155 };
3156
3157 Roo.lib.Point.prototype = new Roo.lib.Region();
3158 /*
3159  * Portions of this file are based on pieces of Yahoo User Interface Library
3160  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3161  * YUI licensed under the BSD License:
3162  * http://developer.yahoo.net/yui/license.txt
3163  * <script type="text/javascript">
3164  *
3165  */
3166  
3167 (function() {   
3168
3169     Roo.lib.Anim = {
3170         scroll : function(el, args, duration, easing, cb, scope) {
3171             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3172         },
3173
3174         motion : function(el, args, duration, easing, cb, scope) {
3175             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3176         },
3177
3178         color : function(el, args, duration, easing, cb, scope) {
3179             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3180         },
3181
3182         run : function(el, args, duration, easing, cb, scope, type) {
3183             type = type || Roo.lib.AnimBase;
3184             if (typeof easing == "string") {
3185                 easing = Roo.lib.Easing[easing];
3186             }
3187             var anim = new type(el, args, duration, easing);
3188             anim.animateX(function() {
3189                 Roo.callback(cb, scope);
3190             });
3191             return anim;
3192         }
3193     };
3194 })();/*
3195  * Portions of this file are based on pieces of Yahoo User Interface Library
3196  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3197  * YUI licensed under the BSD License:
3198  * http://developer.yahoo.net/yui/license.txt
3199  * <script type="text/javascript">
3200  *
3201  */
3202
3203 (function() {    
3204     var libFlyweight;
3205     
3206     function fly(el) {
3207         if (!libFlyweight) {
3208             libFlyweight = new Roo.Element.Flyweight();
3209         }
3210         libFlyweight.dom = el;
3211         return libFlyweight;
3212     }
3213
3214     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3215     
3216    
3217     
3218     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3219         if (el) {
3220             this.init(el, attributes, duration, method);
3221         }
3222     };
3223
3224     Roo.lib.AnimBase.fly = fly;
3225     
3226     
3227     
3228     Roo.lib.AnimBase.prototype = {
3229
3230         toString: function() {
3231             var el = this.getEl();
3232             var id = el.id || el.tagName;
3233             return ("Anim " + id);
3234         },
3235
3236         patterns: {
3237             noNegatives:        /width|height|opacity|padding/i,
3238             offsetAttribute:  /^((width|height)|(top|left))$/,
3239             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3240             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3241         },
3242
3243
3244         doMethod: function(attr, start, end) {
3245             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3246         },
3247
3248
3249         setAttribute: function(attr, val, unit) {
3250             if (this.patterns.noNegatives.test(attr)) {
3251                 val = (val > 0) ? val : 0;
3252             }
3253
3254             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3255         },
3256
3257
3258         getAttribute: function(attr) {
3259             var el = this.getEl();
3260             var val = fly(el).getStyle(attr);
3261
3262             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3263                 return parseFloat(val);
3264             }
3265
3266             var a = this.patterns.offsetAttribute.exec(attr) || [];
3267             var pos = !!( a[3] );
3268             var box = !!( a[2] );
3269
3270
3271             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3272                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3273             } else {
3274                 val = 0;
3275             }
3276
3277             return val;
3278         },
3279
3280
3281         getDefaultUnit: function(attr) {
3282             if (this.patterns.defaultUnit.test(attr)) {
3283                 return 'px';
3284             }
3285
3286             return '';
3287         },
3288
3289         animateX : function(callback, scope) {
3290             var f = function() {
3291                 this.onComplete.removeListener(f);
3292                 if (typeof callback == "function") {
3293                     callback.call(scope || this, this);
3294                 }
3295             };
3296             this.onComplete.addListener(f, this);
3297             this.animate();
3298         },
3299
3300
3301         setRuntimeAttribute: function(attr) {
3302             var start;
3303             var end;
3304             var attributes = this.attributes;
3305
3306             this.runtimeAttributes[attr] = {};
3307
3308             var isset = function(prop) {
3309                 return (typeof prop !== 'undefined');
3310             };
3311
3312             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3313                 return false;
3314             }
3315
3316             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3317
3318
3319             if (isset(attributes[attr]['to'])) {
3320                 end = attributes[attr]['to'];
3321             } else if (isset(attributes[attr]['by'])) {
3322                 if (start.constructor == Array) {
3323                     end = [];
3324                     for (var i = 0, len = start.length; i < len; ++i) {
3325                         end[i] = start[i] + attributes[attr]['by'][i];
3326                     }
3327                 } else {
3328                     end = start + attributes[attr]['by'];
3329                 }
3330             }
3331
3332             this.runtimeAttributes[attr].start = start;
3333             this.runtimeAttributes[attr].end = end;
3334
3335
3336             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3337         },
3338
3339
3340         init: function(el, attributes, duration, method) {
3341
3342             var isAnimated = false;
3343
3344
3345             var startTime = null;
3346
3347
3348             var actualFrames = 0;
3349
3350
3351             el = Roo.getDom(el);
3352
3353
3354             this.attributes = attributes || {};
3355
3356
3357             this.duration = duration || 1;
3358
3359
3360             this.method = method || Roo.lib.Easing.easeNone;
3361
3362
3363             this.useSeconds = true;
3364
3365
3366             this.currentFrame = 0;
3367
3368
3369             this.totalFrames = Roo.lib.AnimMgr.fps;
3370
3371
3372             this.getEl = function() {
3373                 return el;
3374             };
3375
3376
3377             this.isAnimated = function() {
3378                 return isAnimated;
3379             };
3380
3381
3382             this.getStartTime = function() {
3383                 return startTime;
3384             };
3385
3386             this.runtimeAttributes = {};
3387
3388
3389             this.animate = function() {
3390                 if (this.isAnimated()) {
3391                     return false;
3392                 }
3393
3394                 this.currentFrame = 0;
3395
3396                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3397
3398                 Roo.lib.AnimMgr.registerElement(this);
3399             };
3400
3401
3402             this.stop = function(finish) {
3403                 if (finish) {
3404                     this.currentFrame = this.totalFrames;
3405                     this._onTween.fire();
3406                 }
3407                 Roo.lib.AnimMgr.stop(this);
3408             };
3409
3410             var onStart = function() {
3411                 this.onStart.fire();
3412
3413                 this.runtimeAttributes = {};
3414                 for (var attr in this.attributes) {
3415                     this.setRuntimeAttribute(attr);
3416                 }
3417
3418                 isAnimated = true;
3419                 actualFrames = 0;
3420                 startTime = new Date();
3421             };
3422
3423
3424             var onTween = function() {
3425                 var data = {
3426                     duration: new Date() - this.getStartTime(),
3427                     currentFrame: this.currentFrame
3428                 };
3429
3430                 data.toString = function() {
3431                     return (
3432                             'duration: ' + data.duration +
3433                             ', currentFrame: ' + data.currentFrame
3434                             );
3435                 };
3436
3437                 this.onTween.fire(data);
3438
3439                 var runtimeAttributes = this.runtimeAttributes;
3440
3441                 for (var attr in runtimeAttributes) {
3442                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3443                 }
3444
3445                 actualFrames += 1;
3446             };
3447
3448             var onComplete = function() {
3449                 var actual_duration = (new Date() - startTime) / 1000 ;
3450
3451                 var data = {
3452                     duration: actual_duration,
3453                     frames: actualFrames,
3454                     fps: actualFrames / actual_duration
3455                 };
3456
3457                 data.toString = function() {
3458                     return (
3459                             'duration: ' + data.duration +
3460                             ', frames: ' + data.frames +
3461                             ', fps: ' + data.fps
3462                             );
3463                 };
3464
3465                 isAnimated = false;
3466                 actualFrames = 0;
3467                 this.onComplete.fire(data);
3468             };
3469
3470
3471             this._onStart = new Roo.util.Event(this);
3472             this.onStart = new Roo.util.Event(this);
3473             this.onTween = new Roo.util.Event(this);
3474             this._onTween = new Roo.util.Event(this);
3475             this.onComplete = new Roo.util.Event(this);
3476             this._onComplete = new Roo.util.Event(this);
3477             this._onStart.addListener(onStart);
3478             this._onTween.addListener(onTween);
3479             this._onComplete.addListener(onComplete);
3480         }
3481     };
3482 })();
3483 /*
3484  * Portions of this file are based on pieces of Yahoo User Interface Library
3485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3486  * YUI licensed under the BSD License:
3487  * http://developer.yahoo.net/yui/license.txt
3488  * <script type="text/javascript">
3489  *
3490  */
3491
3492 Roo.lib.AnimMgr = new function() {
3493
3494     var thread = null;
3495
3496
3497     var queue = [];
3498
3499
3500     var tweenCount = 0;
3501
3502
3503     this.fps = 1000;
3504
3505
3506     this.delay = 1;
3507
3508
3509     this.registerElement = function(tween) {
3510         queue[queue.length] = tween;
3511         tweenCount += 1;
3512         tween._onStart.fire();
3513         this.start();
3514     };
3515
3516
3517     this.unRegister = function(tween, index) {
3518         tween._onComplete.fire();
3519         index = index || getIndex(tween);
3520         if (index != -1) {
3521             queue.splice(index, 1);
3522         }
3523
3524         tweenCount -= 1;
3525         if (tweenCount <= 0) {
3526             this.stop();
3527         }
3528     };
3529
3530
3531     this.start = function() {
3532         if (thread === null) {
3533             thread = setInterval(this.run, this.delay);
3534         }
3535     };
3536
3537
3538     this.stop = function(tween) {
3539         if (!tween) {
3540             clearInterval(thread);
3541
3542             for (var i = 0, len = queue.length; i < len; ++i) {
3543                 if (queue[0].isAnimated()) {
3544                     this.unRegister(queue[0], 0);
3545                 }
3546             }
3547
3548             queue = [];
3549             thread = null;
3550             tweenCount = 0;
3551         }
3552         else {
3553             this.unRegister(tween);
3554         }
3555     };
3556
3557
3558     this.run = function() {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             var tween = queue[i];
3561             if (!tween || !tween.isAnimated()) {
3562                 continue;
3563             }
3564
3565             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3566             {
3567                 tween.currentFrame += 1;
3568
3569                 if (tween.useSeconds) {
3570                     correctFrame(tween);
3571                 }
3572                 tween._onTween.fire();
3573             }
3574             else {
3575                 Roo.lib.AnimMgr.stop(tween, i);
3576             }
3577         }
3578     };
3579
3580     var getIndex = function(anim) {
3581         for (var i = 0, len = queue.length; i < len; ++i) {
3582             if (queue[i] == anim) {
3583                 return i;
3584             }
3585         }
3586         return -1;
3587     };
3588
3589
3590     var correctFrame = function(tween) {
3591         var frames = tween.totalFrames;
3592         var frame = tween.currentFrame;
3593         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3594         var elapsed = (new Date() - tween.getStartTime());
3595         var tweak = 0;
3596
3597         if (elapsed < tween.duration * 1000) {
3598             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3599         } else {
3600             tweak = frames - (frame + 1);
3601         }
3602         if (tweak > 0 && isFinite(tweak)) {
3603             if (tween.currentFrame + tweak >= frames) {
3604                 tweak = frames - (frame + 1);
3605             }
3606
3607             tween.currentFrame += tweak;
3608         }
3609     };
3610 };
3611
3612     /*
3613  * Portions of this file are based on pieces of Yahoo User Interface Library
3614  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3615  * YUI licensed under the BSD License:
3616  * http://developer.yahoo.net/yui/license.txt
3617  * <script type="text/javascript">
3618  *
3619  */
3620 Roo.lib.Bezier = new function() {
3621
3622         this.getPosition = function(points, t) {
3623             var n = points.length;
3624             var tmp = [];
3625
3626             for (var i = 0; i < n; ++i) {
3627                 tmp[i] = [points[i][0], points[i][1]];
3628             }
3629
3630             for (var j = 1; j < n; ++j) {
3631                 for (i = 0; i < n - j; ++i) {
3632                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3633                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3634                 }
3635             }
3636
3637             return [ tmp[0][0], tmp[0][1] ];
3638
3639         };
3640     };/*
3641  * Portions of this file are based on pieces of Yahoo User Interface Library
3642  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3643  * YUI licensed under the BSD License:
3644  * http://developer.yahoo.net/yui/license.txt
3645  * <script type="text/javascript">
3646  *
3647  */
3648 (function() {
3649
3650     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3651         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3652     };
3653
3654     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3655
3656     var fly = Roo.lib.AnimBase.fly;
3657     var Y = Roo.lib;
3658     var superclass = Y.ColorAnim.superclass;
3659     var proto = Y.ColorAnim.prototype;
3660
3661     proto.toString = function() {
3662         var el = this.getEl();
3663         var id = el.id || el.tagName;
3664         return ("ColorAnim " + id);
3665     };
3666
3667     proto.patterns.color = /color$/i;
3668     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3669     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3670     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3671     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3672
3673
3674     proto.parseColor = function(s) {
3675         if (s.length == 3) {
3676             return s;
3677         }
3678
3679         var c = this.patterns.hex.exec(s);
3680         if (c && c.length == 4) {
3681             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3682         }
3683
3684         c = this.patterns.rgb.exec(s);
3685         if (c && c.length == 4) {
3686             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3687         }
3688
3689         c = this.patterns.hex3.exec(s);
3690         if (c && c.length == 4) {
3691             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3692         }
3693
3694         return null;
3695     };
3696     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3697     proto.getAttribute = function(attr) {
3698         var el = this.getEl();
3699         if (this.patterns.color.test(attr)) {
3700             var val = fly(el).getStyle(attr);
3701
3702             if (this.patterns.transparent.test(val)) {
3703                 var parent = el.parentNode;
3704                 val = fly(parent).getStyle(attr);
3705
3706                 while (parent && this.patterns.transparent.test(val)) {
3707                     parent = parent.parentNode;
3708                     val = fly(parent).getStyle(attr);
3709                     if (parent.tagName.toUpperCase() == 'HTML') {
3710                         val = '#fff';
3711                     }
3712                 }
3713             }
3714         } else {
3715             val = superclass.getAttribute.call(this, attr);
3716         }
3717
3718         return val;
3719     };
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743
3744     proto.doMethod = function(attr, start, end) {
3745         var val;
3746
3747         if (this.patterns.color.test(attr)) {
3748             val = [];
3749             for (var i = 0, len = start.length; i < len; ++i) {
3750                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3751             }
3752
3753             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3754         }
3755         else {
3756             val = superclass.doMethod.call(this, attr, start, end);
3757         }
3758
3759         return val;
3760     };
3761
3762     proto.setRuntimeAttribute = function(attr) {
3763         superclass.setRuntimeAttribute.call(this, attr);
3764
3765         if (this.patterns.color.test(attr)) {
3766             var attributes = this.attributes;
3767             var start = this.parseColor(this.runtimeAttributes[attr].start);
3768             var end = this.parseColor(this.runtimeAttributes[attr].end);
3769
3770             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3771                 end = this.parseColor(attributes[attr].by);
3772
3773                 for (var i = 0, len = start.length; i < len; ++i) {
3774                     end[i] = start[i] + end[i];
3775                 }
3776             }
3777
3778             this.runtimeAttributes[attr].start = start;
3779             this.runtimeAttributes[attr].end = end;
3780         }
3781     };
3782 })();
3783
3784 /*
3785  * Portions of this file are based on pieces of Yahoo User Interface Library
3786  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3787  * YUI licensed under the BSD License:
3788  * http://developer.yahoo.net/yui/license.txt
3789  * <script type="text/javascript">
3790  *
3791  */
3792 Roo.lib.Easing = {
3793
3794
3795     easeNone: function (t, b, c, d) {
3796         return c * t / d + b;
3797     },
3798
3799
3800     easeIn: function (t, b, c, d) {
3801         return c * (t /= d) * t + b;
3802     },
3803
3804
3805     easeOut: function (t, b, c, d) {
3806         return -c * (t /= d) * (t - 2) + b;
3807     },
3808
3809
3810     easeBoth: function (t, b, c, d) {
3811         if ((t /= d / 2) < 1) {
3812             return c / 2 * t * t + b;
3813         }
3814
3815         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3816     },
3817
3818
3819     easeInStrong: function (t, b, c, d) {
3820         return c * (t /= d) * t * t * t + b;
3821     },
3822
3823
3824     easeOutStrong: function (t, b, c, d) {
3825         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3826     },
3827
3828
3829     easeBothStrong: function (t, b, c, d) {
3830         if ((t /= d / 2) < 1) {
3831             return c / 2 * t * t * t * t + b;
3832         }
3833
3834         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3835     },
3836
3837
3838
3839     elasticIn: function (t, b, c, d, a, p) {
3840         if (t == 0) {
3841             return b;
3842         }
3843         if ((t /= d) == 1) {
3844             return b + c;
3845         }
3846         if (!p) {
3847             p = d * .3;
3848         }
3849
3850         if (!a || a < Math.abs(c)) {
3851             a = c;
3852             var s = p / 4;
3853         }
3854         else {
3855             var s = p / (2 * Math.PI) * Math.asin(c / a);
3856         }
3857
3858         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3859     },
3860
3861
3862     elasticOut: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3882     },
3883
3884
3885     elasticBoth: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889
3890         if ((t /= d / 2) == 2) {
3891             return b + c;
3892         }
3893
3894         if (!p) {
3895             p = d * (.3 * 1.5);
3896         }
3897
3898         if (!a || a < Math.abs(c)) {
3899             a = c;
3900             var s = p / 4;
3901         }
3902         else {
3903             var s = p / (2 * Math.PI) * Math.asin(c / a);
3904         }
3905
3906         if (t < 1) {
3907             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3908                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3909         }
3910         return a * Math.pow(2, -10 * (t -= 1)) *
3911                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3912     },
3913
3914
3915
3916     backIn: function (t, b, c, d, s) {
3917         if (typeof s == 'undefined') {
3918             s = 1.70158;
3919         }
3920         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3921     },
3922
3923
3924     backOut: function (t, b, c, d, s) {
3925         if (typeof s == 'undefined') {
3926             s = 1.70158;
3927         }
3928         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3929     },
3930
3931
3932     backBoth: function (t, b, c, d, s) {
3933         if (typeof s == 'undefined') {
3934             s = 1.70158;
3935         }
3936
3937         if ((t /= d / 2 ) < 1) {
3938             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3939         }
3940         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3941     },
3942
3943
3944     bounceIn: function (t, b, c, d) {
3945         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3946     },
3947
3948
3949     bounceOut: function (t, b, c, d) {
3950         if ((t /= d) < (1 / 2.75)) {
3951             return c * (7.5625 * t * t) + b;
3952         } else if (t < (2 / 2.75)) {
3953             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3954         } else if (t < (2.5 / 2.75)) {
3955             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3956         }
3957         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3958     },
3959
3960
3961     bounceBoth: function (t, b, c, d) {
3962         if (t < d / 2) {
3963             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3964         }
3965         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3966     }
3967 };/*
3968  * Portions of this file are based on pieces of Yahoo User Interface Library
3969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3970  * YUI licensed under the BSD License:
3971  * http://developer.yahoo.net/yui/license.txt
3972  * <script type="text/javascript">
3973  *
3974  */
3975     (function() {
3976         Roo.lib.Motion = function(el, attributes, duration, method) {
3977             if (el) {
3978                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3979             }
3980         };
3981
3982         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3983
3984
3985         var Y = Roo.lib;
3986         var superclass = Y.Motion.superclass;
3987         var proto = Y.Motion.prototype;
3988
3989         proto.toString = function() {
3990             var el = this.getEl();
3991             var id = el.id || el.tagName;
3992             return ("Motion " + id);
3993         };
3994
3995         proto.patterns.points = /^points$/i;
3996
3997         proto.setAttribute = function(attr, val, unit) {
3998             if (this.patterns.points.test(attr)) {
3999                 unit = unit || 'px';
4000                 superclass.setAttribute.call(this, 'left', val[0], unit);
4001                 superclass.setAttribute.call(this, 'top', val[1], unit);
4002             } else {
4003                 superclass.setAttribute.call(this, attr, val, unit);
4004             }
4005         };
4006
4007         proto.getAttribute = function(attr) {
4008             if (this.patterns.points.test(attr)) {
4009                 var val = [
4010                         superclass.getAttribute.call(this, 'left'),
4011                         superclass.getAttribute.call(this, 'top')
4012                         ];
4013             } else {
4014                 val = superclass.getAttribute.call(this, attr);
4015             }
4016
4017             return val;
4018         };
4019
4020         proto.doMethod = function(attr, start, end) {
4021             var val = null;
4022
4023             if (this.patterns.points.test(attr)) {
4024                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4025                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4026             } else {
4027                 val = superclass.doMethod.call(this, attr, start, end);
4028             }
4029             return val;
4030         };
4031
4032         proto.setRuntimeAttribute = function(attr) {
4033             if (this.patterns.points.test(attr)) {
4034                 var el = this.getEl();
4035                 var attributes = this.attributes;
4036                 var start;
4037                 var control = attributes['points']['control'] || [];
4038                 var end;
4039                 var i, len;
4040
4041                 if (control.length > 0 && !(control[0] instanceof Array)) {
4042                     control = [control];
4043                 } else {
4044                     var tmp = [];
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         tmp[i] = control[i];
4047                     }
4048                     control = tmp;
4049                 }
4050
4051                 Roo.fly(el).position();
4052
4053                 if (isset(attributes['points']['from'])) {
4054                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4055                 }
4056                 else {
4057                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4058                 }
4059
4060                 start = this.getAttribute('points');
4061
4062
4063                 if (isset(attributes['points']['to'])) {
4064                     end = translateValues.call(this, attributes['points']['to'], start);
4065
4066                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4067                     for (i = 0,len = control.length; i < len; ++i) {
4068                         control[i] = translateValues.call(this, control[i], start);
4069                     }
4070
4071
4072                 } else if (isset(attributes['points']['by'])) {
4073                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4074
4075                     for (i = 0,len = control.length; i < len; ++i) {
4076                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4077                     }
4078                 }
4079
4080                 this.runtimeAttributes[attr] = [start];
4081
4082                 if (control.length > 0) {
4083                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4084                 }
4085
4086                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4087             }
4088             else {
4089                 superclass.setRuntimeAttribute.call(this, attr);
4090             }
4091         };
4092
4093         var translateValues = function(val, start) {
4094             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4095             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4096
4097             return val;
4098         };
4099
4100         var isset = function(prop) {
4101             return (typeof prop !== 'undefined');
4102         };
4103     })();
4104 /*
4105  * Portions of this file are based on pieces of Yahoo User Interface Library
4106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4107  * YUI licensed under the BSD License:
4108  * http://developer.yahoo.net/yui/license.txt
4109  * <script type="text/javascript">
4110  *
4111  */
4112     (function() {
4113         Roo.lib.Scroll = function(el, attributes, duration, method) {
4114             if (el) {
4115                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4116             }
4117         };
4118
4119         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4120
4121
4122         var Y = Roo.lib;
4123         var superclass = Y.Scroll.superclass;
4124         var proto = Y.Scroll.prototype;
4125
4126         proto.toString = function() {
4127             var el = this.getEl();
4128             var id = el.id || el.tagName;
4129             return ("Scroll " + id);
4130         };
4131
4132         proto.doMethod = function(attr, start, end) {
4133             var val = null;
4134
4135             if (attr == 'scroll') {
4136                 val = [
4137                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4138                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4139                         ];
4140
4141             } else {
4142                 val = superclass.doMethod.call(this, attr, start, end);
4143             }
4144             return val;
4145         };
4146
4147         proto.getAttribute = function(attr) {
4148             var val = null;
4149             var el = this.getEl();
4150
4151             if (attr == 'scroll') {
4152                 val = [ el.scrollLeft, el.scrollTop ];
4153             } else {
4154                 val = superclass.getAttribute.call(this, attr);
4155             }
4156
4157             return val;
4158         };
4159
4160         proto.setAttribute = function(attr, val, unit) {
4161             var el = this.getEl();
4162
4163             if (attr == 'scroll') {
4164                 el.scrollLeft = val[0];
4165                 el.scrollTop = val[1];
4166             } else {
4167                 superclass.setAttribute.call(this, attr, val, unit);
4168             }
4169         };
4170     })();
4171 /*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181
4182
4183 // nasty IE9 hack - what a pile of crap that is..
4184
4185  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4186     Range.prototype.createContextualFragment = function (html) {
4187         var doc = window.document;
4188         var container = doc.createElement("div");
4189         container.innerHTML = html;
4190         var frag = doc.createDocumentFragment(), n;
4191         while ((n = container.firstChild)) {
4192             frag.appendChild(n);
4193         }
4194         return frag;
4195     };
4196 }
4197
4198 /**
4199  * @class Roo.DomHelper
4200  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4201  * 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>.
4202  * @singleton
4203  */
4204 Roo.DomHelper = function(){
4205     var tempTableEl = null;
4206     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4207     var tableRe = /^table|tbody|tr|td$/i;
4208     var xmlns = {};
4209     // build as innerHTML where available
4210     /** @ignore */
4211     var createHtml = function(o){
4212         if(typeof o == 'string'){
4213             return o;
4214         }
4215         var b = "";
4216         if(!o.tag){
4217             o.tag = "div";
4218         }
4219         b += "<" + o.tag;
4220         for(var attr in o){
4221             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4222             if(attr == "style"){
4223                 var s = o["style"];
4224                 if(typeof s == "function"){
4225                     s = s.call();
4226                 }
4227                 if(typeof s == "string"){
4228                     b += ' style="' + s + '"';
4229                 }else if(typeof s == "object"){
4230                     b += ' style="';
4231                     for(var key in s){
4232                         if(typeof s[key] != "function"){
4233                             b += key + ":" + s[key] + ";";
4234                         }
4235                     }
4236                     b += '"';
4237                 }
4238             }else{
4239                 if(attr == "cls"){
4240                     b += ' class="' + o["cls"] + '"';
4241                 }else if(attr == "htmlFor"){
4242                     b += ' for="' + o["htmlFor"] + '"';
4243                 }else{
4244                     b += " " + attr + '="' + o[attr] + '"';
4245                 }
4246             }
4247         }
4248         if(emptyTags.test(o.tag)){
4249             b += "/>";
4250         }else{
4251             b += ">";
4252             var cn = o.children || o.cn;
4253             if(cn){
4254                 //http://bugs.kde.org/show_bug.cgi?id=71506
4255                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4256                     for(var i = 0, len = cn.length; i < len; i++) {
4257                         b += createHtml(cn[i], b);
4258                     }
4259                 }else{
4260                     b += createHtml(cn, b);
4261                 }
4262             }
4263             if(o.html){
4264                 b += o.html;
4265             }
4266             b += "</" + o.tag + ">";
4267         }
4268         return b;
4269     };
4270
4271     // build as dom
4272     /** @ignore */
4273     var createDom = function(o, parentNode){
4274          
4275         // defininition craeted..
4276         var ns = false;
4277         if (o.ns && o.ns != 'html') {
4278                
4279             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4280                 xmlns[o.ns] = o.xmlns;
4281                 ns = o.xmlns;
4282             }
4283             if (typeof(xmlns[o.ns]) == 'undefined') {
4284                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4285             }
4286             ns = xmlns[o.ns];
4287         }
4288         
4289         
4290         if (typeof(o) == 'string') {
4291             return parentNode.appendChild(document.createTextNode(o));
4292         }
4293         o.tag = o.tag || div;
4294         if (o.ns && Roo.isIE) {
4295             ns = false;
4296             o.tag = o.ns + ':' + o.tag;
4297             
4298         }
4299         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4300         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4301         for(var attr in o){
4302             
4303             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4304                     attr == "style" || typeof o[attr] == "function") { continue; }
4305                     
4306             if(attr=="cls" && Roo.isIE){
4307                 el.className = o["cls"];
4308             }else{
4309                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4310                 else { 
4311                     el[attr] = o[attr];
4312                 }
4313             }
4314         }
4315         Roo.DomHelper.applyStyles(el, o.style);
4316         var cn = o.children || o.cn;
4317         if(cn){
4318             //http://bugs.kde.org/show_bug.cgi?id=71506
4319              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4320                 for(var i = 0, len = cn.length; i < len; i++) {
4321                     createDom(cn[i], el);
4322                 }
4323             }else{
4324                 createDom(cn, el);
4325             }
4326         }
4327         if(o.html){
4328             el.innerHTML = o.html;
4329         }
4330         if(parentNode){
4331            parentNode.appendChild(el);
4332         }
4333         return el;
4334     };
4335
4336     var ieTable = function(depth, s, h, e){
4337         tempTableEl.innerHTML = [s, h, e].join('');
4338         var i = -1, el = tempTableEl;
4339         while(++i < depth){
4340             el = el.firstChild;
4341         }
4342         return el;
4343     };
4344
4345     // kill repeat to save bytes
4346     var ts = '<table>',
4347         te = '</table>',
4348         tbs = ts+'<tbody>',
4349         tbe = '</tbody>'+te,
4350         trs = tbs + '<tr>',
4351         tre = '</tr>'+tbe;
4352
4353     /**
4354      * @ignore
4355      * Nasty code for IE's broken table implementation
4356      */
4357     var insertIntoTable = function(tag, where, el, html){
4358         if(!tempTableEl){
4359             tempTableEl = document.createElement('div');
4360         }
4361         var node;
4362         var before = null;
4363         if(tag == 'td'){
4364             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4365                 return;
4366             }
4367             if(where == 'beforebegin'){
4368                 before = el;
4369                 el = el.parentNode;
4370             } else{
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373             }
4374             node = ieTable(4, trs, html, tre);
4375         }
4376         else if(tag == 'tr'){
4377             if(where == 'beforebegin'){
4378                 before = el;
4379                 el = el.parentNode;
4380                 node = ieTable(3, tbs, html, tbe);
4381             } else if(where == 'afterend'){
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384                 node = ieTable(3, tbs, html, tbe);
4385             } else{ // INTO a TR
4386                 if(where == 'afterbegin'){
4387                     before = el.firstChild;
4388                 }
4389                 node = ieTable(4, trs, html, tre);
4390             }
4391         } else if(tag == 'tbody'){
4392             if(where == 'beforebegin'){
4393                 before = el;
4394                 el = el.parentNode;
4395                 node = ieTable(2, ts, html, te);
4396             } else if(where == 'afterend'){
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399                 node = ieTable(2, ts, html, te);
4400             } else{
4401                 if(where == 'afterbegin'){
4402                     before = el.firstChild;
4403                 }
4404                 node = ieTable(3, tbs, html, tbe);
4405             }
4406         } else{ // TABLE
4407             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4408                 return;
4409             }
4410             if(where == 'afterbegin'){
4411                 before = el.firstChild;
4412             }
4413             node = ieTable(2, ts, html, te);
4414         }
4415         el.insertBefore(node, before);
4416         return node;
4417     };
4418
4419     return {
4420     /** True to force the use of DOM instead of html fragments @type Boolean */
4421     useDom : false,
4422
4423     /**
4424      * Returns the markup for the passed Element(s) config
4425      * @param {Object} o The Dom object spec (and children)
4426      * @return {String}
4427      */
4428     markup : function(o){
4429         return createHtml(o);
4430     },
4431
4432     /**
4433      * Applies a style specification to an element
4434      * @param {String/HTMLElement} el The element to apply styles to
4435      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4436      * a function which returns such a specification.
4437      */
4438     applyStyles : function(el, styles){
4439         if(styles){
4440            el = Roo.fly(el);
4441            if(typeof styles == "string"){
4442                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4443                var matches;
4444                while ((matches = re.exec(styles)) != null){
4445                    el.setStyle(matches[1], matches[2]);
4446                }
4447            }else if (typeof styles == "object"){
4448                for (var style in styles){
4449                   el.setStyle(style, styles[style]);
4450                }
4451            }else if (typeof styles == "function"){
4452                 Roo.DomHelper.applyStyles(el, styles.call());
4453            }
4454         }
4455     },
4456
4457     /**
4458      * Inserts an HTML fragment into the Dom
4459      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4460      * @param {HTMLElement} el The context element
4461      * @param {String} html The HTML fragmenet
4462      * @return {HTMLElement} The new node
4463      */
4464     insertHtml : function(where, el, html){
4465         where = where.toLowerCase();
4466         if(el.insertAdjacentHTML){
4467             if(tableRe.test(el.tagName)){
4468                 var rs;
4469                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4470                     return rs;
4471                 }
4472             }
4473             switch(where){
4474                 case "beforebegin":
4475                     el.insertAdjacentHTML('BeforeBegin', html);
4476                     return el.previousSibling;
4477                 case "afterbegin":
4478                     el.insertAdjacentHTML('AfterBegin', html);
4479                     return el.firstChild;
4480                 case "beforeend":
4481                     el.insertAdjacentHTML('BeforeEnd', html);
4482                     return el.lastChild;
4483                 case "afterend":
4484                     el.insertAdjacentHTML('AfterEnd', html);
4485                     return el.nextSibling;
4486             }
4487             throw 'Illegal insertion point -> "' + where + '"';
4488         }
4489         var range = el.ownerDocument.createRange();
4490         var frag;
4491         switch(where){
4492              case "beforebegin":
4493                 range.setStartBefore(el);
4494                 frag = range.createContextualFragment(html);
4495                 el.parentNode.insertBefore(frag, el);
4496                 return el.previousSibling;
4497              case "afterbegin":
4498                 if(el.firstChild){
4499                     range.setStartBefore(el.firstChild);
4500                     frag = range.createContextualFragment(html);
4501                     el.insertBefore(frag, el.firstChild);
4502                     return el.firstChild;
4503                 }else{
4504                     el.innerHTML = html;
4505                     return el.firstChild;
4506                 }
4507             case "beforeend":
4508                 if(el.lastChild){
4509                     range.setStartAfter(el.lastChild);
4510                     frag = range.createContextualFragment(html);
4511                     el.appendChild(frag);
4512                     return el.lastChild;
4513                 }else{
4514                     el.innerHTML = html;
4515                     return el.lastChild;
4516                 }
4517             case "afterend":
4518                 range.setStartAfter(el);
4519                 frag = range.createContextualFragment(html);
4520                 el.parentNode.insertBefore(frag, el.nextSibling);
4521                 return el.nextSibling;
4522             }
4523             throw 'Illegal insertion point -> "' + where + '"';
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them before el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertBefore : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "beforeBegin");
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them after el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object} o The Dom object spec (and children)
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertAfter : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them as the first child of el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertFirst : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterBegin");
4557     },
4558
4559     // private
4560     doInsert : function(el, o, returnElement, pos, sibling){
4561         el = Roo.getDom(el);
4562         var newNode;
4563         if(this.useDom || o.ns){
4564             newNode = createDom(o, null);
4565             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4566         }else{
4567             var html = createHtml(o);
4568             newNode = this.insertHtml(pos, el, html);
4569         }
4570         return returnElement ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Creates new Dom element(s) and appends them to el
4575      * @param {String/HTMLElement/Element} el The context element
4576      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4578      * @return {HTMLElement/Roo.Element} The new node
4579      */
4580     append : function(el, o, returnElement){
4581         el = Roo.getDom(el);
4582         var newNode;
4583         if(this.useDom || o.ns){
4584             newNode = createDom(o, null);
4585             el.appendChild(newNode);
4586         }else{
4587             var html = createHtml(o);
4588             newNode = this.insertHtml("beforeEnd", el, html);
4589         }
4590         return returnElement ? Roo.get(newNode, true) : newNode;
4591     },
4592
4593     /**
4594      * Creates new Dom element(s) and overwrites the contents of el with them
4595      * @param {String/HTMLElement/Element} el The context element
4596      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4597      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4598      * @return {HTMLElement/Roo.Element} The new node
4599      */
4600     overwrite : function(el, o, returnElement){
4601         el = Roo.getDom(el);
4602         if (o.ns) {
4603           
4604             while (el.childNodes.length) {
4605                 el.removeChild(el.firstChild);
4606             }
4607             createDom(o, el);
4608         } else {
4609             el.innerHTML = createHtml(o);   
4610         }
4611         
4612         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4613     },
4614
4615     /**
4616      * Creates a new Roo.DomHelper.Template from the Dom object spec
4617      * @param {Object} o The Dom object spec (and children)
4618      * @return {Roo.DomHelper.Template} The new template
4619      */
4620     createTemplate : function(o){
4621         var html = createHtml(o);
4622         return new Roo.Template(html);
4623     }
4624     };
4625 }();
4626 /*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637 /**
4638 * @class Roo.Template
4639 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4640 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4641 * Usage:
4642 <pre><code>
4643 var t = new Roo.Template({
4644     html :  '&lt;div name="{id}"&gt;' + 
4645         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4646         '&lt;/div&gt;',
4647     myformat: function (value, allValues) {
4648         return 'XX' + value;
4649     }
4650 });
4651 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4652 </code></pre>
4653 * For more information see this blog post with examples:
4654 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4655      - Create Elements using DOM, HTML fragments and Templates</a>. 
4656 * @constructor
4657 * @param {Object} cfg - Configuration object.
4658 */
4659 Roo.Template = function(cfg){
4660     // BC!
4661     if(cfg instanceof Array){
4662         cfg = cfg.join("");
4663     }else if(arguments.length > 1){
4664         cfg = Array.prototype.join.call(arguments, "");
4665     }
4666     
4667     
4668     if (typeof(cfg) == 'object') {
4669         Roo.apply(this,cfg)
4670     } else {
4671         // bc
4672         this.html = cfg;
4673     }
4674     if (this.url) {
4675         this.load();
4676     }
4677     
4678 };
4679 Roo.Template.prototype = {
4680     
4681     /**
4682      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4683      */
4684     onLoad : false,
4685     
4686     
4687     /**
4688      * @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..
4689      *                    it should be fixed so that template is observable...
4690      */
4691     url : false,
4692     /**
4693      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4694      */
4695     html : '',
4696     
4697     
4698     compiled : false,
4699     loaded : false,
4700     /**
4701      * Returns an HTML fragment of this template with the specified values applied.
4702      * @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'})
4703      * @return {String} The HTML fragment
4704      */
4705     
4706    
4707     
4708     applyTemplate : function(values){
4709         //Roo.log(["applyTemplate", values]);
4710         try {
4711            
4712             if(this.compiled){
4713                 return this.compiled(values);
4714             }
4715             var useF = this.disableFormats !== true;
4716             var fm = Roo.util.Format, tpl = this;
4717             var fn = function(m, name, format, args){
4718                 if(format && useF){
4719                     if(format.substr(0, 5) == "this."){
4720                         return tpl.call(format.substr(5), values[name], values);
4721                     }else{
4722                         if(args){
4723                             // quoted values are required for strings in compiled templates, 
4724                             // but for non compiled we need to strip them
4725                             // quoted reversed for jsmin
4726                             var re = /^\s*['"](.*)["']\s*$/;
4727                             args = args.split(',');
4728                             for(var i = 0, len = args.length; i < len; i++){
4729                                 args[i] = args[i].replace(re, "$1");
4730                             }
4731                             args = [values[name]].concat(args);
4732                         }else{
4733                             args = [values[name]];
4734                         }
4735                         return fm[format].apply(fm, args);
4736                     }
4737                 }else{
4738                     return values[name] !== undefined ? values[name] : "";
4739                 }
4740             };
4741             return this.html.replace(this.re, fn);
4742         } catch (e) {
4743             Roo.log(e);
4744             throw e;
4745         }
4746          
4747     },
4748     
4749     loading : false,
4750       
4751     load : function ()
4752     {
4753          
4754         if (this.loading) {
4755             return;
4756         }
4757         var _t = this;
4758         
4759         this.loading = true;
4760         this.compiled = false;
4761         
4762         var cx = new Roo.data.Connection();
4763         cx.request({
4764             url : this.url,
4765             method : 'GET',
4766             success : function (response) {
4767                 _t.loading = false;
4768                 _t.url = false;
4769                 
4770                 _t.set(response.responseText,true);
4771                 _t.loaded = true;
4772                 if (_t.onLoad) {
4773                     _t.onLoad();
4774                 }
4775              },
4776             failure : function(response) {
4777                 Roo.log("Template failed to load from " + _t.url);
4778                 _t.loading = false;
4779             }
4780         });
4781     },
4782
4783     /**
4784      * Sets the HTML used as the template and optionally compiles it.
4785      * @param {String} html
4786      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4787      * @return {Roo.Template} this
4788      */
4789     set : function(html, compile){
4790         this.html = html;
4791         this.compiled = false;
4792         if(compile){
4793             this.compile();
4794         }
4795         return this;
4796     },
4797     
4798     /**
4799      * True to disable format functions (defaults to false)
4800      * @type Boolean
4801      */
4802     disableFormats : false,
4803     
4804     /**
4805     * The regular expression used to match template variables 
4806     * @type RegExp
4807     * @property 
4808     */
4809     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4810     
4811     /**
4812      * Compiles the template into an internal function, eliminating the RegEx overhead.
4813      * @return {Roo.Template} this
4814      */
4815     compile : function(){
4816         var fm = Roo.util.Format;
4817         var useF = this.disableFormats !== true;
4818         var sep = Roo.isGecko ? "+" : ",";
4819         var fn = function(m, name, format, args){
4820             if(format && useF){
4821                 args = args ? ',' + args : "";
4822                 if(format.substr(0, 5) != "this."){
4823                     format = "fm." + format + '(';
4824                 }else{
4825                     format = 'this.call("'+ format.substr(5) + '", ';
4826                     args = ", values";
4827                 }
4828             }else{
4829                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4830             }
4831             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4832         };
4833         var body;
4834         // branched to use + in gecko and [].join() in others
4835         if(Roo.isGecko){
4836             body = "this.compiled = function(values){ return '" +
4837                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4838                     "';};";
4839         }else{
4840             body = ["this.compiled = function(values){ return ['"];
4841             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4842             body.push("'].join('');};");
4843             body = body.join('');
4844         }
4845         /**
4846          * eval:var:values
4847          * eval:var:fm
4848          */
4849         eval(body);
4850         return this;
4851     },
4852     
4853     // private function used to call members
4854     call : function(fnName, value, allValues){
4855         return this[fnName](value, allValues);
4856     },
4857     
4858     /**
4859      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4860      * @param {String/HTMLElement/Roo.Element} el The context element
4861      * @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'})
4862      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4863      * @return {HTMLElement/Roo.Element} The new node or Element
4864      */
4865     insertFirst: function(el, values, returnElement){
4866         return this.doInsert('afterBegin', el, values, returnElement);
4867     },
4868
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) before 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     insertBefore: function(el, values, returnElement){
4877         return this.doInsert('beforeBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) after 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     insertAfter : function(el, values, returnElement){
4888         return this.doInsert('afterEnd', el, values, returnElement);
4889     },
4890     
4891     /**
4892      * Applies the supplied values to the template and appends the new node(s) to 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     append : function(el, values, returnElement){
4899         return this.doInsert('beforeEnd', el, values, returnElement);
4900     },
4901
4902     doInsert : function(where, el, values, returnEl){
4903         el = Roo.getDom(el);
4904         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4905         return returnEl ? Roo.get(newNode, true) : newNode;
4906     },
4907
4908     /**
4909      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4910      * @param {String/HTMLElement/Roo.Element} el The context element
4911      * @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'})
4912      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4913      * @return {HTMLElement/Roo.Element} The new node or Element
4914      */
4915     overwrite : function(el, values, returnElement){
4916         el = Roo.getDom(el);
4917         el.innerHTML = this.applyTemplate(values);
4918         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4919     }
4920 };
4921 /**
4922  * Alias for {@link #applyTemplate}
4923  * @method
4924  */
4925 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4926
4927 // backwards compat
4928 Roo.DomHelper.Template = Roo.Template;
4929
4930 /**
4931  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4932  * @param {String/HTMLElement} el A DOM element or its id
4933  * @returns {Roo.Template} The created template
4934  * @static
4935  */
4936 Roo.Template.from = function(el){
4937     el = Roo.getDom(el);
4938     return new Roo.Template(el.value || el.innerHTML);
4939 };/*
4940  * Based on:
4941  * Ext JS Library 1.1.1
4942  * Copyright(c) 2006-2007, Ext JS, LLC.
4943  *
4944  * Originally Released Under LGPL - original licence link has changed is not relivant.
4945  *
4946  * Fork - LGPL
4947  * <script type="text/javascript">
4948  */
4949  
4950
4951 /*
4952  * This is code is also distributed under MIT license for use
4953  * with jQuery and prototype JavaScript libraries.
4954  */
4955 /**
4956  * @class Roo.DomQuery
4957 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).
4958 <p>
4959 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>
4960
4961 <p>
4962 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.
4963 </p>
4964 <h4>Element Selectors:</h4>
4965 <ul class="list">
4966     <li> <b>*</b> any element</li>
4967     <li> <b>E</b> an element with the tag E</li>
4968     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4969     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4970     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4971     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4972 </ul>
4973 <h4>Attribute Selectors:</h4>
4974 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4975 <ul class="list">
4976     <li> <b>E[foo]</b> has an attribute "foo"</li>
4977     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4978     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4979     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4980     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4981     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4982     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4983 </ul>
4984 <h4>Pseudo Classes:</h4>
4985 <ul class="list">
4986     <li> <b>E:first-child</b> E is the first child of its parent</li>
4987     <li> <b>E:last-child</b> E is the last child of its parent</li>
4988     <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>
4989     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4990     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4991     <li> <b>E:only-child</b> E is the only child of its parent</li>
4992     <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>
4993     <li> <b>E:first</b> the first E in the resultset</li>
4994     <li> <b>E:last</b> the last E in the resultset</li>
4995     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4996     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4997     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4998     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4999     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5000     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5001     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5002     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5003     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5004 </ul>
5005 <h4>CSS Value Selectors:</h4>
5006 <ul class="list">
5007     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5008     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5009     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5010     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5011     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5012     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5013 </ul>
5014  * @singleton
5015  */
5016 Roo.DomQuery = function(){
5017     var cache = {}, simpleCache = {}, valueCache = {};
5018     var nonSpace = /\S/;
5019     var trimRe = /^\s+|\s+$/g;
5020     var tplRe = /\{(\d+)\}/g;
5021     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5022     var tagTokenRe = /^(#)?([\w-\*]+)/;
5023     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5024
5025     function child(p, index){
5026         var i = 0;
5027         var n = p.firstChild;
5028         while(n){
5029             if(n.nodeType == 1){
5030                if(++i == index){
5031                    return n;
5032                }
5033             }
5034             n = n.nextSibling;
5035         }
5036         return null;
5037     };
5038
5039     function next(n){
5040         while((n = n.nextSibling) && n.nodeType != 1);
5041         return n;
5042     };
5043
5044     function prev(n){
5045         while((n = n.previousSibling) && n.nodeType != 1);
5046         return n;
5047     };
5048
5049     function children(d){
5050         var n = d.firstChild, ni = -1;
5051             while(n){
5052                 var nx = n.nextSibling;
5053                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5054                     d.removeChild(n);
5055                 }else{
5056                     n.nodeIndex = ++ni;
5057                 }
5058                 n = nx;
5059             }
5060             return this;
5061         };
5062
5063     function byClassName(c, a, v){
5064         if(!v){
5065             return c;
5066         }
5067         var r = [], ri = -1, cn;
5068         for(var i = 0, ci; ci = c[i]; i++){
5069             if((' '+ci.className+' ').indexOf(v) != -1){
5070                 r[++ri] = ci;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function attrValue(n, attr){
5077         if(!n.tagName && typeof n.length != "undefined"){
5078             n = n[0];
5079         }
5080         if(!n){
5081             return null;
5082         }
5083         if(attr == "for"){
5084             return n.htmlFor;
5085         }
5086         if(attr == "class" || attr == "className"){
5087             return n.className;
5088         }
5089         return n.getAttribute(attr) || n[attr];
5090
5091     };
5092
5093     function getNodes(ns, mode, tagName){
5094         var result = [], ri = -1, cs;
5095         if(!ns){
5096             return result;
5097         }
5098         tagName = tagName || "*";
5099         if(typeof ns.getElementsByTagName != "undefined"){
5100             ns = [ns];
5101         }
5102         if(!mode){
5103             for(var i = 0, ni; ni = ns[i]; i++){
5104                 cs = ni.getElementsByTagName(tagName);
5105                 for(var j = 0, ci; ci = cs[j]; j++){
5106                     result[++ri] = ci;
5107                 }
5108             }
5109         }else if(mode == "/" || mode == ">"){
5110             var utag = tagName.toUpperCase();
5111             for(var i = 0, ni, cn; ni = ns[i]; i++){
5112                 cn = ni.children || ni.childNodes;
5113                 for(var j = 0, cj; cj = cn[j]; j++){
5114                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5115                         result[++ri] = cj;
5116                     }
5117                 }
5118             }
5119         }else if(mode == "+"){
5120             var utag = tagName.toUpperCase();
5121             for(var i = 0, n; n = ns[i]; i++){
5122                 while((n = n.nextSibling) && n.nodeType != 1);
5123                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5124                     result[++ri] = n;
5125                 }
5126             }
5127         }else if(mode == "~"){
5128             for(var i = 0, n; n = ns[i]; i++){
5129                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5130                 if(n){
5131                     result[++ri] = n;
5132                 }
5133             }
5134         }
5135         return result;
5136     };
5137
5138     function concat(a, b){
5139         if(b.slice){
5140             return a.concat(b);
5141         }
5142         for(var i = 0, l = b.length; i < l; i++){
5143             a[a.length] = b[i];
5144         }
5145         return a;
5146     }
5147
5148     function byTag(cs, tagName){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!tagName){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         tagName = tagName.toLowerCase();
5157         for(var i = 0, ci; ci = cs[i]; i++){
5158             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5159                 r[++ri] = ci;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byId(cs, attr, id){
5166         if(cs.tagName || cs == document){
5167             cs = [cs];
5168         }
5169         if(!id){
5170             return cs;
5171         }
5172         var r = [], ri = -1;
5173         for(var i = 0,ci; ci = cs[i]; i++){
5174             if(ci && ci.id == id){
5175                 r[++ri] = ci;
5176                 return r;
5177             }
5178         }
5179         return r;
5180     };
5181
5182     function byAttribute(cs, attr, value, op, custom){
5183         var r = [], ri = -1, st = custom=="{";
5184         var f = Roo.DomQuery.operators[op];
5185         for(var i = 0, ci; ci = cs[i]; i++){
5186             var a;
5187             if(st){
5188                 a = Roo.DomQuery.getStyle(ci, attr);
5189             }
5190             else if(attr == "class" || attr == "className"){
5191                 a = ci.className;
5192             }else if(attr == "for"){
5193                 a = ci.htmlFor;
5194             }else if(attr == "href"){
5195                 a = ci.getAttribute("href", 2);
5196             }else{
5197                 a = ci.getAttribute(attr);
5198             }
5199             if((f && f(a, value)) || (!f && a)){
5200                 r[++ri] = ci;
5201             }
5202         }
5203         return r;
5204     };
5205
5206     function byPseudo(cs, name, value){
5207         return Roo.DomQuery.pseudos[name](cs, value);
5208     };
5209
5210     // This is for IE MSXML which does not support expandos.
5211     // IE runs the same speed using setAttribute, however FF slows way down
5212     // and Safari completely fails so they need to continue to use expandos.
5213     var isIE = window.ActiveXObject ? true : false;
5214
5215     // this eval is stop the compressor from
5216     // renaming the variable to something shorter
5217     
5218     /** eval:var:batch */
5219     var batch = 30803; 
5220
5221     var key = 30803;
5222
5223     function nodupIEXml(cs){
5224         var d = ++key;
5225         cs[0].setAttribute("_nodup", d);
5226         var r = [cs[0]];
5227         for(var i = 1, len = cs.length; i < len; i++){
5228             var c = cs[i];
5229             if(!c.getAttribute("_nodup") != d){
5230                 c.setAttribute("_nodup", d);
5231                 r[r.length] = c;
5232             }
5233         }
5234         for(var i = 0, len = cs.length; i < len; i++){
5235             cs[i].removeAttribute("_nodup");
5236         }
5237         return r;
5238     }
5239
5240     function nodup(cs){
5241         if(!cs){
5242             return [];
5243         }
5244         var len = cs.length, c, i, r = cs, cj, ri = -1;
5245         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5246             return cs;
5247         }
5248         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5249             return nodupIEXml(cs);
5250         }
5251         var d = ++key;
5252         cs[0]._nodup = d;
5253         for(i = 1; c = cs[i]; i++){
5254             if(c._nodup != d){
5255                 c._nodup = d;
5256             }else{
5257                 r = [];
5258                 for(var j = 0; j < i; j++){
5259                     r[++ri] = cs[j];
5260                 }
5261                 for(j = i+1; cj = cs[j]; j++){
5262                     if(cj._nodup != d){
5263                         cj._nodup = d;
5264                         r[++ri] = cj;
5265                     }
5266                 }
5267                 return r;
5268             }
5269         }
5270         return r;
5271     }
5272
5273     function quickDiffIEXml(c1, c2){
5274         var d = ++key;
5275         for(var i = 0, len = c1.length; i < len; i++){
5276             c1[i].setAttribute("_qdiff", d);
5277         }
5278         var r = [];
5279         for(var i = 0, len = c2.length; i < len; i++){
5280             if(c2[i].getAttribute("_qdiff") != d){
5281                 r[r.length] = c2[i];
5282             }
5283         }
5284         for(var i = 0, len = c1.length; i < len; i++){
5285            c1[i].removeAttribute("_qdiff");
5286         }
5287         return r;
5288     }
5289
5290     function quickDiff(c1, c2){
5291         var len1 = c1.length;
5292         if(!len1){
5293             return c2;
5294         }
5295         if(isIE && c1[0].selectSingleNode){
5296             return quickDiffIEXml(c1, c2);
5297         }
5298         var d = ++key;
5299         for(var i = 0; i < len1; i++){
5300             c1[i]._qdiff = d;
5301         }
5302         var r = [];
5303         for(var i = 0, len = c2.length; i < len; i++){
5304             if(c2[i]._qdiff != d){
5305                 r[r.length] = c2[i];
5306             }
5307         }
5308         return r;
5309     }
5310
5311     function quickId(ns, mode, root, id){
5312         if(ns == root){
5313            var d = root.ownerDocument || root;
5314            return d.getElementById(id);
5315         }
5316         ns = getNodes(ns, mode, "*");
5317         return byId(ns, null, id);
5318     }
5319
5320     return {
5321         getStyle : function(el, name){
5322             return Roo.fly(el).getStyle(name);
5323         },
5324         /**
5325          * Compiles a selector/xpath query into a reusable function. The returned function
5326          * takes one parameter "root" (optional), which is the context node from where the query should start.
5327          * @param {String} selector The selector/xpath query
5328          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5329          * @return {Function}
5330          */
5331         compile : function(path, type){
5332             type = type || "select";
5333             
5334             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5335             var q = path, mode, lq;
5336             var tk = Roo.DomQuery.matchers;
5337             var tklen = tk.length;
5338             var mm;
5339
5340             // accept leading mode switch
5341             var lmode = q.match(modeRe);
5342             if(lmode && lmode[1]){
5343                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5344                 q = q.replace(lmode[1], "");
5345             }
5346             // strip leading slashes
5347             while(path.substr(0, 1)=="/"){
5348                 path = path.substr(1);
5349             }
5350
5351             while(q && lq != q){
5352                 lq = q;
5353                 var tm = q.match(tagTokenRe);
5354                 if(type == "select"){
5355                     if(tm){
5356                         if(tm[1] == "#"){
5357                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5358                         }else{
5359                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5360                         }
5361                         q = q.replace(tm[0], "");
5362                     }else if(q.substr(0, 1) != '@'){
5363                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5364                     }
5365                 }else{
5366                     if(tm){
5367                         if(tm[1] == "#"){
5368                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5369                         }else{
5370                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5371                         }
5372                         q = q.replace(tm[0], "");
5373                     }
5374                 }
5375                 while(!(mm = q.match(modeRe))){
5376                     var matched = false;
5377                     for(var j = 0; j < tklen; j++){
5378                         var t = tk[j];
5379                         var m = q.match(t.re);
5380                         if(m){
5381                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5382                                                     return m[i];
5383                                                 });
5384                             q = q.replace(m[0], "");
5385                             matched = true;
5386                             break;
5387                         }
5388                     }
5389                     // prevent infinite loop on bad selector
5390                     if(!matched){
5391                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5392                     }
5393                 }
5394                 if(mm[1]){
5395                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5396                     q = q.replace(mm[1], "");
5397                 }
5398             }
5399             fn[fn.length] = "return nodup(n);\n}";
5400             
5401              /** 
5402               * list of variables that need from compression as they are used by eval.
5403              *  eval:var:batch 
5404              *  eval:var:nodup
5405              *  eval:var:byTag
5406              *  eval:var:ById
5407              *  eval:var:getNodes
5408              *  eval:var:quickId
5409              *  eval:var:mode
5410              *  eval:var:root
5411              *  eval:var:n
5412              *  eval:var:byClassName
5413              *  eval:var:byPseudo
5414              *  eval:var:byAttribute
5415              *  eval:var:attrValue
5416              * 
5417              **/ 
5418             eval(fn.join(""));
5419             return f;
5420         },
5421
5422         /**
5423          * Selects a group of elements.
5424          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @return {Array}
5427          */
5428         select : function(path, root, type){
5429             if(!root || root == document){
5430                 root = document;
5431             }
5432             if(typeof root == "string"){
5433                 root = document.getElementById(root);
5434             }
5435             var paths = path.split(",");
5436             var results = [];
5437             for(var i = 0, len = paths.length; i < len; i++){
5438                 var p = paths[i].replace(trimRe, "");
5439                 if(!cache[p]){
5440                     cache[p] = Roo.DomQuery.compile(p);
5441                     if(!cache[p]){
5442                         throw p + " is not a valid selector";
5443                     }
5444                 }
5445                 var result = cache[p](root);
5446                 if(result && result != document){
5447                     results = results.concat(result);
5448                 }
5449             }
5450             if(paths.length > 1){
5451                 return nodup(results);
5452             }
5453             return results;
5454         },
5455
5456         /**
5457          * Selects a single element.
5458          * @param {String} selector The selector/xpath query
5459          * @param {Node} root (optional) The start of the query (defaults to document).
5460          * @return {Element}
5461          */
5462         selectNode : function(path, root){
5463             return Roo.DomQuery.select(path, root)[0];
5464         },
5465
5466         /**
5467          * Selects the value of a node, optionally replacing null with the defaultValue.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {String} defaultValue
5471          */
5472         selectValue : function(path, root, defaultValue){
5473             path = path.replace(trimRe, "");
5474             if(!valueCache[path]){
5475                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5476             }
5477             var n = valueCache[path](root);
5478             n = n[0] ? n[0] : n;
5479             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5480             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5481         },
5482
5483         /**
5484          * Selects the value of a node, parsing integers and floats.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @param {Number} defaultValue
5488          * @return {Number}
5489          */
5490         selectNumber : function(path, root, defaultValue){
5491             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5492             return parseFloat(v);
5493         },
5494
5495         /**
5496          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5497          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5498          * @param {String} selector The simple selector to test
5499          * @return {Boolean}
5500          */
5501         is : function(el, ss){
5502             if(typeof el == "string"){
5503                 el = document.getElementById(el);
5504             }
5505             var isArray = (el instanceof Array);
5506             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5507             return isArray ? (result.length == el.length) : (result.length > 0);
5508         },
5509
5510         /**
5511          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5512          * @param {Array} el An array of elements to filter
5513          * @param {String} selector The simple selector to test
5514          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5515          * the selector instead of the ones that match
5516          * @return {Array}
5517          */
5518         filter : function(els, ss, nonMatches){
5519             ss = ss.replace(trimRe, "");
5520             if(!simpleCache[ss]){
5521                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5522             }
5523             var result = simpleCache[ss](els);
5524             return nonMatches ? quickDiff(result, els) : result;
5525         },
5526
5527         /**
5528          * Collection of matching regular expressions and code snippets.
5529          */
5530         matchers : [{
5531                 re: /^\.([\w-]+)/,
5532                 select: 'n = byClassName(n, null, " {1} ");'
5533             }, {
5534                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5535                 select: 'n = byPseudo(n, "{1}", "{2}");'
5536             },{
5537                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5538                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5539             }, {
5540                 re: /^#([\w-]+)/,
5541                 select: 'n = byId(n, null, "{1}");'
5542             },{
5543                 re: /^@([\w-]+)/,
5544                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5545             }
5546         ],
5547
5548         /**
5549          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5550          * 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;.
5551          */
5552         operators : {
5553             "=" : function(a, v){
5554                 return a == v;
5555             },
5556             "!=" : function(a, v){
5557                 return a != v;
5558             },
5559             "^=" : function(a, v){
5560                 return a && a.substr(0, v.length) == v;
5561             },
5562             "$=" : function(a, v){
5563                 return a && a.substr(a.length-v.length) == v;
5564             },
5565             "*=" : function(a, v){
5566                 return a && a.indexOf(v) !== -1;
5567             },
5568             "%=" : function(a, v){
5569                 return (a % v) == 0;
5570             },
5571             "|=" : function(a, v){
5572                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5573             },
5574             "~=" : function(a, v){
5575                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5576             }
5577         },
5578
5579         /**
5580          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5581          * and the argument (if any) supplied in the selector.
5582          */
5583         pseudos : {
5584             "first-child" : function(c){
5585                 var r = [], ri = -1, n;
5586                 for(var i = 0, ci; ci = n = c[i]; i++){
5587                     while((n = n.previousSibling) && n.nodeType != 1);
5588                     if(!n){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "last-child" : function(c){
5596                 var r = [], ri = -1, n;
5597                 for(var i = 0, ci; ci = n = c[i]; i++){
5598                     while((n = n.nextSibling) && n.nodeType != 1);
5599                     if(!n){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "nth-child" : function(c, a) {
5607                 var r = [], ri = -1;
5608                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5609                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5610                 for(var i = 0, n; n = c[i]; i++){
5611                     var pn = n.parentNode;
5612                     if (batch != pn._batch) {
5613                         var j = 0;
5614                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5615                             if(cn.nodeType == 1){
5616                                cn.nodeIndex = ++j;
5617                             }
5618                         }
5619                         pn._batch = batch;
5620                     }
5621                     if (f == 1) {
5622                         if (l == 0 || n.nodeIndex == l){
5623                             r[++ri] = n;
5624                         }
5625                     } else if ((n.nodeIndex + l) % f == 0){
5626                         r[++ri] = n;
5627                     }
5628                 }
5629
5630                 return r;
5631             },
5632
5633             "only-child" : function(c){
5634                 var r = [], ri = -1;;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(!prev(ci) && !next(ci)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "empty" : function(c){
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var cns = ci.childNodes, j = 0, cn, empty = true;
5647                     while(cn = cns[j]){
5648                         ++j;
5649                         if(cn.nodeType == 1 || cn.nodeType == 3){
5650                             empty = false;
5651                             break;
5652                         }
5653                     }
5654                     if(empty){
5655                         r[++ri] = ci;
5656                     }
5657                 }
5658                 return r;
5659             },
5660
5661             "contains" : function(c, v){
5662                 var r = [], ri = -1;
5663                 for(var i = 0, ci; ci = c[i]; i++){
5664                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5665                         r[++ri] = ci;
5666                     }
5667                 }
5668                 return r;
5669             },
5670
5671             "nodeValue" : function(c, v){
5672                 var r = [], ri = -1;
5673                 for(var i = 0, ci; ci = c[i]; i++){
5674                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5675                         r[++ri] = ci;
5676                     }
5677                 }
5678                 return r;
5679             },
5680
5681             "checked" : function(c){
5682                 var r = [], ri = -1;
5683                 for(var i = 0, ci; ci = c[i]; i++){
5684                     if(ci.checked == true){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "not" : function(c, ss){
5692                 return Roo.DomQuery.filter(c, ss, true);
5693             },
5694
5695             "odd" : function(c){
5696                 return this["nth-child"](c, "odd");
5697             },
5698
5699             "even" : function(c){
5700                 return this["nth-child"](c, "even");
5701             },
5702
5703             "nth" : function(c, a){
5704                 return c[a-1] || [];
5705             },
5706
5707             "first" : function(c){
5708                 return c[0] || [];
5709             },
5710
5711             "last" : function(c){
5712                 return c[c.length-1] || [];
5713             },
5714
5715             "has" : function(c, ss){
5716                 var s = Roo.DomQuery.select;
5717                 var r = [], ri = -1;
5718                 for(var i = 0, ci; ci = c[i]; i++){
5719                     if(s(ss, ci).length > 0){
5720                         r[++ri] = ci;
5721                     }
5722                 }
5723                 return r;
5724             },
5725
5726             "next" : function(c, ss){
5727                 var is = Roo.DomQuery.is;
5728                 var r = [], ri = -1;
5729                 for(var i = 0, ci; ci = c[i]; i++){
5730                     var n = next(ci);
5731                     if(n && is(n, ss)){
5732                         r[++ri] = ci;
5733                     }
5734                 }
5735                 return r;
5736             },
5737
5738             "prev" : function(c, ss){
5739                 var is = Roo.DomQuery.is;
5740                 var r = [], ri = -1;
5741                 for(var i = 0, ci; ci = c[i]; i++){
5742                     var n = prev(ci);
5743                     if(n && is(n, ss)){
5744                         r[++ri] = ci;
5745                     }
5746                 }
5747                 return r;
5748             }
5749         }
5750     };
5751 }();
5752
5753 /**
5754  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5755  * @param {String} path The selector/xpath query
5756  * @param {Node} root (optional) The start of the query (defaults to document).
5757  * @return {Array}
5758  * @member Roo
5759  * @method query
5760  */
5761 Roo.query = Roo.DomQuery.select;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772
5773 /**
5774  * @class Roo.util.Observable
5775  * Base class that provides a common interface for publishing events. Subclasses are expected to
5776  * to have a property "events" with all the events defined.<br>
5777  * For example:
5778  * <pre><code>
5779  Employee = function(name){
5780     this.name = name;
5781     this.addEvents({
5782         "fired" : true,
5783         "quit" : true
5784     });
5785  }
5786  Roo.extend(Employee, Roo.util.Observable);
5787 </code></pre>
5788  * @param {Object} config properties to use (incuding events / listeners)
5789  */
5790
5791 Roo.util.Observable = function(cfg){
5792     
5793     cfg = cfg|| {};
5794     this.addEvents(cfg.events || {});
5795     if (cfg.events) {
5796         delete cfg.events; // make sure
5797     }
5798      
5799     Roo.apply(this, cfg);
5800     
5801     if(this.listeners){
5802         this.on(this.listeners);
5803         delete this.listeners;
5804     }
5805 };
5806 Roo.util.Observable.prototype = {
5807     /** 
5808  * @cfg {Object} listeners  list of events and functions to call for this object, 
5809  * For example :
5810  * <pre><code>
5811     listeners :  { 
5812        'click' : function(e) {
5813            ..... 
5814         } ,
5815         .... 
5816     } 
5817   </code></pre>
5818  */
5819     
5820     
5821     /**
5822      * Fires the specified event with the passed parameters (minus the event name).
5823      * @param {String} eventName
5824      * @param {Object...} args Variable number of parameters are passed to handlers
5825      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5826      */
5827     fireEvent : function(){
5828         var ce = this.events[arguments[0].toLowerCase()];
5829         if(typeof ce == "object"){
5830             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5831         }else{
5832             return true;
5833         }
5834     },
5835
5836     // private
5837     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5838
5839     /**
5840      * Appends an event handler to this component
5841      * @param {String}   eventName The type of event to listen for
5842      * @param {Function} handler The method the event invokes
5843      * @param {Object}   scope (optional) The scope in which to execute the handler
5844      * function. The handler function's "this" context.
5845      * @param {Object}   options (optional) An object containing handler configuration
5846      * properties. This may contain any of the following properties:<ul>
5847      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5848      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5849      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5850      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5851      * by the specified number of milliseconds. If the event fires again within that time, the original
5852      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5853      * </ul><br>
5854      * <p>
5855      * <b>Combining Options</b><br>
5856      * Using the options argument, it is possible to combine different types of listeners:<br>
5857      * <br>
5858      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5859                 <pre><code>
5860                 el.on('click', this.onClick, this, {
5861                         single: true,
5862                 delay: 100,
5863                 forumId: 4
5864                 });
5865                 </code></pre>
5866      * <p>
5867      * <b>Attaching multiple handlers in 1 call</b><br>
5868      * The method also allows for a single argument to be passed which is a config object containing properties
5869      * which specify multiple handlers.
5870      * <pre><code>
5871                 el.on({
5872                         'click': {
5873                         fn: this.onClick,
5874                         scope: this,
5875                         delay: 100
5876                 }, 
5877                 'mouseover': {
5878                         fn: this.onMouseOver,
5879                         scope: this
5880                 },
5881                 'mouseout': {
5882                         fn: this.onMouseOut,
5883                         scope: this
5884                 }
5885                 });
5886                 </code></pre>
5887      * <p>
5888      * Or a shorthand syntax which passes the same scope object to all handlers:
5889         <pre><code>
5890                 el.on({
5891                         'click': this.onClick,
5892                 'mouseover': this.onMouseOver,
5893                 'mouseout': this.onMouseOut,
5894                 scope: this
5895                 });
5896                 </code></pre>
5897      */
5898     addListener : function(eventName, fn, scope, o){
5899         if(typeof eventName == "object"){
5900             o = eventName;
5901             for(var e in o){
5902                 if(this.filterOptRe.test(e)){
5903                     continue;
5904                 }
5905                 if(typeof o[e] == "function"){
5906                     // shared options
5907                     this.addListener(e, o[e], o.scope,  o);
5908                 }else{
5909                     // individual options
5910                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5911                 }
5912             }
5913             return;
5914         }
5915         o = (!o || typeof o == "boolean") ? {} : o;
5916         eventName = eventName.toLowerCase();
5917         var ce = this.events[eventName] || true;
5918         if(typeof ce == "boolean"){
5919             ce = new Roo.util.Event(this, eventName);
5920             this.events[eventName] = ce;
5921         }
5922         ce.addListener(fn, scope, o);
5923     },
5924
5925     /**
5926      * Removes a listener
5927      * @param {String}   eventName     The type of event to listen for
5928      * @param {Function} handler        The handler to remove
5929      * @param {Object}   scope  (optional) The scope (this object) for the handler
5930      */
5931     removeListener : function(eventName, fn, scope){
5932         var ce = this.events[eventName.toLowerCase()];
5933         if(typeof ce == "object"){
5934             ce.removeListener(fn, scope);
5935         }
5936     },
5937
5938     /**
5939      * Removes all listeners for this object
5940      */
5941     purgeListeners : function(){
5942         for(var evt in this.events){
5943             if(typeof this.events[evt] == "object"){
5944                  this.events[evt].clearListeners();
5945             }
5946         }
5947     },
5948
5949     relayEvents : function(o, events){
5950         var createHandler = function(ename){
5951             return function(){
5952                  
5953                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5954             };
5955         };
5956         for(var i = 0, len = events.length; i < len; i++){
5957             var ename = events[i];
5958             if(!this.events[ename]){
5959                 this.events[ename] = true;
5960             };
5961             o.on(ename, createHandler(ename), this);
5962         }
5963     },
5964
5965     /**
5966      * Used to define events on this Observable
5967      * @param {Object} object The object with the events defined
5968      */
5969     addEvents : function(o){
5970         if(!this.events){
5971             this.events = {};
5972         }
5973         Roo.applyIf(this.events, o);
5974     },
5975
5976     /**
5977      * Checks to see if this object has any listeners for a specified event
5978      * @param {String} eventName The name of the event to check for
5979      * @return {Boolean} True if the event is being listened for, else false
5980      */
5981     hasListener : function(eventName){
5982         var e = this.events[eventName];
5983         return typeof e == "object" && e.listeners.length > 0;
5984     }
5985 };
5986 /**
5987  * Appends an event handler to this element (shorthand for addListener)
5988  * @param {String}   eventName     The type of event to listen for
5989  * @param {Function} handler        The method the event invokes
5990  * @param {Object}   scope (optional) The scope in which to execute the handler
5991  * function. The handler function's "this" context.
5992  * @param {Object}   options  (optional)
5993  * @method
5994  */
5995 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5996 /**
5997  * Removes a listener (shorthand for removeListener)
5998  * @param {String}   eventName     The type of event to listen for
5999  * @param {Function} handler        The handler to remove
6000  * @param {Object}   scope  (optional) The scope (this object) for the handler
6001  * @method
6002  */
6003 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6004
6005 /**
6006  * Starts capture on the specified Observable. All events will be passed
6007  * to the supplied function with the event name + standard signature of the event
6008  * <b>before</b> the event is fired. If the supplied function returns false,
6009  * the event will not fire.
6010  * @param {Observable} o The Observable to capture
6011  * @param {Function} fn The function to call
6012  * @param {Object} scope (optional) The scope (this object) for the fn
6013  * @static
6014  */
6015 Roo.util.Observable.capture = function(o, fn, scope){
6016     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6017 };
6018
6019 /**
6020  * Removes <b>all</b> added captures from the Observable.
6021  * @param {Observable} o The Observable to release
6022  * @static
6023  */
6024 Roo.util.Observable.releaseCapture = function(o){
6025     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6026 };
6027
6028 (function(){
6029
6030     var createBuffered = function(h, o, scope){
6031         var task = new Roo.util.DelayedTask();
6032         return function(){
6033             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6034         };
6035     };
6036
6037     var createSingle = function(h, e, fn, scope){
6038         return function(){
6039             e.removeListener(fn, scope);
6040             return h.apply(scope, arguments);
6041         };
6042     };
6043
6044     var createDelayed = function(h, o, scope){
6045         return function(){
6046             var args = Array.prototype.slice.call(arguments, 0);
6047             setTimeout(function(){
6048                 h.apply(scope, args);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     Roo.util.Event = function(obj, name){
6054         this.name = name;
6055         this.obj = obj;
6056         this.listeners = [];
6057     };
6058
6059     Roo.util.Event.prototype = {
6060         addListener : function(fn, scope, options){
6061             var o = options || {};
6062             scope = scope || this.obj;
6063             if(!this.isListening(fn, scope)){
6064                 var l = {fn: fn, scope: scope, options: o};
6065                 var h = fn;
6066                 if(o.delay){
6067                     h = createDelayed(h, o, scope);
6068                 }
6069                 if(o.single){
6070                     h = createSingle(h, this, fn, scope);
6071                 }
6072                 if(o.buffer){
6073                     h = createBuffered(h, o, scope);
6074                 }
6075                 l.fireFn = h;
6076                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6077                     this.listeners.push(l);
6078                 }else{
6079                     this.listeners = this.listeners.slice(0);
6080                     this.listeners.push(l);
6081                 }
6082             }
6083         },
6084
6085         findListener : function(fn, scope){
6086             scope = scope || this.obj;
6087             var ls = this.listeners;
6088             for(var i = 0, len = ls.length; i < len; i++){
6089                 var l = ls[i];
6090                 if(l.fn == fn && l.scope == scope){
6091                     return i;
6092                 }
6093             }
6094             return -1;
6095         },
6096
6097         isListening : function(fn, scope){
6098             return this.findListener(fn, scope) != -1;
6099         },
6100
6101         removeListener : function(fn, scope){
6102             var index;
6103             if((index = this.findListener(fn, scope)) != -1){
6104                 if(!this.firing){
6105                     this.listeners.splice(index, 1);
6106                 }else{
6107                     this.listeners = this.listeners.slice(0);
6108                     this.listeners.splice(index, 1);
6109                 }
6110                 return true;
6111             }
6112             return false;
6113         },
6114
6115         clearListeners : function(){
6116             this.listeners = [];
6117         },
6118
6119         fire : function(){
6120             var ls = this.listeners, scope, len = ls.length;
6121             if(len > 0){
6122                 this.firing = true;
6123                 var args = Array.prototype.slice.call(arguments, 0);                
6124                 for(var i = 0; i < len; i++){
6125                     var l = ls[i];
6126                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6127                         this.firing = false;
6128                         return false;
6129                     }
6130                 }
6131                 this.firing = false;
6132             }
6133             return true;
6134         }
6135     };
6136 })();/*
6137  * RooJS Library 
6138  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6139  *
6140  * Licence LGPL 
6141  *
6142  */
6143  
6144 /**
6145  * @class Roo.Document
6146  * @extends Roo.util.Observable
6147  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6148  * 
6149  * @param {Object} config the methods and properties of the 'base' class for the application.
6150  * 
6151  *  Generic Page handler - implement this to start your app..
6152  * 
6153  * eg.
6154  *  MyProject = new Roo.Document({
6155         events : {
6156             'load' : true // your events..
6157         },
6158         listeners : {
6159             'ready' : function() {
6160                 // fired on Roo.onReady()
6161             }
6162         }
6163  * 
6164  */
6165 Roo.Document = function(cfg) {
6166      
6167     this.addEvents({ 
6168         'ready' : true
6169     });
6170     Roo.util.Observable.call(this,cfg);
6171     
6172     var _this = this;
6173     
6174     Roo.onReady(function() {
6175         _this.fireEvent('ready');
6176     },null,false);
6177     
6178     
6179 }
6180
6181 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6182  * Based on:
6183  * Ext JS Library 1.1.1
6184  * Copyright(c) 2006-2007, Ext JS, LLC.
6185  *
6186  * Originally Released Under LGPL - original licence link has changed is not relivant.
6187  *
6188  * Fork - LGPL
6189  * <script type="text/javascript">
6190  */
6191
6192 /**
6193  * @class Roo.EventManager
6194  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6195  * several useful events directly.
6196  * See {@link Roo.EventObject} for more details on normalized event objects.
6197  * @singleton
6198  */
6199 Roo.EventManager = function(){
6200     var docReadyEvent, docReadyProcId, docReadyState = false;
6201     var resizeEvent, resizeTask, textEvent, textSize;
6202     var E = Roo.lib.Event;
6203     var D = Roo.lib.Dom;
6204
6205     
6206     
6207
6208     var fireDocReady = function(){
6209         if(!docReadyState){
6210             docReadyState = true;
6211             Roo.isReady = true;
6212             if(docReadyProcId){
6213                 clearInterval(docReadyProcId);
6214             }
6215             if(Roo.isGecko || Roo.isOpera) {
6216                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6217             }
6218             if(Roo.isIE){
6219                 var defer = document.getElementById("ie-deferred-loader");
6220                 if(defer){
6221                     defer.onreadystatechange = null;
6222                     defer.parentNode.removeChild(defer);
6223                 }
6224             }
6225             if(docReadyEvent){
6226                 docReadyEvent.fire();
6227                 docReadyEvent.clearListeners();
6228             }
6229         }
6230     };
6231     
6232     var initDocReady = function(){
6233         docReadyEvent = new Roo.util.Event();
6234         if(Roo.isGecko || Roo.isOpera) {
6235             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6236         }else if(Roo.isIE){
6237             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6238             var defer = document.getElementById("ie-deferred-loader");
6239             defer.onreadystatechange = function(){
6240                 if(this.readyState == "complete"){
6241                     fireDocReady();
6242                 }
6243             };
6244         }else if(Roo.isSafari){ 
6245             docReadyProcId = setInterval(function(){
6246                 var rs = document.readyState;
6247                 if(rs == "complete") {
6248                     fireDocReady();     
6249                  }
6250             }, 10);
6251         }
6252         // no matter what, make sure it fires on load
6253         E.on(window, "load", fireDocReady);
6254     };
6255
6256     var createBuffered = function(h, o){
6257         var task = new Roo.util.DelayedTask(h);
6258         return function(e){
6259             // create new event object impl so new events don't wipe out properties
6260             e = new Roo.EventObjectImpl(e);
6261             task.delay(o.buffer, h, null, [e]);
6262         };
6263     };
6264
6265     var createSingle = function(h, el, ename, fn){
6266         return function(e){
6267             Roo.EventManager.removeListener(el, ename, fn);
6268             h(e);
6269         };
6270     };
6271
6272     var createDelayed = function(h, o){
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             setTimeout(function(){
6277                 h(e);
6278             }, o.delay || 10);
6279         };
6280     };
6281     var transitionEndVal = false;
6282     
6283     var transitionEnd = function()
6284     {
6285         if (transitionEndVal) {
6286             return transitionEndVal;
6287         }
6288         var el = document.createElement('div');
6289
6290         var transEndEventNames = {
6291             WebkitTransition : 'webkitTransitionEnd',
6292             MozTransition    : 'transitionend',
6293             OTransition      : 'oTransitionEnd otransitionend',
6294             transition       : 'transitionend'
6295         };
6296     
6297         for (var name in transEndEventNames) {
6298             if (el.style[name] !== undefined) {
6299                 transitionEndVal = transEndEventNames[name];
6300                 return  transitionEndVal ;
6301             }
6302         }
6303     }
6304     
6305   
6306
6307     var listen = function(element, ename, opt, fn, scope)
6308     {
6309         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6310         fn = fn || o.fn; scope = scope || o.scope;
6311         var el = Roo.getDom(element);
6312         
6313         
6314         if(!el){
6315             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6316         }
6317         
6318         if (ename == 'transitionend') {
6319             ename = transitionEnd();
6320         }
6321         var h = function(e){
6322             e = Roo.EventObject.setEvent(e);
6323             var t;
6324             if(o.delegate){
6325                 t = e.getTarget(o.delegate, el);
6326                 if(!t){
6327                     return;
6328                 }
6329             }else{
6330                 t = e.target;
6331             }
6332             if(o.stopEvent === true){
6333                 e.stopEvent();
6334             }
6335             if(o.preventDefault === true){
6336                e.preventDefault();
6337             }
6338             if(o.stopPropagation === true){
6339                 e.stopPropagation();
6340             }
6341
6342             if(o.normalized === false){
6343                 e = e.browserEvent;
6344             }
6345
6346             fn.call(scope || el, e, t, o);
6347         };
6348         if(o.delay){
6349             h = createDelayed(h, o);
6350         }
6351         if(o.single){
6352             h = createSingle(h, el, ename, fn);
6353         }
6354         if(o.buffer){
6355             h = createBuffered(h, o);
6356         }
6357         
6358         fn._handlers = fn._handlers || [];
6359         
6360         
6361         fn._handlers.push([Roo.id(el), ename, h]);
6362         
6363         
6364          
6365         E.on(el, ename, h); // this adds the actuall listener to the object..
6366         
6367         
6368         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6369             el.addEventListener("DOMMouseScroll", h, false);
6370             E.on(window, 'unload', function(){
6371                 el.removeEventListener("DOMMouseScroll", h, false);
6372             });
6373         }
6374         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6375             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6376         }
6377         return h;
6378     };
6379
6380     var stopListening = function(el, ename, fn){
6381         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6382         if(hds){
6383             for(var i = 0, len = hds.length; i < len; i++){
6384                 var h = hds[i];
6385                 if(h[0] == id && h[1] == ename){
6386                     hd = h[2];
6387                     hds.splice(i, 1);
6388                     break;
6389                 }
6390             }
6391         }
6392         E.un(el, ename, hd);
6393         el = Roo.getDom(el);
6394         if(ename == "mousewheel" && el.addEventListener){
6395             el.removeEventListener("DOMMouseScroll", hd, false);
6396         }
6397         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6398             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6399         }
6400     };
6401
6402     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6403     
6404     var pub = {
6405         
6406         
6407         /** 
6408          * Fix for doc tools
6409          * @scope Roo.EventManager
6410          */
6411         
6412         
6413         /** 
6414          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6415          * object with a Roo.EventObject
6416          * @param {Function} fn        The method the event invokes
6417          * @param {Object}   scope    An object that becomes the scope of the handler
6418          * @param {boolean}  override If true, the obj passed in becomes
6419          *                             the execution scope of the listener
6420          * @return {Function} The wrapped function
6421          * @deprecated
6422          */
6423         wrap : function(fn, scope, override){
6424             return function(e){
6425                 Roo.EventObject.setEvent(e);
6426                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6427             };
6428         },
6429         
6430         /**
6431      * Appends an event handler to an element (shorthand for addListener)
6432      * @param {String/HTMLElement}   element        The html element or id to assign the
6433      * @param {String}   eventName The type of event to listen for
6434      * @param {Function} handler The method the event invokes
6435      * @param {Object}   scope (optional) The scope in which to execute the handler
6436      * function. The handler function's "this" context.
6437      * @param {Object}   options (optional) An object containing handler configuration
6438      * properties. This may contain any of the following properties:<ul>
6439      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6440      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6441      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6442      * <li>preventDefault {Boolean} True to prevent the default action</li>
6443      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6444      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6445      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6446      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6447      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6448      * by the specified number of milliseconds. If the event fires again within that time, the original
6449      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6450      * </ul><br>
6451      * <p>
6452      * <b>Combining Options</b><br>
6453      * Using the options argument, it is possible to combine different types of listeners:<br>
6454      * <br>
6455      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6456      * Code:<pre><code>
6457 el.on('click', this.onClick, this, {
6458     single: true,
6459     delay: 100,
6460     stopEvent : true,
6461     forumId: 4
6462 });</code></pre>
6463      * <p>
6464      * <b>Attaching multiple handlers in 1 call</b><br>
6465       * The method also allows for a single argument to be passed which is a config object containing properties
6466      * which specify multiple handlers.
6467      * <p>
6468      * Code:<pre><code>
6469 el.on({
6470     'click' : {
6471         fn: this.onClick
6472         scope: this,
6473         delay: 100
6474     },
6475     'mouseover' : {
6476         fn: this.onMouseOver
6477         scope: this
6478     },
6479     'mouseout' : {
6480         fn: this.onMouseOut
6481         scope: this
6482     }
6483 });</code></pre>
6484      * <p>
6485      * Or a shorthand syntax:<br>
6486      * Code:<pre><code>
6487 el.on({
6488     'click' : this.onClick,
6489     'mouseover' : this.onMouseOver,
6490     'mouseout' : this.onMouseOut
6491     scope: this
6492 });</code></pre>
6493      */
6494         addListener : function(element, eventName, fn, scope, options){
6495             if(typeof eventName == "object"){
6496                 var o = eventName;
6497                 for(var e in o){
6498                     if(propRe.test(e)){
6499                         continue;
6500                     }
6501                     if(typeof o[e] == "function"){
6502                         // shared options
6503                         listen(element, e, o, o[e], o.scope);
6504                     }else{
6505                         // individual options
6506                         listen(element, e, o[e]);
6507                     }
6508                 }
6509                 return;
6510             }
6511             return listen(element, eventName, options, fn, scope);
6512         },
6513         
6514         /**
6515          * Removes an event handler
6516          *
6517          * @param {String/HTMLElement}   element        The id or html element to remove the 
6518          *                             event from
6519          * @param {String}   eventName     The type of event
6520          * @param {Function} fn
6521          * @return {Boolean} True if a listener was actually removed
6522          */
6523         removeListener : function(element, eventName, fn){
6524             return stopListening(element, eventName, fn);
6525         },
6526         
6527         /**
6528          * Fires when the document is ready (before onload and before images are loaded). Can be 
6529          * accessed shorthanded Roo.onReady().
6530          * @param {Function} fn        The method the event invokes
6531          * @param {Object}   scope    An  object that becomes the scope of the handler
6532          * @param {boolean}  options
6533          */
6534         onDocumentReady : function(fn, scope, options){
6535             if(docReadyState){ // if it already fired
6536                 docReadyEvent.addListener(fn, scope, options);
6537                 docReadyEvent.fire();
6538                 docReadyEvent.clearListeners();
6539                 return;
6540             }
6541             if(!docReadyEvent){
6542                 initDocReady();
6543             }
6544             docReadyEvent.addListener(fn, scope, options);
6545         },
6546         
6547         /**
6548          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6549          * @param {Function} fn        The method the event invokes
6550          * @param {Object}   scope    An object that becomes the scope of the handler
6551          * @param {boolean}  options
6552          */
6553         onWindowResize : function(fn, scope, options){
6554             if(!resizeEvent){
6555                 resizeEvent = new Roo.util.Event();
6556                 resizeTask = new Roo.util.DelayedTask(function(){
6557                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558                 });
6559                 E.on(window, "resize", function(){
6560                     if(Roo.isIE){
6561                         resizeTask.delay(50);
6562                     }else{
6563                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6564                     }
6565                 });
6566             }
6567             resizeEvent.addListener(fn, scope, options);
6568         },
6569
6570         /**
6571          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6572          * @param {Function} fn        The method the event invokes
6573          * @param {Object}   scope    An object that becomes the scope of the handler
6574          * @param {boolean}  options
6575          */
6576         onTextResize : function(fn, scope, options){
6577             if(!textEvent){
6578                 textEvent = new Roo.util.Event();
6579                 var textEl = new Roo.Element(document.createElement('div'));
6580                 textEl.dom.className = 'x-text-resize';
6581                 textEl.dom.innerHTML = 'X';
6582                 textEl.appendTo(document.body);
6583                 textSize = textEl.dom.offsetHeight;
6584                 setInterval(function(){
6585                     if(textEl.dom.offsetHeight != textSize){
6586                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6587                     }
6588                 }, this.textResizeInterval);
6589             }
6590             textEvent.addListener(fn, scope, options);
6591         },
6592
6593         /**
6594          * Removes the passed window resize listener.
6595          * @param {Function} fn        The method the event invokes
6596          * @param {Object}   scope    The scope of handler
6597          */
6598         removeResizeListener : function(fn, scope){
6599             if(resizeEvent){
6600                 resizeEvent.removeListener(fn, scope);
6601             }
6602         },
6603
6604         // private
6605         fireResize : function(){
6606             if(resizeEvent){
6607                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6608             }   
6609         },
6610         /**
6611          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6612          */
6613         ieDeferSrc : false,
6614         /**
6615          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6616          */
6617         textResizeInterval : 50
6618     };
6619     
6620     /**
6621      * Fix for doc tools
6622      * @scopeAlias pub=Roo.EventManager
6623      */
6624     
6625      /**
6626      * Appends an event handler to an element (shorthand for addListener)
6627      * @param {String/HTMLElement}   element        The html element or id to assign the
6628      * @param {String}   eventName The type of event to listen for
6629      * @param {Function} handler The method the event invokes
6630      * @param {Object}   scope (optional) The scope in which to execute the handler
6631      * function. The handler function's "this" context.
6632      * @param {Object}   options (optional) An object containing handler configuration
6633      * properties. This may contain any of the following properties:<ul>
6634      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6635      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6636      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6637      * <li>preventDefault {Boolean} True to prevent the default action</li>
6638      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6639      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6640      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6641      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6642      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6643      * by the specified number of milliseconds. If the event fires again within that time, the original
6644      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6645      * </ul><br>
6646      * <p>
6647      * <b>Combining Options</b><br>
6648      * Using the options argument, it is possible to combine different types of listeners:<br>
6649      * <br>
6650      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6651      * Code:<pre><code>
6652 el.on('click', this.onClick, this, {
6653     single: true,
6654     delay: 100,
6655     stopEvent : true,
6656     forumId: 4
6657 });</code></pre>
6658      * <p>
6659      * <b>Attaching multiple handlers in 1 call</b><br>
6660       * The method also allows for a single argument to be passed which is a config object containing properties
6661      * which specify multiple handlers.
6662      * <p>
6663      * Code:<pre><code>
6664 el.on({
6665     'click' : {
6666         fn: this.onClick
6667         scope: this,
6668         delay: 100
6669     },
6670     'mouseover' : {
6671         fn: this.onMouseOver
6672         scope: this
6673     },
6674     'mouseout' : {
6675         fn: this.onMouseOut
6676         scope: this
6677     }
6678 });</code></pre>
6679      * <p>
6680      * Or a shorthand syntax:<br>
6681      * Code:<pre><code>
6682 el.on({
6683     'click' : this.onClick,
6684     'mouseover' : this.onMouseOver,
6685     'mouseout' : this.onMouseOut
6686     scope: this
6687 });</code></pre>
6688      */
6689     pub.on = pub.addListener;
6690     pub.un = pub.removeListener;
6691
6692     pub.stoppedMouseDownEvent = new Roo.util.Event();
6693     return pub;
6694 }();
6695 /**
6696   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6697   * @param {Function} fn        The method the event invokes
6698   * @param {Object}   scope    An  object that becomes the scope of the handler
6699   * @param {boolean}  override If true, the obj passed in becomes
6700   *                             the execution scope of the listener
6701   * @member Roo
6702   * @method onReady
6703  */
6704 Roo.onReady = Roo.EventManager.onDocumentReady;
6705
6706 Roo.onReady(function(){
6707     var bd = Roo.get(document.body);
6708     if(!bd){ return; }
6709
6710     var cls = [
6711             Roo.isIE ? "roo-ie"
6712             : Roo.isIE11 ? "roo-ie11"
6713             : Roo.isEdge ? "roo-edge"
6714             : Roo.isGecko ? "roo-gecko"
6715             : Roo.isOpera ? "roo-opera"
6716             : Roo.isSafari ? "roo-safari" : ""];
6717
6718     if(Roo.isMac){
6719         cls.push("roo-mac");
6720     }
6721     if(Roo.isLinux){
6722         cls.push("roo-linux");
6723     }
6724     if(Roo.isIOS){
6725         cls.push("roo-ios");
6726     }
6727     if(Roo.isTouch){
6728         cls.push("roo-touch");
6729     }
6730     if(Roo.isBorderBox){
6731         cls.push('roo-border-box');
6732     }
6733     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6734         var p = bd.dom.parentNode;
6735         if(p){
6736             p.className += ' roo-strict';
6737         }
6738     }
6739     bd.addClass(cls.join(' '));
6740 });
6741
6742 /**
6743  * @class Roo.EventObject
6744  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6745  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6746  * Example:
6747  * <pre><code>
6748  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6749     e.preventDefault();
6750     var target = e.getTarget();
6751     ...
6752  }
6753  var myDiv = Roo.get("myDiv");
6754  myDiv.on("click", handleClick);
6755  //or
6756  Roo.EventManager.on("myDiv", 'click', handleClick);
6757  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6758  </code></pre>
6759  * @singleton
6760  */
6761 Roo.EventObject = function(){
6762     
6763     var E = Roo.lib.Event;
6764     
6765     // safari keypress events for special keys return bad keycodes
6766     var safariKeys = {
6767         63234 : 37, // left
6768         63235 : 39, // right
6769         63232 : 38, // up
6770         63233 : 40, // down
6771         63276 : 33, // page up
6772         63277 : 34, // page down
6773         63272 : 46, // delete
6774         63273 : 36, // home
6775         63275 : 35  // end
6776     };
6777
6778     // normalize button clicks
6779     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6780                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6781
6782     Roo.EventObjectImpl = function(e){
6783         if(e){
6784             this.setEvent(e.browserEvent || e);
6785         }
6786     };
6787     Roo.EventObjectImpl.prototype = {
6788         /**
6789          * Used to fix doc tools.
6790          * @scope Roo.EventObject.prototype
6791          */
6792             
6793
6794         
6795         
6796         /** The normal browser event */
6797         browserEvent : null,
6798         /** The button pressed in a mouse event */
6799         button : -1,
6800         /** True if the shift key was down during the event */
6801         shiftKey : false,
6802         /** True if the control key was down during the event */
6803         ctrlKey : false,
6804         /** True if the alt key was down during the event */
6805         altKey : false,
6806
6807         /** Key constant 
6808         * @type Number */
6809         BACKSPACE : 8,
6810         /** Key constant 
6811         * @type Number */
6812         TAB : 9,
6813         /** Key constant 
6814         * @type Number */
6815         RETURN : 13,
6816         /** Key constant 
6817         * @type Number */
6818         ENTER : 13,
6819         /** Key constant 
6820         * @type Number */
6821         SHIFT : 16,
6822         /** Key constant 
6823         * @type Number */
6824         CONTROL : 17,
6825         /** Key constant 
6826         * @type Number */
6827         ESC : 27,
6828         /** Key constant 
6829         * @type Number */
6830         SPACE : 32,
6831         /** Key constant 
6832         * @type Number */
6833         PAGEUP : 33,
6834         /** Key constant 
6835         * @type Number */
6836         PAGEDOWN : 34,
6837         /** Key constant 
6838         * @type Number */
6839         END : 35,
6840         /** Key constant 
6841         * @type Number */
6842         HOME : 36,
6843         /** Key constant 
6844         * @type Number */
6845         LEFT : 37,
6846         /** Key constant 
6847         * @type Number */
6848         UP : 38,
6849         /** Key constant 
6850         * @type Number */
6851         RIGHT : 39,
6852         /** Key constant 
6853         * @type Number */
6854         DOWN : 40,
6855         /** Key constant 
6856         * @type Number */
6857         DELETE : 46,
6858         /** Key constant 
6859         * @type Number */
6860         F5 : 116,
6861
6862            /** @private */
6863         setEvent : function(e){
6864             if(e == this || (e && e.browserEvent)){ // already wrapped
6865                 return e;
6866             }
6867             this.browserEvent = e;
6868             if(e){
6869                 // normalize buttons
6870                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6871                 if(e.type == 'click' && this.button == -1){
6872                     this.button = 0;
6873                 }
6874                 this.type = e.type;
6875                 this.shiftKey = e.shiftKey;
6876                 // mac metaKey behaves like ctrlKey
6877                 this.ctrlKey = e.ctrlKey || e.metaKey;
6878                 this.altKey = e.altKey;
6879                 // in getKey these will be normalized for the mac
6880                 this.keyCode = e.keyCode;
6881                 // keyup warnings on firefox.
6882                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6883                 // cache the target for the delayed and or buffered events
6884                 this.target = E.getTarget(e);
6885                 // same for XY
6886                 this.xy = E.getXY(e);
6887             }else{
6888                 this.button = -1;
6889                 this.shiftKey = false;
6890                 this.ctrlKey = false;
6891                 this.altKey = false;
6892                 this.keyCode = 0;
6893                 this.charCode =0;
6894                 this.target = null;
6895                 this.xy = [0, 0];
6896             }
6897             return this;
6898         },
6899
6900         /**
6901          * Stop the event (preventDefault and stopPropagation)
6902          */
6903         stopEvent : function(){
6904             if(this.browserEvent){
6905                 if(this.browserEvent.type == 'mousedown'){
6906                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6907                 }
6908                 E.stopEvent(this.browserEvent);
6909             }
6910         },
6911
6912         /**
6913          * Prevents the browsers default handling of the event.
6914          */
6915         preventDefault : function(){
6916             if(this.browserEvent){
6917                 E.preventDefault(this.browserEvent);
6918             }
6919         },
6920
6921         /** @private */
6922         isNavKeyPress : function(){
6923             var k = this.keyCode;
6924             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6925             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6926         },
6927
6928         isSpecialKey : function(){
6929             var k = this.keyCode;
6930             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6931             (k == 16) || (k == 17) ||
6932             (k >= 18 && k <= 20) ||
6933             (k >= 33 && k <= 35) ||
6934             (k >= 36 && k <= 39) ||
6935             (k >= 44 && k <= 45);
6936         },
6937         /**
6938          * Cancels bubbling of the event.
6939          */
6940         stopPropagation : function(){
6941             if(this.browserEvent){
6942                 if(this.type == 'mousedown'){
6943                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6944                 }
6945                 E.stopPropagation(this.browserEvent);
6946             }
6947         },
6948
6949         /**
6950          * Gets the key code for the event.
6951          * @return {Number}
6952          */
6953         getCharCode : function(){
6954             return this.charCode || this.keyCode;
6955         },
6956
6957         /**
6958          * Returns a normalized keyCode for the event.
6959          * @return {Number} The key code
6960          */
6961         getKey : function(){
6962             var k = this.keyCode || this.charCode;
6963             return Roo.isSafari ? (safariKeys[k] || k) : k;
6964         },
6965
6966         /**
6967          * Gets the x coordinate of the event.
6968          * @return {Number}
6969          */
6970         getPageX : function(){
6971             return this.xy[0];
6972         },
6973
6974         /**
6975          * Gets the y coordinate of the event.
6976          * @return {Number}
6977          */
6978         getPageY : function(){
6979             return this.xy[1];
6980         },
6981
6982         /**
6983          * Gets the time of the event.
6984          * @return {Number}
6985          */
6986         getTime : function(){
6987             if(this.browserEvent){
6988                 return E.getTime(this.browserEvent);
6989             }
6990             return null;
6991         },
6992
6993         /**
6994          * Gets the page coordinates of the event.
6995          * @return {Array} The xy values like [x, y]
6996          */
6997         getXY : function(){
6998             return this.xy;
6999         },
7000
7001         /**
7002          * Gets the target for the event.
7003          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7004          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7005                 search as a number or element (defaults to 10 || document.body)
7006          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7007          * @return {HTMLelement}
7008          */
7009         getTarget : function(selector, maxDepth, returnEl){
7010             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7011         },
7012         /**
7013          * Gets the related target.
7014          * @return {HTMLElement}
7015          */
7016         getRelatedTarget : function(){
7017             if(this.browserEvent){
7018                 return E.getRelatedTarget(this.browserEvent);
7019             }
7020             return null;
7021         },
7022
7023         /**
7024          * Normalizes mouse wheel delta across browsers
7025          * @return {Number} The delta
7026          */
7027         getWheelDelta : function(){
7028             var e = this.browserEvent;
7029             var delta = 0;
7030             if(e.wheelDelta){ /* IE/Opera. */
7031                 delta = e.wheelDelta/120;
7032             }else if(e.detail){ /* Mozilla case. */
7033                 delta = -e.detail/3;
7034             }
7035             return delta;
7036         },
7037
7038         /**
7039          * Returns true if the control, meta, shift or alt key was pressed during this event.
7040          * @return {Boolean}
7041          */
7042         hasModifier : function(){
7043             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7044         },
7045
7046         /**
7047          * Returns true if the target of this event equals el or is a child of el
7048          * @param {String/HTMLElement/Element} el
7049          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7050          * @return {Boolean}
7051          */
7052         within : function(el, related){
7053             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7054             return t && Roo.fly(el).contains(t);
7055         },
7056
7057         getPoint : function(){
7058             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7059         }
7060     };
7061
7062     return new Roo.EventObjectImpl();
7063 }();
7064             
7065     /*
7066  * Based on:
7067  * Ext JS Library 1.1.1
7068  * Copyright(c) 2006-2007, Ext JS, LLC.
7069  *
7070  * Originally Released Under LGPL - original licence link has changed is not relivant.
7071  *
7072  * Fork - LGPL
7073  * <script type="text/javascript">
7074  */
7075
7076  
7077 // was in Composite Element!??!?!
7078  
7079 (function(){
7080     var D = Roo.lib.Dom;
7081     var E = Roo.lib.Event;
7082     var A = Roo.lib.Anim;
7083
7084     // local style camelizing for speed
7085     var propCache = {};
7086     var camelRe = /(-[a-z])/gi;
7087     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7088     var view = document.defaultView;
7089
7090 /**
7091  * @class Roo.Element
7092  * Represents an Element in the DOM.<br><br>
7093  * Usage:<br>
7094 <pre><code>
7095 var el = Roo.get("my-div");
7096
7097 // or with getEl
7098 var el = getEl("my-div");
7099
7100 // or with a DOM element
7101 var el = Roo.get(myDivElement);
7102 </code></pre>
7103  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7104  * each call instead of constructing a new one.<br><br>
7105  * <b>Animations</b><br />
7106  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7107  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7108 <pre>
7109 Option    Default   Description
7110 --------- --------  ---------------------------------------------
7111 duration  .35       The duration of the animation in seconds
7112 easing    easeOut   The YUI easing method
7113 callback  none      A function to execute when the anim completes
7114 scope     this      The scope (this) of the callback function
7115 </pre>
7116 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7117 * manipulate the animation. Here's an example:
7118 <pre><code>
7119 var el = Roo.get("my-div");
7120
7121 // no animation
7122 el.setWidth(100);
7123
7124 // default animation
7125 el.setWidth(100, true);
7126
7127 // animation with some options set
7128 el.setWidth(100, {
7129     duration: 1,
7130     callback: this.foo,
7131     scope: this
7132 });
7133
7134 // using the "anim" property to get the Anim object
7135 var opt = {
7136     duration: 1,
7137     callback: this.foo,
7138     scope: this
7139 };
7140 el.setWidth(100, opt);
7141 ...
7142 if(opt.anim.isAnimated()){
7143     opt.anim.stop();
7144 }
7145 </code></pre>
7146 * <b> Composite (Collections of) Elements</b><br />
7147  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7148  * @constructor Create a new Element directly.
7149  * @param {String/HTMLElement} element
7150  * @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).
7151  */
7152     Roo.Element = function(element, forceNew)
7153     {
7154         var dom = typeof element == "string" ?
7155                 document.getElementById(element) : element;
7156         if(!dom){ // invalid id/element
7157             return null;
7158         }
7159         var id = dom.id;
7160         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7161             return Roo.Element.cache[id];
7162         }
7163
7164         /**
7165          * The DOM element
7166          * @type HTMLElement
7167          */
7168         this.dom = dom;
7169
7170         /**
7171          * The DOM element ID
7172          * @type String
7173          */
7174         this.id = id || Roo.id(dom);
7175         
7176         this.listeners = {};
7177     };
7178
7179     var El = Roo.Element;
7180
7181     El.prototype = {
7182         /**
7183          * The element's default display mode  (defaults to "") 
7184          * @type String
7185          */
7186         originalDisplay : "",
7187
7188         
7189         // note this is overridden in BS version..
7190         visibilityMode : 1, 
7191         /**
7192          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7193          * @type String
7194          */
7195         defaultUnit : "px",
7196         
7197         /**
7198          * Sets the element's visibility mode. When setVisible() is called it
7199          * will use this to determine whether to set the visibility or the display property.
7200          * @param visMode Element.VISIBILITY or Element.DISPLAY
7201          * @return {Roo.Element} this
7202          */
7203         setVisibilityMode : function(visMode){
7204             this.visibilityMode = visMode;
7205             return this;
7206         },
7207         /**
7208          * Convenience method for setVisibilityMode(Element.DISPLAY)
7209          * @param {String} display (optional) What to set display to when visible
7210          * @return {Roo.Element} this
7211          */
7212         enableDisplayMode : function(display){
7213             this.setVisibilityMode(El.DISPLAY);
7214             if(typeof display != "undefined") { this.originalDisplay = display; }
7215             return this;
7216         },
7217
7218         /**
7219          * 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)
7220          * @param {String} selector The simple selector to test
7221          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7222                 search as a number or element (defaults to 10 || document.body)
7223          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7224          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7225          */
7226         findParent : function(simpleSelector, maxDepth, returnEl){
7227             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7228             maxDepth = maxDepth || 50;
7229             if(typeof maxDepth != "number"){
7230                 stopEl = Roo.getDom(maxDepth);
7231                 maxDepth = 10;
7232             }
7233             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7234                 if(dq.is(p, simpleSelector)){
7235                     return returnEl ? Roo.get(p) : p;
7236                 }
7237                 depth++;
7238                 p = p.parentNode;
7239             }
7240             return null;
7241         },
7242
7243
7244         /**
7245          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7246          * @param {String} selector The simple selector to test
7247          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7248                 search as a number or element (defaults to 10 || document.body)
7249          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7250          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7251          */
7252         findParentNode : function(simpleSelector, maxDepth, returnEl){
7253             var p = Roo.fly(this.dom.parentNode, '_internal');
7254             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7255         },
7256         
7257         /**
7258          * Looks at  the scrollable parent element
7259          */
7260         findScrollableParent : function()
7261         {
7262             var overflowRegex = /(auto|scroll)/;
7263             
7264             if(this.getStyle('position') === 'fixed'){
7265                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7266             }
7267             
7268             var excludeStaticParent = this.getStyle('position') === "absolute";
7269             
7270             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7271                 
7272                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7273                     continue;
7274                 }
7275                 
7276                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7277                     return parent;
7278                 }
7279                 
7280                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7281                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7282                 }
7283             }
7284             
7285             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7286         },
7287
7288         /**
7289          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7290          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7291          * @param {String} selector The simple selector to test
7292          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7293                 search as a number or element (defaults to 10 || document.body)
7294          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7295          */
7296         up : function(simpleSelector, maxDepth){
7297             return this.findParentNode(simpleSelector, maxDepth, true);
7298         },
7299
7300
7301
7302         /**
7303          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7304          * @param {String} selector The simple selector to test
7305          * @return {Boolean} True if this element matches the selector, else false
7306          */
7307         is : function(simpleSelector){
7308             return Roo.DomQuery.is(this.dom, simpleSelector);
7309         },
7310
7311         /**
7312          * Perform animation on this element.
7313          * @param {Object} args The YUI animation control args
7314          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7315          * @param {Function} onComplete (optional) Function to call when animation completes
7316          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7317          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7318          * @return {Roo.Element} this
7319          */
7320         animate : function(args, duration, onComplete, easing, animType){
7321             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7322             return this;
7323         },
7324
7325         /*
7326          * @private Internal animation call
7327          */
7328         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7329             animType = animType || 'run';
7330             opt = opt || {};
7331             var anim = Roo.lib.Anim[animType](
7332                 this.dom, args,
7333                 (opt.duration || defaultDur) || .35,
7334                 (opt.easing || defaultEase) || 'easeOut',
7335                 function(){
7336                     Roo.callback(cb, this);
7337                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7338                 },
7339                 this
7340             );
7341             opt.anim = anim;
7342             return anim;
7343         },
7344
7345         // private legacy anim prep
7346         preanim : function(a, i){
7347             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7348         },
7349
7350         /**
7351          * Removes worthless text nodes
7352          * @param {Boolean} forceReclean (optional) By default the element
7353          * keeps track if it has been cleaned already so
7354          * you can call this over and over. However, if you update the element and
7355          * need to force a reclean, you can pass true.
7356          */
7357         clean : function(forceReclean){
7358             if(this.isCleaned && forceReclean !== true){
7359                 return this;
7360             }
7361             var ns = /\S/;
7362             var d = this.dom, n = d.firstChild, ni = -1;
7363             while(n){
7364                 var nx = n.nextSibling;
7365                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7366                     d.removeChild(n);
7367                 }else{
7368                     n.nodeIndex = ++ni;
7369                 }
7370                 n = nx;
7371             }
7372             this.isCleaned = true;
7373             return this;
7374         },
7375
7376         // private
7377         calcOffsetsTo : function(el){
7378             el = Roo.get(el);
7379             var d = el.dom;
7380             var restorePos = false;
7381             if(el.getStyle('position') == 'static'){
7382                 el.position('relative');
7383                 restorePos = true;
7384             }
7385             var x = 0, y =0;
7386             var op = this.dom;
7387             while(op && op != d && op.tagName != 'HTML'){
7388                 x+= op.offsetLeft;
7389                 y+= op.offsetTop;
7390                 op = op.offsetParent;
7391             }
7392             if(restorePos){
7393                 el.position('static');
7394             }
7395             return [x, y];
7396         },
7397
7398         /**
7399          * Scrolls this element into view within the passed container.
7400          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7401          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7402          * @return {Roo.Element} this
7403          */
7404         scrollIntoView : function(container, hscroll){
7405             var c = Roo.getDom(container) || document.body;
7406             var el = this.dom;
7407
7408             var o = this.calcOffsetsTo(c),
7409                 l = o[0],
7410                 t = o[1],
7411                 b = t+el.offsetHeight,
7412                 r = l+el.offsetWidth;
7413
7414             var ch = c.clientHeight;
7415             var ct = parseInt(c.scrollTop, 10);
7416             var cl = parseInt(c.scrollLeft, 10);
7417             var cb = ct + ch;
7418             var cr = cl + c.clientWidth;
7419
7420             if(t < ct){
7421                 c.scrollTop = t;
7422             }else if(b > cb){
7423                 c.scrollTop = b-ch;
7424             }
7425
7426             if(hscroll !== false){
7427                 if(l < cl){
7428                     c.scrollLeft = l;
7429                 }else if(r > cr){
7430                     c.scrollLeft = r-c.clientWidth;
7431                 }
7432             }
7433             return this;
7434         },
7435
7436         // private
7437         scrollChildIntoView : function(child, hscroll){
7438             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7439         },
7440
7441         /**
7442          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7443          * the new height may not be available immediately.
7444          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7445          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7446          * @param {Function} onComplete (optional) Function to call when animation completes
7447          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7448          * @return {Roo.Element} this
7449          */
7450         autoHeight : function(animate, duration, onComplete, easing){
7451             var oldHeight = this.getHeight();
7452             this.clip();
7453             this.setHeight(1); // force clipping
7454             setTimeout(function(){
7455                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7456                 if(!animate){
7457                     this.setHeight(height);
7458                     this.unclip();
7459                     if(typeof onComplete == "function"){
7460                         onComplete();
7461                     }
7462                 }else{
7463                     this.setHeight(oldHeight); // restore original height
7464                     this.setHeight(height, animate, duration, function(){
7465                         this.unclip();
7466                         if(typeof onComplete == "function") { onComplete(); }
7467                     }.createDelegate(this), easing);
7468                 }
7469             }.createDelegate(this), 0);
7470             return this;
7471         },
7472
7473         /**
7474          * Returns true if this element is an ancestor of the passed element
7475          * @param {HTMLElement/String} el The element to check
7476          * @return {Boolean} True if this element is an ancestor of el, else false
7477          */
7478         contains : function(el){
7479             if(!el){return false;}
7480             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7481         },
7482
7483         /**
7484          * Checks whether the element is currently visible using both visibility and display properties.
7485          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7486          * @return {Boolean} True if the element is currently visible, else false
7487          */
7488         isVisible : function(deep) {
7489             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7490             if(deep !== true || !vis){
7491                 return vis;
7492             }
7493             var p = this.dom.parentNode;
7494             while(p && p.tagName.toLowerCase() != "body"){
7495                 if(!Roo.fly(p, '_isVisible').isVisible()){
7496                     return false;
7497                 }
7498                 p = p.parentNode;
7499             }
7500             return true;
7501         },
7502
7503         /**
7504          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7505          * @param {String} selector The CSS selector
7506          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7507          * @return {CompositeElement/CompositeElementLite} The composite element
7508          */
7509         select : function(selector, unique){
7510             return El.select(selector, unique, this.dom);
7511         },
7512
7513         /**
7514          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7515          * @param {String} selector The CSS selector
7516          * @return {Array} An array of the matched nodes
7517          */
7518         query : function(selector, unique){
7519             return Roo.DomQuery.select(selector, this.dom);
7520         },
7521
7522         /**
7523          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7524          * @param {String} selector The CSS selector
7525          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7526          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7527          */
7528         child : function(selector, returnDom){
7529             var n = Roo.DomQuery.selectNode(selector, this.dom);
7530             return returnDom ? n : Roo.get(n);
7531         },
7532
7533         /**
7534          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7535          * @param {String} selector The CSS selector
7536          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7537          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7538          */
7539         down : function(selector, returnDom){
7540             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7541             return returnDom ? n : Roo.get(n);
7542         },
7543
7544         /**
7545          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7546          * @param {String} group The group the DD object is member of
7547          * @param {Object} config The DD config object
7548          * @param {Object} overrides An object containing methods to override/implement on the DD object
7549          * @return {Roo.dd.DD} The DD object
7550          */
7551         initDD : function(group, config, overrides){
7552             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7553             return Roo.apply(dd, overrides);
7554         },
7555
7556         /**
7557          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7558          * @param {String} group The group the DDProxy object is member of
7559          * @param {Object} config The DDProxy config object
7560          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7561          * @return {Roo.dd.DDProxy} The DDProxy object
7562          */
7563         initDDProxy : function(group, config, overrides){
7564             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7565             return Roo.apply(dd, overrides);
7566         },
7567
7568         /**
7569          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7570          * @param {String} group The group the DDTarget object is member of
7571          * @param {Object} config The DDTarget config object
7572          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7573          * @return {Roo.dd.DDTarget} The DDTarget object
7574          */
7575         initDDTarget : function(group, config, overrides){
7576             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7577             return Roo.apply(dd, overrides);
7578         },
7579
7580         /**
7581          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7582          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7583          * @param {Boolean} visible Whether the element is visible
7584          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7585          * @return {Roo.Element} this
7586          */
7587          setVisible : function(visible, animate){
7588             if(!animate || !A){
7589                 if(this.visibilityMode == El.DISPLAY){
7590                     this.setDisplayed(visible);
7591                 }else{
7592                     this.fixDisplay();
7593                     this.dom.style.visibility = visible ? "visible" : "hidden";
7594                 }
7595             }else{
7596                 // closure for composites
7597                 var dom = this.dom;
7598                 var visMode = this.visibilityMode;
7599                 if(visible){
7600                     this.setOpacity(.01);
7601                     this.setVisible(true);
7602                 }
7603                 this.anim({opacity: { to: (visible?1:0) }},
7604                       this.preanim(arguments, 1),
7605                       null, .35, 'easeIn', function(){
7606                          if(!visible){
7607                              if(visMode == El.DISPLAY){
7608                                  dom.style.display = "none";
7609                              }else{
7610                                  dom.style.visibility = "hidden";
7611                              }
7612                              Roo.get(dom).setOpacity(1);
7613                          }
7614                      });
7615             }
7616             return this;
7617         },
7618
7619         /**
7620          * Returns true if display is not "none"
7621          * @return {Boolean}
7622          */
7623         isDisplayed : function() {
7624             return this.getStyle("display") != "none";
7625         },
7626
7627         /**
7628          * Toggles the element's visibility or display, depending on visibility mode.
7629          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7630          * @return {Roo.Element} this
7631          */
7632         toggle : function(animate){
7633             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7639          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7640          * @return {Roo.Element} this
7641          */
7642         setDisplayed : function(value) {
7643             if(typeof value == "boolean"){
7644                value = value ? this.originalDisplay : "none";
7645             }
7646             this.setStyle("display", value);
7647             return this;
7648         },
7649
7650         /**
7651          * Tries to focus the element. Any exceptions are caught and ignored.
7652          * @return {Roo.Element} this
7653          */
7654         focus : function() {
7655             try{
7656                 this.dom.focus();
7657             }catch(e){}
7658             return this;
7659         },
7660
7661         /**
7662          * Tries to blur the element. Any exceptions are caught and ignored.
7663          * @return {Roo.Element} this
7664          */
7665         blur : function() {
7666             try{
7667                 this.dom.blur();
7668             }catch(e){}
7669             return this;
7670         },
7671
7672         /**
7673          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7674          * @param {String/Array} className The CSS class to add, or an array of classes
7675          * @return {Roo.Element} this
7676          */
7677         addClass : function(className){
7678             if(className instanceof Array){
7679                 for(var i = 0, len = className.length; i < len; i++) {
7680                     this.addClass(className[i]);
7681                 }
7682             }else{
7683                 if(className && !this.hasClass(className)){
7684                     if (this.dom instanceof SVGElement) {
7685                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7686                     } else {
7687                         this.dom.className = this.dom.className + " " + className;
7688                     }
7689                 }
7690             }
7691             return this;
7692         },
7693
7694         /**
7695          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7696          * @param {String/Array} className The CSS class to add, or an array of classes
7697          * @return {Roo.Element} this
7698          */
7699         radioClass : function(className){
7700             var siblings = this.dom.parentNode.childNodes;
7701             for(var i = 0; i < siblings.length; i++) {
7702                 var s = siblings[i];
7703                 if(s.nodeType == 1){
7704                     Roo.get(s).removeClass(className);
7705                 }
7706             }
7707             this.addClass(className);
7708             return this;
7709         },
7710
7711         /**
7712          * Removes one or more CSS classes from the element.
7713          * @param {String/Array} className The CSS class to remove, or an array of classes
7714          * @return {Roo.Element} this
7715          */
7716         removeClass : function(className){
7717             
7718             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7719             if(!className || !cn){
7720                 return this;
7721             }
7722             if(className instanceof Array){
7723                 for(var i = 0, len = className.length; i < len; i++) {
7724                     this.removeClass(className[i]);
7725                 }
7726             }else{
7727                 if(this.hasClass(className)){
7728                     var re = this.classReCache[className];
7729                     if (!re) {
7730                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7731                        this.classReCache[className] = re;
7732                     }
7733                     if (this.dom instanceof SVGElement) {
7734                         this.dom.className.baseVal = cn.replace(re, " ");
7735                     } else {
7736                         this.dom.className = cn.replace(re, " ");
7737                     }
7738                 }
7739             }
7740             return this;
7741         },
7742
7743         // private
7744         classReCache: {},
7745
7746         /**
7747          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7748          * @param {String} className The CSS class to toggle
7749          * @return {Roo.Element} this
7750          */
7751         toggleClass : function(className){
7752             if(this.hasClass(className)){
7753                 this.removeClass(className);
7754             }else{
7755                 this.addClass(className);
7756             }
7757             return this;
7758         },
7759
7760         /**
7761          * Checks if the specified CSS class exists on this element's DOM node.
7762          * @param {String} className The CSS class to check for
7763          * @return {Boolean} True if the class exists, else false
7764          */
7765         hasClass : function(className){
7766             if (this.dom instanceof SVGElement) {
7767                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7768             } 
7769             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7770         },
7771
7772         /**
7773          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7774          * @param {String} oldClassName The CSS class to replace
7775          * @param {String} newClassName The replacement CSS class
7776          * @return {Roo.Element} this
7777          */
7778         replaceClass : function(oldClassName, newClassName){
7779             this.removeClass(oldClassName);
7780             this.addClass(newClassName);
7781             return this;
7782         },
7783
7784         /**
7785          * Returns an object with properties matching the styles requested.
7786          * For example, el.getStyles('color', 'font-size', 'width') might return
7787          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7788          * @param {String} style1 A style name
7789          * @param {String} style2 A style name
7790          * @param {String} etc.
7791          * @return {Object} The style object
7792          */
7793         getStyles : function(){
7794             var a = arguments, len = a.length, r = {};
7795             for(var i = 0; i < len; i++){
7796                 r[a[i]] = this.getStyle(a[i]);
7797             }
7798             return r;
7799         },
7800
7801         /**
7802          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7803          * @param {String} property The style property whose value is returned.
7804          * @return {String} The current value of the style property for this element.
7805          */
7806         getStyle : function(){
7807             return view && view.getComputedStyle ?
7808                 function(prop){
7809                     var el = this.dom, v, cs, camel;
7810                     if(prop == 'float'){
7811                         prop = "cssFloat";
7812                     }
7813                     if(el.style && (v = el.style[prop])){
7814                         return v;
7815                     }
7816                     if(cs = view.getComputedStyle(el, "")){
7817                         if(!(camel = propCache[prop])){
7818                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7819                         }
7820                         return cs[camel];
7821                     }
7822                     return null;
7823                 } :
7824                 function(prop){
7825                     var el = this.dom, v, cs, camel;
7826                     if(prop == 'opacity'){
7827                         if(typeof el.style.filter == 'string'){
7828                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7829                             if(m){
7830                                 var fv = parseFloat(m[1]);
7831                                 if(!isNaN(fv)){
7832                                     return fv ? fv / 100 : 0;
7833                                 }
7834                             }
7835                         }
7836                         return 1;
7837                     }else if(prop == 'float'){
7838                         prop = "styleFloat";
7839                     }
7840                     if(!(camel = propCache[prop])){
7841                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7842                     }
7843                     if(v = el.style[camel]){
7844                         return v;
7845                     }
7846                     if(cs = el.currentStyle){
7847                         return cs[camel];
7848                     }
7849                     return null;
7850                 };
7851         }(),
7852
7853         /**
7854          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7855          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7856          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7857          * @return {Roo.Element} this
7858          */
7859         setStyle : function(prop, value){
7860             if(typeof prop == "string"){
7861                 
7862                 if (prop == 'float') {
7863                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7864                     return this;
7865                 }
7866                 
7867                 var camel;
7868                 if(!(camel = propCache[prop])){
7869                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7870                 }
7871                 
7872                 if(camel == 'opacity') {
7873                     this.setOpacity(value);
7874                 }else{
7875                     this.dom.style[camel] = value;
7876                 }
7877             }else{
7878                 for(var style in prop){
7879                     if(typeof prop[style] != "function"){
7880                        this.setStyle(style, prop[style]);
7881                     }
7882                 }
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * More flexible version of {@link #setStyle} for setting style properties.
7889          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7890          * a function which returns such a specification.
7891          * @return {Roo.Element} this
7892          */
7893         applyStyles : function(style){
7894             Roo.DomHelper.applyStyles(this.dom, style);
7895             return this;
7896         },
7897
7898         /**
7899           * 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).
7900           * @return {Number} The X position of the element
7901           */
7902         getX : function(){
7903             return D.getX(this.dom);
7904         },
7905
7906         /**
7907           * 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).
7908           * @return {Number} The Y position of the element
7909           */
7910         getY : function(){
7911             return D.getY(this.dom);
7912         },
7913
7914         /**
7915           * 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).
7916           * @return {Array} The XY position of the element
7917           */
7918         getXY : function(){
7919             return D.getXY(this.dom);
7920         },
7921
7922         /**
7923          * 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).
7924          * @param {Number} The X position of the element
7925          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7926          * @return {Roo.Element} this
7927          */
7928         setX : function(x, animate){
7929             if(!animate || !A){
7930                 D.setX(this.dom, x);
7931             }else{
7932                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7933             }
7934             return this;
7935         },
7936
7937         /**
7938          * 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).
7939          * @param {Number} The Y position of the element
7940          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7941          * @return {Roo.Element} this
7942          */
7943         setY : function(y, animate){
7944             if(!animate || !A){
7945                 D.setY(this.dom, y);
7946             }else{
7947                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7948             }
7949             return this;
7950         },
7951
7952         /**
7953          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7954          * @param {String} left The left CSS property value
7955          * @return {Roo.Element} this
7956          */
7957         setLeft : function(left){
7958             this.setStyle("left", this.addUnits(left));
7959             return this;
7960         },
7961
7962         /**
7963          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7964          * @param {String} top The top CSS property value
7965          * @return {Roo.Element} this
7966          */
7967         setTop : function(top){
7968             this.setStyle("top", this.addUnits(top));
7969             return this;
7970         },
7971
7972         /**
7973          * Sets the element's CSS right style.
7974          * @param {String} right The right CSS property value
7975          * @return {Roo.Element} this
7976          */
7977         setRight : function(right){
7978             this.setStyle("right", this.addUnits(right));
7979             return this;
7980         },
7981
7982         /**
7983          * Sets the element's CSS bottom style.
7984          * @param {String} bottom The bottom CSS property value
7985          * @return {Roo.Element} this
7986          */
7987         setBottom : function(bottom){
7988             this.setStyle("bottom", this.addUnits(bottom));
7989             return this;
7990         },
7991
7992         /**
7993          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7994          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7995          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7996          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7997          * @return {Roo.Element} this
7998          */
7999         setXY : function(pos, animate){
8000             if(!animate || !A){
8001                 D.setXY(this.dom, pos);
8002             }else{
8003                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8004             }
8005             return this;
8006         },
8007
8008         /**
8009          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8010          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8011          * @param {Number} x X value for new position (coordinates are page-based)
8012          * @param {Number} y Y value for new position (coordinates are page-based)
8013          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8014          * @return {Roo.Element} this
8015          */
8016         setLocation : function(x, y, animate){
8017             this.setXY([x, y], this.preanim(arguments, 2));
8018             return this;
8019         },
8020
8021         /**
8022          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8023          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8024          * @param {Number} x X value for new position (coordinates are page-based)
8025          * @param {Number} y Y value for new position (coordinates are page-based)
8026          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         moveTo : function(x, y, animate){
8030             this.setXY([x, y], this.preanim(arguments, 2));
8031             return this;
8032         },
8033
8034         /**
8035          * Returns the region of the given element.
8036          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8037          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8038          */
8039         getRegion : function(){
8040             return D.getRegion(this.dom);
8041         },
8042
8043         /**
8044          * Returns the offset height of the element
8045          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8046          * @return {Number} The element's height
8047          */
8048         getHeight : function(contentHeight){
8049             var h = this.dom.offsetHeight || 0;
8050             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8051         },
8052
8053         /**
8054          * Returns the offset width of the element
8055          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8056          * @return {Number} The element's width
8057          */
8058         getWidth : function(contentWidth){
8059             var w = this.dom.offsetWidth || 0;
8060             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8061         },
8062
8063         /**
8064          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8065          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8066          * if a height has not been set using CSS.
8067          * @return {Number}
8068          */
8069         getComputedHeight : function(){
8070             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8071             if(!h){
8072                 h = parseInt(this.getStyle('height'), 10) || 0;
8073                 if(!this.isBorderBox()){
8074                     h += this.getFrameWidth('tb');
8075                 }
8076             }
8077             return h;
8078         },
8079
8080         /**
8081          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8082          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8083          * if a width has not been set using CSS.
8084          * @return {Number}
8085          */
8086         getComputedWidth : function(){
8087             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8088             if(!w){
8089                 w = parseInt(this.getStyle('width'), 10) || 0;
8090                 if(!this.isBorderBox()){
8091                     w += this.getFrameWidth('lr');
8092                 }
8093             }
8094             return w;
8095         },
8096
8097         /**
8098          * Returns the size of the element.
8099          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8100          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8101          */
8102         getSize : function(contentSize){
8103             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8104         },
8105
8106         /**
8107          * Returns the width and height of the viewport.
8108          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8109          */
8110         getViewSize : function(){
8111             var d = this.dom, doc = document, aw = 0, ah = 0;
8112             if(d == doc || d == doc.body){
8113                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8114             }else{
8115                 return {
8116                     width : d.clientWidth,
8117                     height: d.clientHeight
8118                 };
8119             }
8120         },
8121
8122         /**
8123          * Returns the value of the "value" attribute
8124          * @param {Boolean} asNumber true to parse the value as a number
8125          * @return {String/Number}
8126          */
8127         getValue : function(asNumber){
8128             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8129         },
8130
8131         // private
8132         adjustWidth : function(width){
8133             if(typeof width == "number"){
8134                 if(this.autoBoxAdjust && !this.isBorderBox()){
8135                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8136                 }
8137                 if(width < 0){
8138                     width = 0;
8139                 }
8140             }
8141             return width;
8142         },
8143
8144         // private
8145         adjustHeight : function(height){
8146             if(typeof height == "number"){
8147                if(this.autoBoxAdjust && !this.isBorderBox()){
8148                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8149                }
8150                if(height < 0){
8151                    height = 0;
8152                }
8153             }
8154             return height;
8155         },
8156
8157         /**
8158          * Set the width of the element
8159          * @param {Number} width The new width
8160          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8161          * @return {Roo.Element} this
8162          */
8163         setWidth : function(width, animate){
8164             width = this.adjustWidth(width);
8165             if(!animate || !A){
8166                 this.dom.style.width = this.addUnits(width);
8167             }else{
8168                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8169             }
8170             return this;
8171         },
8172
8173         /**
8174          * Set the height of the element
8175          * @param {Number} height The new height
8176          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8177          * @return {Roo.Element} this
8178          */
8179          setHeight : function(height, animate){
8180             height = this.adjustHeight(height);
8181             if(!animate || !A){
8182                 this.dom.style.height = this.addUnits(height);
8183             }else{
8184                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8185             }
8186             return this;
8187         },
8188
8189         /**
8190          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8191          * @param {Number} width The new width
8192          * @param {Number} height The new height
8193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8194          * @return {Roo.Element} this
8195          */
8196          setSize : function(width, height, animate){
8197             if(typeof width == "object"){ // in case of object from getSize()
8198                 height = width.height; width = width.width;
8199             }
8200             width = this.adjustWidth(width); height = this.adjustHeight(height);
8201             if(!animate || !A){
8202                 this.dom.style.width = this.addUnits(width);
8203                 this.dom.style.height = this.addUnits(height);
8204             }else{
8205                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8206             }
8207             return this;
8208         },
8209
8210         /**
8211          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8212          * @param {Number} x X value for new position (coordinates are page-based)
8213          * @param {Number} y Y value for new position (coordinates are page-based)
8214          * @param {Number} width The new width
8215          * @param {Number} height The new height
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219         setBounds : function(x, y, width, height, animate){
8220             if(!animate || !A){
8221                 this.setSize(width, height);
8222                 this.setLocation(x, y);
8223             }else{
8224                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8225                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8226                               this.preanim(arguments, 4), 'motion');
8227             }
8228             return this;
8229         },
8230
8231         /**
8232          * 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.
8233          * @param {Roo.lib.Region} region The region to fill
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         setRegion : function(region, animate){
8238             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8239             return this;
8240         },
8241
8242         /**
8243          * Appends an event handler
8244          *
8245          * @param {String}   eventName     The type of event to append
8246          * @param {Function} fn        The method the event invokes
8247          * @param {Object} scope       (optional) The scope (this object) of the fn
8248          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8249          */
8250         addListener : function(eventName, fn, scope, options)
8251         {
8252             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8253                 this.addListener('touchstart', this.onTapHandler, this);
8254             }
8255             
8256             // we need to handle a special case where dom element is a svg element.
8257             // in this case we do not actua
8258             if (!this.dom) {
8259                 return;
8260             }
8261             
8262             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8263                 if (typeof(this.listeners[eventName]) == 'undefined') {
8264                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8265                 }
8266                 this.listeners[eventName].addListener(fn, scope, options);
8267                 return;
8268             }
8269             
8270                 
8271             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8272             
8273             
8274         },
8275         tapedTwice : false,
8276         onTapHandler : function(event)
8277         {
8278             if(!this.tapedTwice) {
8279                 this.tapedTwice = true;
8280                 var s = this;
8281                 setTimeout( function() {
8282                     s.tapedTwice = false;
8283                 }, 300 );
8284                 return;
8285             }
8286             event.preventDefault();
8287             var revent = new MouseEvent('dblclick',  {
8288                 view: window,
8289                 bubbles: true,
8290                 cancelable: true
8291             });
8292              
8293             this.dom.dispatchEvent(revent);
8294             //action on double tap goes below
8295              
8296         }, 
8297
8298         /**
8299          * Removes an event handler from this element
8300          * @param {String} eventName the type of event to remove
8301          * @param {Function} fn the method the event invokes
8302          * @param {Function} scope (needed for svg fake listeners)
8303          * @return {Roo.Element} this
8304          */
8305         removeListener : function(eventName, fn, scope){
8306             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8307             if (typeof(this.listeners[eventName]) == 'undefined') {
8308                 return this;
8309             }
8310             this.listeners[eventName].removeListener(fn, scope);
8311             return this;
8312         },
8313
8314         /**
8315          * Removes all previous added listeners from this element
8316          * @return {Roo.Element} this
8317          */
8318         removeAllListeners : function(){
8319             E.purgeElement(this.dom);
8320             this.listeners = {};
8321             return this;
8322         },
8323
8324         relayEvent : function(eventName, observable){
8325             this.on(eventName, function(e){
8326                 observable.fireEvent(eventName, e);
8327             });
8328         },
8329
8330         
8331         /**
8332          * Set the opacity of the element
8333          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8334          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8335          * @return {Roo.Element} this
8336          */
8337          setOpacity : function(opacity, animate){
8338             if(!animate || !A){
8339                 var s = this.dom.style;
8340                 if(Roo.isIE){
8341                     s.zoom = 1;
8342                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8343                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8344                 }else{
8345                     s.opacity = opacity;
8346                 }
8347             }else{
8348                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8349             }
8350             return this;
8351         },
8352
8353         /**
8354          * Gets the left X coordinate
8355          * @param {Boolean} local True to get the local css position instead of page coordinate
8356          * @return {Number}
8357          */
8358         getLeft : function(local){
8359             if(!local){
8360                 return this.getX();
8361             }else{
8362                 return parseInt(this.getStyle("left"), 10) || 0;
8363             }
8364         },
8365
8366         /**
8367          * Gets the right X coordinate of the element (element X position + element width)
8368          * @param {Boolean} local True to get the local css position instead of page coordinate
8369          * @return {Number}
8370          */
8371         getRight : function(local){
8372             if(!local){
8373                 return this.getX() + this.getWidth();
8374             }else{
8375                 return (this.getLeft(true) + this.getWidth()) || 0;
8376             }
8377         },
8378
8379         /**
8380          * Gets the top Y coordinate
8381          * @param {Boolean} local True to get the local css position instead of page coordinate
8382          * @return {Number}
8383          */
8384         getTop : function(local) {
8385             if(!local){
8386                 return this.getY();
8387             }else{
8388                 return parseInt(this.getStyle("top"), 10) || 0;
8389             }
8390         },
8391
8392         /**
8393          * Gets the bottom Y coordinate of the element (element Y position + element height)
8394          * @param {Boolean} local True to get the local css position instead of page coordinate
8395          * @return {Number}
8396          */
8397         getBottom : function(local){
8398             if(!local){
8399                 return this.getY() + this.getHeight();
8400             }else{
8401                 return (this.getTop(true) + this.getHeight()) || 0;
8402             }
8403         },
8404
8405         /**
8406         * Initializes positioning on this element. If a desired position is not passed, it will make the
8407         * the element positioned relative IF it is not already positioned.
8408         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8409         * @param {Number} zIndex (optional) The zIndex to apply
8410         * @param {Number} x (optional) Set the page X position
8411         * @param {Number} y (optional) Set the page Y position
8412         */
8413         position : function(pos, zIndex, x, y){
8414             if(!pos){
8415                if(this.getStyle('position') == 'static'){
8416                    this.setStyle('position', 'relative');
8417                }
8418             }else{
8419                 this.setStyle("position", pos);
8420             }
8421             if(zIndex){
8422                 this.setStyle("z-index", zIndex);
8423             }
8424             if(x !== undefined && y !== undefined){
8425                 this.setXY([x, y]);
8426             }else if(x !== undefined){
8427                 this.setX(x);
8428             }else if(y !== undefined){
8429                 this.setY(y);
8430             }
8431         },
8432
8433         /**
8434         * Clear positioning back to the default when the document was loaded
8435         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8436         * @return {Roo.Element} this
8437          */
8438         clearPositioning : function(value){
8439             value = value ||'';
8440             this.setStyle({
8441                 "left": value,
8442                 "right": value,
8443                 "top": value,
8444                 "bottom": value,
8445                 "z-index": "",
8446                 "position" : "static"
8447             });
8448             return this;
8449         },
8450
8451         /**
8452         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8453         * snapshot before performing an update and then restoring the element.
8454         * @return {Object}
8455         */
8456         getPositioning : function(){
8457             var l = this.getStyle("left");
8458             var t = this.getStyle("top");
8459             return {
8460                 "position" : this.getStyle("position"),
8461                 "left" : l,
8462                 "right" : l ? "" : this.getStyle("right"),
8463                 "top" : t,
8464                 "bottom" : t ? "" : this.getStyle("bottom"),
8465                 "z-index" : this.getStyle("z-index")
8466             };
8467         },
8468
8469         /**
8470          * Gets the width of the border(s) for the specified side(s)
8471          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8472          * passing lr would get the border (l)eft width + the border (r)ight width.
8473          * @return {Number} The width of the sides passed added together
8474          */
8475         getBorderWidth : function(side){
8476             return this.addStyles(side, El.borders);
8477         },
8478
8479         /**
8480          * Gets the width of the padding(s) for the specified side(s)
8481          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8482          * passing lr would get the padding (l)eft + the padding (r)ight.
8483          * @return {Number} The padding of the sides passed added together
8484          */
8485         getPadding : function(side){
8486             return this.addStyles(side, El.paddings);
8487         },
8488
8489         /**
8490         * Set positioning with an object returned by getPositioning().
8491         * @param {Object} posCfg
8492         * @return {Roo.Element} this
8493          */
8494         setPositioning : function(pc){
8495             this.applyStyles(pc);
8496             if(pc.right == "auto"){
8497                 this.dom.style.right = "";
8498             }
8499             if(pc.bottom == "auto"){
8500                 this.dom.style.bottom = "";
8501             }
8502             return this;
8503         },
8504
8505         // private
8506         fixDisplay : function(){
8507             if(this.getStyle("display") == "none"){
8508                 this.setStyle("visibility", "hidden");
8509                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8510                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8511                     this.setStyle("display", "block");
8512                 }
8513             }
8514         },
8515
8516         /**
8517          * Quick set left and top adding default units
8518          * @param {String} left The left CSS property value
8519          * @param {String} top The top CSS property value
8520          * @return {Roo.Element} this
8521          */
8522          setLeftTop : function(left, top){
8523             this.dom.style.left = this.addUnits(left);
8524             this.dom.style.top = this.addUnits(top);
8525             return this;
8526         },
8527
8528         /**
8529          * Move this element relative to its current position.
8530          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8531          * @param {Number} distance How far to move the element in pixels
8532          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8533          * @return {Roo.Element} this
8534          */
8535          move : function(direction, distance, animate){
8536             var xy = this.getXY();
8537             direction = direction.toLowerCase();
8538             switch(direction){
8539                 case "l":
8540                 case "left":
8541                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8542                     break;
8543                case "r":
8544                case "right":
8545                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8546                     break;
8547                case "t":
8548                case "top":
8549                case "up":
8550                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8551                     break;
8552                case "b":
8553                case "bottom":
8554                case "down":
8555                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8556                     break;
8557             }
8558             return this;
8559         },
8560
8561         /**
8562          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8563          * @return {Roo.Element} this
8564          */
8565         clip : function(){
8566             if(!this.isClipped){
8567                this.isClipped = true;
8568                this.originalClip = {
8569                    "o": this.getStyle("overflow"),
8570                    "x": this.getStyle("overflow-x"),
8571                    "y": this.getStyle("overflow-y")
8572                };
8573                this.setStyle("overflow", "hidden");
8574                this.setStyle("overflow-x", "hidden");
8575                this.setStyle("overflow-y", "hidden");
8576             }
8577             return this;
8578         },
8579
8580         /**
8581          *  Return clipping (overflow) to original clipping before clip() was called
8582          * @return {Roo.Element} this
8583          */
8584         unclip : function(){
8585             if(this.isClipped){
8586                 this.isClipped = false;
8587                 var o = this.originalClip;
8588                 if(o.o){this.setStyle("overflow", o.o);}
8589                 if(o.x){this.setStyle("overflow-x", o.x);}
8590                 if(o.y){this.setStyle("overflow-y", o.y);}
8591             }
8592             return this;
8593         },
8594
8595
8596         /**
8597          * Gets the x,y coordinates specified by the anchor position on the element.
8598          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8599          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8600          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8601          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8602          * @return {Array} [x, y] An array containing the element's x and y coordinates
8603          */
8604         getAnchorXY : function(anchor, local, s){
8605             //Passing a different size is useful for pre-calculating anchors,
8606             //especially for anchored animations that change the el size.
8607
8608             var w, h, vp = false;
8609             if(!s){
8610                 var d = this.dom;
8611                 if(d == document.body || d == document){
8612                     vp = true;
8613                     w = D.getViewWidth(); h = D.getViewHeight();
8614                 }else{
8615                     w = this.getWidth(); h = this.getHeight();
8616                 }
8617             }else{
8618                 w = s.width;  h = s.height;
8619             }
8620             var x = 0, y = 0, r = Math.round;
8621             switch((anchor || "tl").toLowerCase()){
8622                 case "c":
8623                     x = r(w*.5);
8624                     y = r(h*.5);
8625                 break;
8626                 case "t":
8627                     x = r(w*.5);
8628                     y = 0;
8629                 break;
8630                 case "l":
8631                     x = 0;
8632                     y = r(h*.5);
8633                 break;
8634                 case "r":
8635                     x = w;
8636                     y = r(h*.5);
8637                 break;
8638                 case "b":
8639                     x = r(w*.5);
8640                     y = h;
8641                 break;
8642                 case "tl":
8643                     x = 0;
8644                     y = 0;
8645                 break;
8646                 case "bl":
8647                     x = 0;
8648                     y = h;
8649                 break;
8650                 case "br":
8651                     x = w;
8652                     y = h;
8653                 break;
8654                 case "tr":
8655                     x = w;
8656                     y = 0;
8657                 break;
8658             }
8659             if(local === true){
8660                 return [x, y];
8661             }
8662             if(vp){
8663                 var sc = this.getScroll();
8664                 return [x + sc.left, y + sc.top];
8665             }
8666             //Add the element's offset xy
8667             var o = this.getXY();
8668             return [x+o[0], y+o[1]];
8669         },
8670
8671         /**
8672          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8673          * supported position values.
8674          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8675          * @param {String} position The position to align to.
8676          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8677          * @return {Array} [x, y]
8678          */
8679         getAlignToXY : function(el, p, o)
8680         {
8681             el = Roo.get(el);
8682             var d = this.dom;
8683             if(!el.dom){
8684                 throw "Element.alignTo with an element that doesn't exist";
8685             }
8686             var c = false; //constrain to viewport
8687             var p1 = "", p2 = "";
8688             o = o || [0,0];
8689
8690             if(!p){
8691                 p = "tl-bl";
8692             }else if(p == "?"){
8693                 p = "tl-bl?";
8694             }else if(p.indexOf("-") == -1){
8695                 p = "tl-" + p;
8696             }
8697             p = p.toLowerCase();
8698             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8699             if(!m){
8700                throw "Element.alignTo with an invalid alignment " + p;
8701             }
8702             p1 = m[1]; p2 = m[2]; c = !!m[3];
8703
8704             //Subtract the aligned el's internal xy from the target's offset xy
8705             //plus custom offset to get the aligned el's new offset xy
8706             var a1 = this.getAnchorXY(p1, true);
8707             var a2 = el.getAnchorXY(p2, false);
8708             var x = a2[0] - a1[0] + o[0];
8709             var y = a2[1] - a1[1] + o[1];
8710             if(c){
8711                 //constrain the aligned el to viewport if necessary
8712                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8713                 // 5px of margin for ie
8714                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8715
8716                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8717                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8718                 //otherwise swap the aligned el to the opposite border of the target.
8719                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8720                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8721                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8722                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8723
8724                var doc = document;
8725                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8726                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8727
8728                if((x+w) > dw + scrollX){
8729                     x = swapX ? r.left-w : dw+scrollX-w;
8730                 }
8731                if(x < scrollX){
8732                    x = swapX ? r.right : scrollX;
8733                }
8734                if((y+h) > dh + scrollY){
8735                     y = swapY ? r.top-h : dh+scrollY-h;
8736                 }
8737                if (y < scrollY){
8738                    y = swapY ? r.bottom : scrollY;
8739                }
8740             }
8741             return [x,y];
8742         },
8743
8744         // private
8745         getConstrainToXY : function(){
8746             var os = {top:0, left:0, bottom:0, right: 0};
8747
8748             return function(el, local, offsets, proposedXY){
8749                 el = Roo.get(el);
8750                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8751
8752                 var vw, vh, vx = 0, vy = 0;
8753                 if(el.dom == document.body || el.dom == document){
8754                     vw = Roo.lib.Dom.getViewWidth();
8755                     vh = Roo.lib.Dom.getViewHeight();
8756                 }else{
8757                     vw = el.dom.clientWidth;
8758                     vh = el.dom.clientHeight;
8759                     if(!local){
8760                         var vxy = el.getXY();
8761                         vx = vxy[0];
8762                         vy = vxy[1];
8763                     }
8764                 }
8765
8766                 var s = el.getScroll();
8767
8768                 vx += offsets.left + s.left;
8769                 vy += offsets.top + s.top;
8770
8771                 vw -= offsets.right;
8772                 vh -= offsets.bottom;
8773
8774                 var vr = vx+vw;
8775                 var vb = vy+vh;
8776
8777                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8778                 var x = xy[0], y = xy[1];
8779                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8780
8781                 // only move it if it needs it
8782                 var moved = false;
8783
8784                 // first validate right/bottom
8785                 if((x + w) > vr){
8786                     x = vr - w;
8787                     moved = true;
8788                 }
8789                 if((y + h) > vb){
8790                     y = vb - h;
8791                     moved = true;
8792                 }
8793                 // then make sure top/left isn't negative
8794                 if(x < vx){
8795                     x = vx;
8796                     moved = true;
8797                 }
8798                 if(y < vy){
8799                     y = vy;
8800                     moved = true;
8801                 }
8802                 return moved ? [x, y] : false;
8803             };
8804         }(),
8805
8806         // private
8807         adjustForConstraints : function(xy, parent, offsets){
8808             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8809         },
8810
8811         /**
8812          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8813          * document it aligns it to the viewport.
8814          * The position parameter is optional, and can be specified in any one of the following formats:
8815          * <ul>
8816          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8817          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8818          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8819          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8820          *   <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
8821          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8822          * </ul>
8823          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8824          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8825          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8826          * that specified in order to enforce the viewport constraints.
8827          * Following are all of the supported anchor positions:
8828     <pre>
8829     Value  Description
8830     -----  -----------------------------
8831     tl     The top left corner (default)
8832     t      The center of the top edge
8833     tr     The top right corner
8834     l      The center of the left edge
8835     c      In the center of the element
8836     r      The center of the right edge
8837     bl     The bottom left corner
8838     b      The center of the bottom edge
8839     br     The bottom right corner
8840     </pre>
8841     Example Usage:
8842     <pre><code>
8843     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8844     el.alignTo("other-el");
8845
8846     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8847     el.alignTo("other-el", "tr?");
8848
8849     // align the bottom right corner of el with the center left edge of other-el
8850     el.alignTo("other-el", "br-l?");
8851
8852     // align the center of el with the bottom left corner of other-el and
8853     // adjust the x position by -6 pixels (and the y position by 0)
8854     el.alignTo("other-el", "c-bl", [-6, 0]);
8855     </code></pre>
8856          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8857          * @param {String} position The position to align to.
8858          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8859          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8860          * @return {Roo.Element} this
8861          */
8862         alignTo : function(element, position, offsets, animate){
8863             var xy = this.getAlignToXY(element, position, offsets);
8864             this.setXY(xy, this.preanim(arguments, 3));
8865             return this;
8866         },
8867
8868         /**
8869          * Anchors an element to another element and realigns it when the window is resized.
8870          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8871          * @param {String} position The position to align to.
8872          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8873          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8874          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8875          * is a number, it is used as the buffer delay (defaults to 50ms).
8876          * @param {Function} callback The function to call after the animation finishes
8877          * @return {Roo.Element} this
8878          */
8879         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8880             var action = function(){
8881                 this.alignTo(el, alignment, offsets, animate);
8882                 Roo.callback(callback, this);
8883             };
8884             Roo.EventManager.onWindowResize(action, this);
8885             var tm = typeof monitorScroll;
8886             if(tm != 'undefined'){
8887                 Roo.EventManager.on(window, 'scroll', action, this,
8888                     {buffer: tm == 'number' ? monitorScroll : 50});
8889             }
8890             action.call(this); // align immediately
8891             return this;
8892         },
8893         /**
8894          * Clears any opacity settings from this element. Required in some cases for IE.
8895          * @return {Roo.Element} this
8896          */
8897         clearOpacity : function(){
8898             if (window.ActiveXObject) {
8899                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8900                     this.dom.style.filter = "";
8901                 }
8902             } else {
8903                 this.dom.style.opacity = "";
8904                 this.dom.style["-moz-opacity"] = "";
8905                 this.dom.style["-khtml-opacity"] = "";
8906             }
8907             return this;
8908         },
8909
8910         /**
8911          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8913          * @return {Roo.Element} this
8914          */
8915         hide : function(animate){
8916             this.setVisible(false, this.preanim(arguments, 0));
8917             return this;
8918         },
8919
8920         /**
8921         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8922         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8923          * @return {Roo.Element} this
8924          */
8925         show : function(animate){
8926             this.setVisible(true, this.preanim(arguments, 0));
8927             return this;
8928         },
8929
8930         /**
8931          * @private Test if size has a unit, otherwise appends the default
8932          */
8933         addUnits : function(size){
8934             return Roo.Element.addUnits(size, this.defaultUnit);
8935         },
8936
8937         /**
8938          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8939          * @return {Roo.Element} this
8940          */
8941         beginMeasure : function(){
8942             var el = this.dom;
8943             if(el.offsetWidth || el.offsetHeight){
8944                 return this; // offsets work already
8945             }
8946             var changed = [];
8947             var p = this.dom, b = document.body; // start with this element
8948             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8949                 var pe = Roo.get(p);
8950                 if(pe.getStyle('display') == 'none'){
8951                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8952                     p.style.visibility = "hidden";
8953                     p.style.display = "block";
8954                 }
8955                 p = p.parentNode;
8956             }
8957             this._measureChanged = changed;
8958             return this;
8959
8960         },
8961
8962         /**
8963          * Restores displays to before beginMeasure was called
8964          * @return {Roo.Element} this
8965          */
8966         endMeasure : function(){
8967             var changed = this._measureChanged;
8968             if(changed){
8969                 for(var i = 0, len = changed.length; i < len; i++) {
8970                     var r = changed[i];
8971                     r.el.style.visibility = r.visibility;
8972                     r.el.style.display = "none";
8973                 }
8974                 this._measureChanged = null;
8975             }
8976             return this;
8977         },
8978
8979         /**
8980         * Update the innerHTML of this element, optionally searching for and processing scripts
8981         * @param {String} html The new HTML
8982         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8983         * @param {Function} callback For async script loading you can be noticed when the update completes
8984         * @return {Roo.Element} this
8985          */
8986         update : function(html, loadScripts, callback){
8987             if(typeof html == "undefined"){
8988                 html = "";
8989             }
8990             if(loadScripts !== true){
8991                 this.dom.innerHTML = html;
8992                 if(typeof callback == "function"){
8993                     callback();
8994                 }
8995                 return this;
8996             }
8997             var id = Roo.id();
8998             var dom = this.dom;
8999
9000             html += '<span id="' + id + '"></span>';
9001
9002             E.onAvailable(id, function(){
9003                 var hd = document.getElementsByTagName("head")[0];
9004                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9005                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9006                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9007
9008                 var match;
9009                 while(match = re.exec(html)){
9010                     var attrs = match[1];
9011                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9012                     if(srcMatch && srcMatch[2]){
9013                        var s = document.createElement("script");
9014                        s.src = srcMatch[2];
9015                        var typeMatch = attrs.match(typeRe);
9016                        if(typeMatch && typeMatch[2]){
9017                            s.type = typeMatch[2];
9018                        }
9019                        hd.appendChild(s);
9020                     }else if(match[2] && match[2].length > 0){
9021                         if(window.execScript) {
9022                            window.execScript(match[2]);
9023                         } else {
9024                             /**
9025                              * eval:var:id
9026                              * eval:var:dom
9027                              * eval:var:html
9028                              * 
9029                              */
9030                            window.eval(match[2]);
9031                         }
9032                     }
9033                 }
9034                 var el = document.getElementById(id);
9035                 if(el){el.parentNode.removeChild(el);}
9036                 if(typeof callback == "function"){
9037                     callback();
9038                 }
9039             });
9040             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9041             return this;
9042         },
9043
9044         /**
9045          * Direct access to the UpdateManager update() method (takes the same parameters).
9046          * @param {String/Function} url The url for this request or a function to call to get the url
9047          * @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}
9048          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9049          * @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.
9050          * @return {Roo.Element} this
9051          */
9052         load : function(){
9053             var um = this.getUpdateManager();
9054             um.update.apply(um, arguments);
9055             return this;
9056         },
9057
9058         /**
9059         * Gets this element's UpdateManager
9060         * @return {Roo.UpdateManager} The UpdateManager
9061         */
9062         getUpdateManager : function(){
9063             if(!this.updateManager){
9064                 this.updateManager = new Roo.UpdateManager(this);
9065             }
9066             return this.updateManager;
9067         },
9068
9069         /**
9070          * Disables text selection for this element (normalized across browsers)
9071          * @return {Roo.Element} this
9072          */
9073         unselectable : function(){
9074             this.dom.unselectable = "on";
9075             this.swallowEvent("selectstart", true);
9076             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9077             this.addClass("x-unselectable");
9078             return this;
9079         },
9080
9081         /**
9082         * Calculates the x, y to center this element on the screen
9083         * @return {Array} The x, y values [x, y]
9084         */
9085         getCenterXY : function(){
9086             return this.getAlignToXY(document, 'c-c');
9087         },
9088
9089         /**
9090         * Centers the Element in either the viewport, or another Element.
9091         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9092         */
9093         center : function(centerIn){
9094             this.alignTo(centerIn || document, 'c-c');
9095             return this;
9096         },
9097
9098         /**
9099          * Tests various css rules/browsers to determine if this element uses a border box
9100          * @return {Boolean}
9101          */
9102         isBorderBox : function(){
9103             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9104         },
9105
9106         /**
9107          * Return a box {x, y, width, height} that can be used to set another elements
9108          * size/location to match this element.
9109          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9110          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9111          * @return {Object} box An object in the format {x, y, width, height}
9112          */
9113         getBox : function(contentBox, local){
9114             var xy;
9115             if(!local){
9116                 xy = this.getXY();
9117             }else{
9118                 var left = parseInt(this.getStyle("left"), 10) || 0;
9119                 var top = parseInt(this.getStyle("top"), 10) || 0;
9120                 xy = [left, top];
9121             }
9122             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9123             if(!contentBox){
9124                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9125             }else{
9126                 var l = this.getBorderWidth("l")+this.getPadding("l");
9127                 var r = this.getBorderWidth("r")+this.getPadding("r");
9128                 var t = this.getBorderWidth("t")+this.getPadding("t");
9129                 var b = this.getBorderWidth("b")+this.getPadding("b");
9130                 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)};
9131             }
9132             bx.right = bx.x + bx.width;
9133             bx.bottom = bx.y + bx.height;
9134             return bx;
9135         },
9136
9137         /**
9138          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9139          for more information about the sides.
9140          * @param {String} sides
9141          * @return {Number}
9142          */
9143         getFrameWidth : function(sides, onlyContentBox){
9144             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9145         },
9146
9147         /**
9148          * 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.
9149          * @param {Object} box The box to fill {x, y, width, height}
9150          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9152          * @return {Roo.Element} this
9153          */
9154         setBox : function(box, adjust, animate){
9155             var w = box.width, h = box.height;
9156             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9157                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9158                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9159             }
9160             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9161             return this;
9162         },
9163
9164         /**
9165          * Forces the browser to repaint this element
9166          * @return {Roo.Element} this
9167          */
9168          repaint : function(){
9169             var dom = this.dom;
9170             this.addClass("x-repaint");
9171             setTimeout(function(){
9172                 Roo.get(dom).removeClass("x-repaint");
9173             }, 1);
9174             return this;
9175         },
9176
9177         /**
9178          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9179          * then it returns the calculated width of the sides (see getPadding)
9180          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9181          * @return {Object/Number}
9182          */
9183         getMargins : function(side){
9184             if(!side){
9185                 return {
9186                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9187                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9188                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9189                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9190                 };
9191             }else{
9192                 return this.addStyles(side, El.margins);
9193              }
9194         },
9195
9196         // private
9197         addStyles : function(sides, styles){
9198             var val = 0, v, w;
9199             for(var i = 0, len = sides.length; i < len; i++){
9200                 v = this.getStyle(styles[sides.charAt(i)]);
9201                 if(v){
9202                      w = parseInt(v, 10);
9203                      if(w){ val += w; }
9204                 }
9205             }
9206             return val;
9207         },
9208
9209         /**
9210          * Creates a proxy element of this element
9211          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9212          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9213          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9214          * @return {Roo.Element} The new proxy element
9215          */
9216         createProxy : function(config, renderTo, matchBox){
9217             if(renderTo){
9218                 renderTo = Roo.getDom(renderTo);
9219             }else{
9220                 renderTo = document.body;
9221             }
9222             config = typeof config == "object" ?
9223                 config : {tag : "div", cls: config};
9224             var proxy = Roo.DomHelper.append(renderTo, config, true);
9225             if(matchBox){
9226                proxy.setBox(this.getBox());
9227             }
9228             return proxy;
9229         },
9230
9231         /**
9232          * Puts a mask over this element to disable user interaction. Requires core.css.
9233          * This method can only be applied to elements which accept child nodes.
9234          * @param {String} msg (optional) A message to display in the mask
9235          * @param {String} msgCls (optional) A css class to apply to the msg element
9236          * @return {Element} The mask  element
9237          */
9238         mask : function(msg, msgCls)
9239         {
9240             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9241                 this.setStyle("position", "relative");
9242             }
9243             if(!this._mask){
9244                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9245             }
9246             
9247             this.addClass("x-masked");
9248             this._mask.setDisplayed(true);
9249             
9250             // we wander
9251             var z = 0;
9252             var dom = this.dom;
9253             while (dom && dom.style) {
9254                 if (!isNaN(parseInt(dom.style.zIndex))) {
9255                     z = Math.max(z, parseInt(dom.style.zIndex));
9256                 }
9257                 dom = dom.parentNode;
9258             }
9259             // if we are masking the body - then it hides everything..
9260             if (this.dom == document.body) {
9261                 z = 1000000;
9262                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9263                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9264             }
9265            
9266             if(typeof msg == 'string'){
9267                 if(!this._maskMsg){
9268                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9269                         cls: "roo-el-mask-msg", 
9270                         cn: [
9271                             {
9272                                 tag: 'i',
9273                                 cls: 'fa fa-spinner fa-spin'
9274                             },
9275                             {
9276                                 tag: 'div'
9277                             }   
9278                         ]
9279                     }, true);
9280                 }
9281                 var mm = this._maskMsg;
9282                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9283                 if (mm.dom.lastChild) { // weird IE issue?
9284                     mm.dom.lastChild.innerHTML = msg;
9285                 }
9286                 mm.setDisplayed(true);
9287                 mm.center(this);
9288                 mm.setStyle('z-index', z + 102);
9289             }
9290             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9291                 this._mask.setHeight(this.getHeight());
9292             }
9293             this._mask.setStyle('z-index', z + 100);
9294             
9295             return this._mask;
9296         },
9297
9298         /**
9299          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9300          * it is cached for reuse.
9301          */
9302         unmask : function(removeEl){
9303             if(this._mask){
9304                 if(removeEl === true){
9305                     this._mask.remove();
9306                     delete this._mask;
9307                     if(this._maskMsg){
9308                         this._maskMsg.remove();
9309                         delete this._maskMsg;
9310                     }
9311                 }else{
9312                     this._mask.setDisplayed(false);
9313                     if(this._maskMsg){
9314                         this._maskMsg.setDisplayed(false);
9315                     }
9316                 }
9317             }
9318             this.removeClass("x-masked");
9319         },
9320
9321         /**
9322          * Returns true if this element is masked
9323          * @return {Boolean}
9324          */
9325         isMasked : function(){
9326             return this._mask && this._mask.isVisible();
9327         },
9328
9329         /**
9330          * Creates an iframe shim for this element to keep selects and other windowed objects from
9331          * showing through.
9332          * @return {Roo.Element} The new shim element
9333          */
9334         createShim : function(){
9335             var el = document.createElement('iframe');
9336             el.frameBorder = 'no';
9337             el.className = 'roo-shim';
9338             if(Roo.isIE && Roo.isSecure){
9339                 el.src = Roo.SSL_SECURE_URL;
9340             }
9341             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9342             shim.autoBoxAdjust = false;
9343             return shim;
9344         },
9345
9346         /**
9347          * Removes this element from the DOM and deletes it from the cache
9348          */
9349         remove : function(){
9350             if(this.dom.parentNode){
9351                 this.dom.parentNode.removeChild(this.dom);
9352             }
9353             delete El.cache[this.dom.id];
9354         },
9355
9356         /**
9357          * Sets up event handlers to add and remove a css class when the mouse is over this element
9358          * @param {String} className
9359          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9360          * mouseout events for children elements
9361          * @return {Roo.Element} this
9362          */
9363         addClassOnOver : function(className, preventFlicker){
9364             this.on("mouseover", function(){
9365                 Roo.fly(this, '_internal').addClass(className);
9366             }, this.dom);
9367             var removeFn = function(e){
9368                 if(preventFlicker !== true || !e.within(this, true)){
9369                     Roo.fly(this, '_internal').removeClass(className);
9370                 }
9371             };
9372             this.on("mouseout", removeFn, this.dom);
9373             return this;
9374         },
9375
9376         /**
9377          * Sets up event handlers to add and remove a css class when this element has the focus
9378          * @param {String} className
9379          * @return {Roo.Element} this
9380          */
9381         addClassOnFocus : function(className){
9382             this.on("focus", function(){
9383                 Roo.fly(this, '_internal').addClass(className);
9384             }, this.dom);
9385             this.on("blur", function(){
9386                 Roo.fly(this, '_internal').removeClass(className);
9387             }, this.dom);
9388             return this;
9389         },
9390         /**
9391          * 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)
9392          * @param {String} className
9393          * @return {Roo.Element} this
9394          */
9395         addClassOnClick : function(className){
9396             var dom = this.dom;
9397             this.on("mousedown", function(){
9398                 Roo.fly(dom, '_internal').addClass(className);
9399                 var d = Roo.get(document);
9400                 var fn = function(){
9401                     Roo.fly(dom, '_internal').removeClass(className);
9402                     d.removeListener("mouseup", fn);
9403                 };
9404                 d.on("mouseup", fn);
9405             });
9406             return this;
9407         },
9408
9409         /**
9410          * Stops the specified event from bubbling and optionally prevents the default action
9411          * @param {String} eventName
9412          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9413          * @return {Roo.Element} this
9414          */
9415         swallowEvent : function(eventName, preventDefault){
9416             var fn = function(e){
9417                 e.stopPropagation();
9418                 if(preventDefault){
9419                     e.preventDefault();
9420                 }
9421             };
9422             if(eventName instanceof Array){
9423                 for(var i = 0, len = eventName.length; i < len; i++){
9424                      this.on(eventName[i], fn);
9425                 }
9426                 return this;
9427             }
9428             this.on(eventName, fn);
9429             return this;
9430         },
9431
9432         /**
9433          * @private
9434          */
9435         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9436
9437         /**
9438          * Sizes this element to its parent element's dimensions performing
9439          * neccessary box adjustments.
9440          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9441          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9442          * @return {Roo.Element} this
9443          */
9444         fitToParent : function(monitorResize, targetParent) {
9445           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9446           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9447           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9448             return;
9449           }
9450           var p = Roo.get(targetParent || this.dom.parentNode);
9451           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9452           if (monitorResize === true) {
9453             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9454             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9455           }
9456           return this;
9457         },
9458
9459         /**
9460          * Gets the next sibling, skipping text nodes
9461          * @return {HTMLElement} The next sibling or null
9462          */
9463         getNextSibling : function(){
9464             var n = this.dom.nextSibling;
9465             while(n && n.nodeType != 1){
9466                 n = n.nextSibling;
9467             }
9468             return n;
9469         },
9470
9471         /**
9472          * Gets the previous sibling, skipping text nodes
9473          * @return {HTMLElement} The previous sibling or null
9474          */
9475         getPrevSibling : function(){
9476             var n = this.dom.previousSibling;
9477             while(n && n.nodeType != 1){
9478                 n = n.previousSibling;
9479             }
9480             return n;
9481         },
9482
9483
9484         /**
9485          * Appends the passed element(s) to this element
9486          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9487          * @return {Roo.Element} this
9488          */
9489         appendChild: function(el){
9490             el = Roo.get(el);
9491             el.appendTo(this);
9492             return this;
9493         },
9494
9495         /**
9496          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9497          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9498          * automatically generated with the specified attributes.
9499          * @param {HTMLElement} insertBefore (optional) a child element of this element
9500          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9501          * @return {Roo.Element} The new child element
9502          */
9503         createChild: function(config, insertBefore, returnDom){
9504             config = config || {tag:'div'};
9505             if(insertBefore){
9506                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9507             }
9508             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9509         },
9510
9511         /**
9512          * Appends this element to the passed element
9513          * @param {String/HTMLElement/Element} el The new parent element
9514          * @return {Roo.Element} this
9515          */
9516         appendTo: function(el){
9517             el = Roo.getDom(el);
9518             el.appendChild(this.dom);
9519             return this;
9520         },
9521
9522         /**
9523          * Inserts this element before the passed element in the DOM
9524          * @param {String/HTMLElement/Element} el The element to insert before
9525          * @return {Roo.Element} this
9526          */
9527         insertBefore: function(el){
9528             el = Roo.getDom(el);
9529             el.parentNode.insertBefore(this.dom, el);
9530             return this;
9531         },
9532
9533         /**
9534          * Inserts this element after the passed element in the DOM
9535          * @param {String/HTMLElement/Element} el The element to insert after
9536          * @return {Roo.Element} this
9537          */
9538         insertAfter: function(el){
9539             el = Roo.getDom(el);
9540             el.parentNode.insertBefore(this.dom, el.nextSibling);
9541             return this;
9542         },
9543
9544         /**
9545          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9546          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9547          * @return {Roo.Element} The new child
9548          */
9549         insertFirst: function(el, returnDom){
9550             el = el || {};
9551             if(typeof el == 'object' && !el.nodeType){ // dh config
9552                 return this.createChild(el, this.dom.firstChild, returnDom);
9553             }else{
9554                 el = Roo.getDom(el);
9555                 this.dom.insertBefore(el, this.dom.firstChild);
9556                 return !returnDom ? Roo.get(el) : el;
9557             }
9558         },
9559
9560         /**
9561          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9562          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9563          * @param {String} where (optional) 'before' or 'after' defaults to before
9564          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9565          * @return {Roo.Element} the inserted Element
9566          */
9567         insertSibling: function(el, where, returnDom){
9568             where = where ? where.toLowerCase() : 'before';
9569             el = el || {};
9570             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9571
9572             if(typeof el == 'object' && !el.nodeType){ // dh config
9573                 if(where == 'after' && !this.dom.nextSibling){
9574                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9575                 }else{
9576                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9577                 }
9578
9579             }else{
9580                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9581                             where == 'before' ? this.dom : this.dom.nextSibling);
9582                 if(!returnDom){
9583                     rt = Roo.get(rt);
9584                 }
9585             }
9586             return rt;
9587         },
9588
9589         /**
9590          * Creates and wraps this element with another element
9591          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9592          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9593          * @return {HTMLElement/Element} The newly created wrapper element
9594          */
9595         wrap: function(config, returnDom){
9596             if(!config){
9597                 config = {tag: "div"};
9598             }
9599             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9600             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9601             return newEl;
9602         },
9603
9604         /**
9605          * Replaces the passed element with this element
9606          * @param {String/HTMLElement/Element} el The element to replace
9607          * @return {Roo.Element} this
9608          */
9609         replace: function(el){
9610             el = Roo.get(el);
9611             this.insertBefore(el);
9612             el.remove();
9613             return this;
9614         },
9615
9616         /**
9617          * Inserts an html fragment into this element
9618          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9619          * @param {String} html The HTML fragment
9620          * @param {Boolean} returnEl True to return an Roo.Element
9621          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9622          */
9623         insertHtml : function(where, html, returnEl){
9624             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9625             return returnEl ? Roo.get(el) : el;
9626         },
9627
9628         /**
9629          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9630          * @param {Object} o The object with the attributes
9631          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9632          * @return {Roo.Element} this
9633          */
9634         set : function(o, useSet){
9635             var el = this.dom;
9636             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9637             for(var attr in o){
9638                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9639                 if(attr=="cls"){
9640                     el.className = o["cls"];
9641                 }else{
9642                     if(useSet) {
9643                         el.setAttribute(attr, o[attr]);
9644                     } else {
9645                         el[attr] = o[attr];
9646                     }
9647                 }
9648             }
9649             if(o.style){
9650                 Roo.DomHelper.applyStyles(el, o.style);
9651             }
9652             return this;
9653         },
9654
9655         /**
9656          * Convenience method for constructing a KeyMap
9657          * @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:
9658          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9659          * @param {Function} fn The function to call
9660          * @param {Object} scope (optional) The scope of the function
9661          * @return {Roo.KeyMap} The KeyMap created
9662          */
9663         addKeyListener : function(key, fn, scope){
9664             var config;
9665             if(typeof key != "object" || key instanceof Array){
9666                 config = {
9667                     key: key,
9668                     fn: fn,
9669                     scope: scope
9670                 };
9671             }else{
9672                 config = {
9673                     key : key.key,
9674                     shift : key.shift,
9675                     ctrl : key.ctrl,
9676                     alt : key.alt,
9677                     fn: fn,
9678                     scope: scope
9679                 };
9680             }
9681             return new Roo.KeyMap(this, config);
9682         },
9683
9684         /**
9685          * Creates a KeyMap for this element
9686          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9687          * @return {Roo.KeyMap} The KeyMap created
9688          */
9689         addKeyMap : function(config){
9690             return new Roo.KeyMap(this, config);
9691         },
9692
9693         /**
9694          * Returns true if this element is scrollable.
9695          * @return {Boolean}
9696          */
9697          isScrollable : function(){
9698             var dom = this.dom;
9699             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9700         },
9701
9702         /**
9703          * 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().
9704          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9705          * @param {Number} value The new scroll value
9706          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9707          * @return {Element} this
9708          */
9709
9710         scrollTo : function(side, value, animate){
9711             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9712             if(!animate || !A){
9713                 this.dom[prop] = value;
9714             }else{
9715                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9716                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9717             }
9718             return this;
9719         },
9720
9721         /**
9722          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9723          * within this element's scrollable range.
9724          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9725          * @param {Number} distance How far to scroll the element in pixels
9726          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9727          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9728          * was scrolled as far as it could go.
9729          */
9730          scroll : function(direction, distance, animate){
9731              if(!this.isScrollable()){
9732                  return;
9733              }
9734              var el = this.dom;
9735              var l = el.scrollLeft, t = el.scrollTop;
9736              var w = el.scrollWidth, h = el.scrollHeight;
9737              var cw = el.clientWidth, ch = el.clientHeight;
9738              direction = direction.toLowerCase();
9739              var scrolled = false;
9740              var a = this.preanim(arguments, 2);
9741              switch(direction){
9742                  case "l":
9743                  case "left":
9744                      if(w - l > cw){
9745                          var v = Math.min(l + distance, w-cw);
9746                          this.scrollTo("left", v, a);
9747                          scrolled = true;
9748                      }
9749                      break;
9750                 case "r":
9751                 case "right":
9752                      if(l > 0){
9753                          var v = Math.max(l - distance, 0);
9754                          this.scrollTo("left", v, a);
9755                          scrolled = true;
9756                      }
9757                      break;
9758                 case "t":
9759                 case "top":
9760                 case "up":
9761                      if(t > 0){
9762                          var v = Math.max(t - distance, 0);
9763                          this.scrollTo("top", v, a);
9764                          scrolled = true;
9765                      }
9766                      break;
9767                 case "b":
9768                 case "bottom":
9769                 case "down":
9770                      if(h - t > ch){
9771                          var v = Math.min(t + distance, h-ch);
9772                          this.scrollTo("top", v, a);
9773                          scrolled = true;
9774                      }
9775                      break;
9776              }
9777              return scrolled;
9778         },
9779
9780         /**
9781          * Translates the passed page coordinates into left/top css values for this element
9782          * @param {Number/Array} x The page x or an array containing [x, y]
9783          * @param {Number} y The page y
9784          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9785          */
9786         translatePoints : function(x, y){
9787             if(typeof x == 'object' || x instanceof Array){
9788                 y = x[1]; x = x[0];
9789             }
9790             var p = this.getStyle('position');
9791             var o = this.getXY();
9792
9793             var l = parseInt(this.getStyle('left'), 10);
9794             var t = parseInt(this.getStyle('top'), 10);
9795
9796             if(isNaN(l)){
9797                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9798             }
9799             if(isNaN(t)){
9800                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9801             }
9802
9803             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9804         },
9805
9806         /**
9807          * Returns the current scroll position of the element.
9808          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9809          */
9810         getScroll : function(){
9811             var d = this.dom, doc = document;
9812             if(d == doc || d == doc.body){
9813                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9814                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9815                 return {left: l, top: t};
9816             }else{
9817                 return {left: d.scrollLeft, top: d.scrollTop};
9818             }
9819         },
9820
9821         /**
9822          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9823          * are convert to standard 6 digit hex color.
9824          * @param {String} attr The css attribute
9825          * @param {String} defaultValue The default value to use when a valid color isn't found
9826          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9827          * YUI color anims.
9828          */
9829         getColor : function(attr, defaultValue, prefix){
9830             var v = this.getStyle(attr);
9831             if(!v || v == "transparent" || v == "inherit") {
9832                 return defaultValue;
9833             }
9834             var color = typeof prefix == "undefined" ? "#" : prefix;
9835             if(v.substr(0, 4) == "rgb("){
9836                 var rvs = v.slice(4, v.length -1).split(",");
9837                 for(var i = 0; i < 3; i++){
9838                     var h = parseInt(rvs[i]).toString(16);
9839                     if(h < 16){
9840                         h = "0" + h;
9841                     }
9842                     color += h;
9843                 }
9844             } else {
9845                 if(v.substr(0, 1) == "#"){
9846                     if(v.length == 4) {
9847                         for(var i = 1; i < 4; i++){
9848                             var c = v.charAt(i);
9849                             color +=  c + c;
9850                         }
9851                     }else if(v.length == 7){
9852                         color += v.substr(1);
9853                     }
9854                 }
9855             }
9856             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9857         },
9858
9859         /**
9860          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9861          * gradient background, rounded corners and a 4-way shadow.
9862          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9863          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9864          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9865          * @return {Roo.Element} this
9866          */
9867         boxWrap : function(cls){
9868             cls = cls || 'x-box';
9869             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9870             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9871             return el;
9872         },
9873
9874         /**
9875          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9876          * @param {String} namespace The namespace in which to look for the attribute
9877          * @param {String} name The attribute name
9878          * @return {String} The attribute value
9879          */
9880         getAttributeNS : Roo.isIE ? function(ns, name){
9881             var d = this.dom;
9882             var type = typeof d[ns+":"+name];
9883             if(type != 'undefined' && type != 'unknown'){
9884                 return d[ns+":"+name];
9885             }
9886             return d[name];
9887         } : function(ns, name){
9888             var d = this.dom;
9889             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9890         },
9891         
9892         
9893         /**
9894          * Sets or Returns the value the dom attribute value
9895          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9896          * @param {String} value (optional) The value to set the attribute to
9897          * @return {String} The attribute value
9898          */
9899         attr : function(name){
9900             if (arguments.length > 1) {
9901                 this.dom.setAttribute(name, arguments[1]);
9902                 return arguments[1];
9903             }
9904             if (typeof(name) == 'object') {
9905                 for(var i in name) {
9906                     this.attr(i, name[i]);
9907                 }
9908                 return name;
9909             }
9910             
9911             
9912             if (!this.dom.hasAttribute(name)) {
9913                 return undefined;
9914             }
9915             return this.dom.getAttribute(name);
9916         }
9917         
9918         
9919         
9920     };
9921
9922     var ep = El.prototype;
9923
9924     /**
9925      * Appends an event handler (Shorthand for addListener)
9926      * @param {String}   eventName     The type of event to append
9927      * @param {Function} fn        The method the event invokes
9928      * @param {Object} scope       (optional) The scope (this object) of the fn
9929      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9930      * @method
9931      */
9932     ep.on = ep.addListener;
9933         // backwards compat
9934     ep.mon = ep.addListener;
9935
9936     /**
9937      * Removes an event handler from this element (shorthand for removeListener)
9938      * @param {String} eventName the type of event to remove
9939      * @param {Function} fn the method the event invokes
9940      * @return {Roo.Element} this
9941      * @method
9942      */
9943     ep.un = ep.removeListener;
9944
9945     /**
9946      * true to automatically adjust width and height settings for box-model issues (default to true)
9947      */
9948     ep.autoBoxAdjust = true;
9949
9950     // private
9951     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9952
9953     // private
9954     El.addUnits = function(v, defaultUnit){
9955         if(v === "" || v == "auto"){
9956             return v;
9957         }
9958         if(v === undefined){
9959             return '';
9960         }
9961         if(typeof v == "number" || !El.unitPattern.test(v)){
9962             return v + (defaultUnit || 'px');
9963         }
9964         return v;
9965     };
9966
9967     // special markup used throughout Roo when box wrapping elements
9968     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>';
9969     /**
9970      * Visibility mode constant - Use visibility to hide element
9971      * @static
9972      * @type Number
9973      */
9974     El.VISIBILITY = 1;
9975     /**
9976      * Visibility mode constant - Use display to hide element
9977      * @static
9978      * @type Number
9979      */
9980     El.DISPLAY = 2;
9981
9982     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9983     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9984     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9985
9986
9987
9988     /**
9989      * @private
9990      */
9991     El.cache = {};
9992
9993     var docEl;
9994
9995     /**
9996      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9997      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9998      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9999      * @return {Element} The Element object
10000      * @static
10001      */
10002     El.get = function(el){
10003         var ex, elm, id;
10004         if(!el){ return null; }
10005         if(typeof el == "string"){ // element id
10006             if(!(elm = document.getElementById(el))){
10007                 return null;
10008             }
10009             if(ex = El.cache[el]){
10010                 ex.dom = elm;
10011             }else{
10012                 ex = El.cache[el] = new El(elm);
10013             }
10014             return ex;
10015         }else if(el.tagName){ // dom element
10016             if(!(id = el.id)){
10017                 id = Roo.id(el);
10018             }
10019             if(ex = El.cache[id]){
10020                 ex.dom = el;
10021             }else{
10022                 ex = El.cache[id] = new El(el);
10023             }
10024             return ex;
10025         }else if(el instanceof El){
10026             if(el != docEl){
10027                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10028                                                               // catch case where it hasn't been appended
10029                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10030             }
10031             return el;
10032         }else if(el.isComposite){
10033             return el;
10034         }else if(el instanceof Array){
10035             return El.select(el);
10036         }else if(el == document){
10037             // create a bogus element object representing the document object
10038             if(!docEl){
10039                 var f = function(){};
10040                 f.prototype = El.prototype;
10041                 docEl = new f();
10042                 docEl.dom = document;
10043             }
10044             return docEl;
10045         }
10046         return null;
10047     };
10048
10049     // private
10050     El.uncache = function(el){
10051         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10052             if(a[i]){
10053                 delete El.cache[a[i].id || a[i]];
10054             }
10055         }
10056     };
10057
10058     // private
10059     // Garbage collection - uncache elements/purge listeners on orphaned elements
10060     // so we don't hold a reference and cause the browser to retain them
10061     El.garbageCollect = function(){
10062         if(!Roo.enableGarbageCollector){
10063             clearInterval(El.collectorThread);
10064             return;
10065         }
10066         for(var eid in El.cache){
10067             var el = El.cache[eid], d = el.dom;
10068             // -------------------------------------------------------
10069             // Determining what is garbage:
10070             // -------------------------------------------------------
10071             // !d
10072             // dom node is null, definitely garbage
10073             // -------------------------------------------------------
10074             // !d.parentNode
10075             // no parentNode == direct orphan, definitely garbage
10076             // -------------------------------------------------------
10077             // !d.offsetParent && !document.getElementById(eid)
10078             // display none elements have no offsetParent so we will
10079             // also try to look it up by it's id. However, check
10080             // offsetParent first so we don't do unneeded lookups.
10081             // This enables collection of elements that are not orphans
10082             // directly, but somewhere up the line they have an orphan
10083             // parent.
10084             // -------------------------------------------------------
10085             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10086                 delete El.cache[eid];
10087                 if(d && Roo.enableListenerCollection){
10088                     E.purgeElement(d);
10089                 }
10090             }
10091         }
10092     }
10093     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10094
10095
10096     // dom is optional
10097     El.Flyweight = function(dom){
10098         this.dom = dom;
10099     };
10100     El.Flyweight.prototype = El.prototype;
10101
10102     El._flyweights = {};
10103     /**
10104      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10105      * the dom node can be overwritten by other code.
10106      * @param {String/HTMLElement} el The dom node or id
10107      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10108      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10109      * @static
10110      * @return {Element} The shared Element object
10111      */
10112     El.fly = function(el, named){
10113         named = named || '_global';
10114         el = Roo.getDom(el);
10115         if(!el){
10116             return null;
10117         }
10118         if(!El._flyweights[named]){
10119             El._flyweights[named] = new El.Flyweight();
10120         }
10121         El._flyweights[named].dom = el;
10122         return El._flyweights[named];
10123     };
10124
10125     /**
10126      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10127      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10128      * Shorthand of {@link Roo.Element#get}
10129      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10130      * @return {Element} The Element object
10131      * @member Roo
10132      * @method get
10133      */
10134     Roo.get = El.get;
10135     /**
10136      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10137      * the dom node can be overwritten by other code.
10138      * Shorthand of {@link Roo.Element#fly}
10139      * @param {String/HTMLElement} el The dom node or id
10140      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10141      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10142      * @static
10143      * @return {Element} The shared Element object
10144      * @member Roo
10145      * @method fly
10146      */
10147     Roo.fly = El.fly;
10148
10149     // speedy lookup for elements never to box adjust
10150     var noBoxAdjust = Roo.isStrict ? {
10151         select:1
10152     } : {
10153         input:1, select:1, textarea:1
10154     };
10155     if(Roo.isIE || Roo.isGecko){
10156         noBoxAdjust['button'] = 1;
10157     }
10158
10159
10160     Roo.EventManager.on(window, 'unload', function(){
10161         delete El.cache;
10162         delete El._flyweights;
10163     });
10164 })();
10165
10166
10167
10168
10169 if(Roo.DomQuery){
10170     Roo.Element.selectorFunction = Roo.DomQuery.select;
10171 }
10172
10173 Roo.Element.select = function(selector, unique, root){
10174     var els;
10175     if(typeof selector == "string"){
10176         els = Roo.Element.selectorFunction(selector, root);
10177     }else if(selector.length !== undefined){
10178         els = selector;
10179     }else{
10180         throw "Invalid selector";
10181     }
10182     if(unique === true){
10183         return new Roo.CompositeElement(els);
10184     }else{
10185         return new Roo.CompositeElementLite(els);
10186     }
10187 };
10188 /**
10189  * Selects elements based on the passed CSS selector to enable working on them as 1.
10190  * @param {String/Array} selector The CSS selector or an array of elements
10191  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10192  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10193  * @return {CompositeElementLite/CompositeElement}
10194  * @member Roo
10195  * @method select
10196  */
10197 Roo.select = Roo.Element.select;
10198
10199
10200
10201
10202
10203
10204
10205
10206
10207
10208
10209
10210
10211
10212 /*
10213  * Based on:
10214  * Ext JS Library 1.1.1
10215  * Copyright(c) 2006-2007, Ext JS, LLC.
10216  *
10217  * Originally Released Under LGPL - original licence link has changed is not relivant.
10218  *
10219  * Fork - LGPL
10220  * <script type="text/javascript">
10221  */
10222
10223
10224
10225 //Notifies Element that fx methods are available
10226 Roo.enableFx = true;
10227
10228 /**
10229  * @class Roo.Fx
10230  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10231  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10232  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10233  * Element effects to work.</p><br/>
10234  *
10235  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10236  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10237  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10238  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10239  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10240  * expected results and should be done with care.</p><br/>
10241  *
10242  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10243  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10244 <pre>
10245 Value  Description
10246 -----  -----------------------------
10247 tl     The top left corner
10248 t      The center of the top edge
10249 tr     The top right corner
10250 l      The center of the left edge
10251 r      The center of the right edge
10252 bl     The bottom left corner
10253 b      The center of the bottom edge
10254 br     The bottom right corner
10255 </pre>
10256  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10257  * below are common options that can be passed to any Fx method.</b>
10258  * @cfg {Function} callback A function called when the effect is finished
10259  * @cfg {Object} scope The scope of the effect function
10260  * @cfg {String} easing A valid Easing value for the effect
10261  * @cfg {String} afterCls A css class to apply after the effect
10262  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10263  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10264  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10265  * effects that end with the element being visually hidden, ignored otherwise)
10266  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10267  * a function which returns such a specification that will be applied to the Element after the effect finishes
10268  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10269  * @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
10270  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10271  */
10272 Roo.Fx = {
10273         /**
10274          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10275          * origin for the slide effect.  This function automatically handles wrapping the element with
10276          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10277          * Usage:
10278          *<pre><code>
10279 // default: slide the element in from the top
10280 el.slideIn();
10281
10282 // custom: slide the element in from the right with a 2-second duration
10283 el.slideIn('r', { duration: 2 });
10284
10285 // common config options shown with default values
10286 el.slideIn('t', {
10287     easing: 'easeOut',
10288     duration: .5
10289 });
10290 </code></pre>
10291          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10292          * @param {Object} options (optional) Object literal with any of the Fx config options
10293          * @return {Roo.Element} The Element
10294          */
10295     slideIn : function(anchor, o){
10296         var el = this.getFxEl();
10297         o = o || {};
10298
10299         el.queueFx(o, function(){
10300
10301             anchor = anchor || "t";
10302
10303             // fix display to visibility
10304             this.fixDisplay();
10305
10306             // restore values after effect
10307             var r = this.getFxRestore();
10308             var b = this.getBox();
10309             // fixed size for slide
10310             this.setSize(b);
10311
10312             // wrap if needed
10313             var wrap = this.fxWrap(r.pos, o, "hidden");
10314
10315             var st = this.dom.style;
10316             st.visibility = "visible";
10317             st.position = "absolute";
10318
10319             // clear out temp styles after slide and unwrap
10320             var after = function(){
10321                 el.fxUnwrap(wrap, r.pos, o);
10322                 st.width = r.width;
10323                 st.height = r.height;
10324                 el.afterFx(o);
10325             };
10326             // time to calc the positions
10327             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10328
10329             switch(anchor.toLowerCase()){
10330                 case "t":
10331                     wrap.setSize(b.width, 0);
10332                     st.left = st.bottom = "0";
10333                     a = {height: bh};
10334                 break;
10335                 case "l":
10336                     wrap.setSize(0, b.height);
10337                     st.right = st.top = "0";
10338                     a = {width: bw};
10339                 break;
10340                 case "r":
10341                     wrap.setSize(0, b.height);
10342                     wrap.setX(b.right);
10343                     st.left = st.top = "0";
10344                     a = {width: bw, points: pt};
10345                 break;
10346                 case "b":
10347                     wrap.setSize(b.width, 0);
10348                     wrap.setY(b.bottom);
10349                     st.left = st.top = "0";
10350                     a = {height: bh, points: pt};
10351                 break;
10352                 case "tl":
10353                     wrap.setSize(0, 0);
10354                     st.right = st.bottom = "0";
10355                     a = {width: bw, height: bh};
10356                 break;
10357                 case "bl":
10358                     wrap.setSize(0, 0);
10359                     wrap.setY(b.y+b.height);
10360                     st.right = st.top = "0";
10361                     a = {width: bw, height: bh, points: pt};
10362                 break;
10363                 case "br":
10364                     wrap.setSize(0, 0);
10365                     wrap.setXY([b.right, b.bottom]);
10366                     st.left = st.top = "0";
10367                     a = {width: bw, height: bh, points: pt};
10368                 break;
10369                 case "tr":
10370                     wrap.setSize(0, 0);
10371                     wrap.setX(b.x+b.width);
10372                     st.left = st.bottom = "0";
10373                     a = {width: bw, height: bh, points: pt};
10374                 break;
10375             }
10376             this.dom.style.visibility = "visible";
10377             wrap.show();
10378
10379             arguments.callee.anim = wrap.fxanim(a,
10380                 o,
10381                 'motion',
10382                 .5,
10383                 'easeOut', after);
10384         });
10385         return this;
10386     },
10387     
10388         /**
10389          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10390          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10391          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10392          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10393          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10394          * Usage:
10395          *<pre><code>
10396 // default: slide the element out to the top
10397 el.slideOut();
10398
10399 // custom: slide the element out to the right with a 2-second duration
10400 el.slideOut('r', { duration: 2 });
10401
10402 // common config options shown with default values
10403 el.slideOut('t', {
10404     easing: 'easeOut',
10405     duration: .5,
10406     remove: false,
10407     useDisplay: false
10408 });
10409 </code></pre>
10410          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10411          * @param {Object} options (optional) Object literal with any of the Fx config options
10412          * @return {Roo.Element} The Element
10413          */
10414     slideOut : function(anchor, o){
10415         var el = this.getFxEl();
10416         o = o || {};
10417
10418         el.queueFx(o, function(){
10419
10420             anchor = anchor || "t";
10421
10422             // restore values after effect
10423             var r = this.getFxRestore();
10424             
10425             var b = this.getBox();
10426             // fixed size for slide
10427             this.setSize(b);
10428
10429             // wrap if needed
10430             var wrap = this.fxWrap(r.pos, o, "visible");
10431
10432             var st = this.dom.style;
10433             st.visibility = "visible";
10434             st.position = "absolute";
10435
10436             wrap.setSize(b);
10437
10438             var after = function(){
10439                 if(o.useDisplay){
10440                     el.setDisplayed(false);
10441                 }else{
10442                     el.hide();
10443                 }
10444
10445                 el.fxUnwrap(wrap, r.pos, o);
10446
10447                 st.width = r.width;
10448                 st.height = r.height;
10449
10450                 el.afterFx(o);
10451             };
10452
10453             var a, zero = {to: 0};
10454             switch(anchor.toLowerCase()){
10455                 case "t":
10456                     st.left = st.bottom = "0";
10457                     a = {height: zero};
10458                 break;
10459                 case "l":
10460                     st.right = st.top = "0";
10461                     a = {width: zero};
10462                 break;
10463                 case "r":
10464                     st.left = st.top = "0";
10465                     a = {width: zero, points: {to:[b.right, b.y]}};
10466                 break;
10467                 case "b":
10468                     st.left = st.top = "0";
10469                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10470                 break;
10471                 case "tl":
10472                     st.right = st.bottom = "0";
10473                     a = {width: zero, height: zero};
10474                 break;
10475                 case "bl":
10476                     st.right = st.top = "0";
10477                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10478                 break;
10479                 case "br":
10480                     st.left = st.top = "0";
10481                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10482                 break;
10483                 case "tr":
10484                     st.left = st.bottom = "0";
10485                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10486                 break;
10487             }
10488
10489             arguments.callee.anim = wrap.fxanim(a,
10490                 o,
10491                 'motion',
10492                 .5,
10493                 "easeOut", after);
10494         });
10495         return this;
10496     },
10497
10498         /**
10499          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10500          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10501          * The element must be removed from the DOM using the 'remove' config option if desired.
10502          * Usage:
10503          *<pre><code>
10504 // default
10505 el.puff();
10506
10507 // common config options shown with default values
10508 el.puff({
10509     easing: 'easeOut',
10510     duration: .5,
10511     remove: false,
10512     useDisplay: false
10513 });
10514 </code></pre>
10515          * @param {Object} options (optional) Object literal with any of the Fx config options
10516          * @return {Roo.Element} The Element
10517          */
10518     puff : function(o){
10519         var el = this.getFxEl();
10520         o = o || {};
10521
10522         el.queueFx(o, function(){
10523             this.clearOpacity();
10524             this.show();
10525
10526             // restore values after effect
10527             var r = this.getFxRestore();
10528             var st = this.dom.style;
10529
10530             var after = function(){
10531                 if(o.useDisplay){
10532                     el.setDisplayed(false);
10533                 }else{
10534                     el.hide();
10535                 }
10536
10537                 el.clearOpacity();
10538
10539                 el.setPositioning(r.pos);
10540                 st.width = r.width;
10541                 st.height = r.height;
10542                 st.fontSize = '';
10543                 el.afterFx(o);
10544             };
10545
10546             var width = this.getWidth();
10547             var height = this.getHeight();
10548
10549             arguments.callee.anim = this.fxanim({
10550                     width : {to: this.adjustWidth(width * 2)},
10551                     height : {to: this.adjustHeight(height * 2)},
10552                     points : {by: [-(width * .5), -(height * .5)]},
10553                     opacity : {to: 0},
10554                     fontSize: {to:200, unit: "%"}
10555                 },
10556                 o,
10557                 'motion',
10558                 .5,
10559                 "easeOut", after);
10560         });
10561         return this;
10562     },
10563
10564         /**
10565          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10566          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10567          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10568          * Usage:
10569          *<pre><code>
10570 // default
10571 el.switchOff();
10572
10573 // all config options shown with default values
10574 el.switchOff({
10575     easing: 'easeIn',
10576     duration: .3,
10577     remove: false,
10578     useDisplay: false
10579 });
10580 </code></pre>
10581          * @param {Object} options (optional) Object literal with any of the Fx config options
10582          * @return {Roo.Element} The Element
10583          */
10584     switchOff : function(o){
10585         var el = this.getFxEl();
10586         o = o || {};
10587
10588         el.queueFx(o, function(){
10589             this.clearOpacity();
10590             this.clip();
10591
10592             // restore values after effect
10593             var r = this.getFxRestore();
10594             var st = this.dom.style;
10595
10596             var after = function(){
10597                 if(o.useDisplay){
10598                     el.setDisplayed(false);
10599                 }else{
10600                     el.hide();
10601                 }
10602
10603                 el.clearOpacity();
10604                 el.setPositioning(r.pos);
10605                 st.width = r.width;
10606                 st.height = r.height;
10607
10608                 el.afterFx(o);
10609             };
10610
10611             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10612                 this.clearOpacity();
10613                 (function(){
10614                     this.fxanim({
10615                         height:{to:1},
10616                         points:{by:[0, this.getHeight() * .5]}
10617                     }, o, 'motion', 0.3, 'easeIn', after);
10618                 }).defer(100, this);
10619             });
10620         });
10621         return this;
10622     },
10623
10624     /**
10625      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10626      * changed using the "attr" config option) and then fading back to the original color. If no original
10627      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10628      * Usage:
10629 <pre><code>
10630 // default: highlight background to yellow
10631 el.highlight();
10632
10633 // custom: highlight foreground text to blue for 2 seconds
10634 el.highlight("0000ff", { attr: 'color', duration: 2 });
10635
10636 // common config options shown with default values
10637 el.highlight("ffff9c", {
10638     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10639     endColor: (current color) or "ffffff",
10640     easing: 'easeIn',
10641     duration: 1
10642 });
10643 </code></pre>
10644      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10645      * @param {Object} options (optional) Object literal with any of the Fx config options
10646      * @return {Roo.Element} The Element
10647      */ 
10648     highlight : function(color, o){
10649         var el = this.getFxEl();
10650         o = o || {};
10651
10652         el.queueFx(o, function(){
10653             color = color || "ffff9c";
10654             attr = o.attr || "backgroundColor";
10655
10656             this.clearOpacity();
10657             this.show();
10658
10659             var origColor = this.getColor(attr);
10660             var restoreColor = this.dom.style[attr];
10661             endColor = (o.endColor || origColor) || "ffffff";
10662
10663             var after = function(){
10664                 el.dom.style[attr] = restoreColor;
10665                 el.afterFx(o);
10666             };
10667
10668             var a = {};
10669             a[attr] = {from: color, to: endColor};
10670             arguments.callee.anim = this.fxanim(a,
10671                 o,
10672                 'color',
10673                 1,
10674                 'easeIn', after);
10675         });
10676         return this;
10677     },
10678
10679    /**
10680     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10681     * Usage:
10682 <pre><code>
10683 // default: a single light blue ripple
10684 el.frame();
10685
10686 // custom: 3 red ripples lasting 3 seconds total
10687 el.frame("ff0000", 3, { duration: 3 });
10688
10689 // common config options shown with default values
10690 el.frame("C3DAF9", 1, {
10691     duration: 1 //duration of entire animation (not each individual ripple)
10692     // Note: Easing is not configurable and will be ignored if included
10693 });
10694 </code></pre>
10695     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10696     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10697     * @param {Object} options (optional) Object literal with any of the Fx config options
10698     * @return {Roo.Element} The Element
10699     */
10700     frame : function(color, count, o){
10701         var el = this.getFxEl();
10702         o = o || {};
10703
10704         el.queueFx(o, function(){
10705             color = color || "#C3DAF9";
10706             if(color.length == 6){
10707                 color = "#" + color;
10708             }
10709             count = count || 1;
10710             duration = o.duration || 1;
10711             this.show();
10712
10713             var b = this.getBox();
10714             var animFn = function(){
10715                 var proxy = this.createProxy({
10716
10717                      style:{
10718                         visbility:"hidden",
10719                         position:"absolute",
10720                         "z-index":"35000", // yee haw
10721                         border:"0px solid " + color
10722                      }
10723                   });
10724                 var scale = Roo.isBorderBox ? 2 : 1;
10725                 proxy.animate({
10726                     top:{from:b.y, to:b.y - 20},
10727                     left:{from:b.x, to:b.x - 20},
10728                     borderWidth:{from:0, to:10},
10729                     opacity:{from:1, to:0},
10730                     height:{from:b.height, to:(b.height + (20*scale))},
10731                     width:{from:b.width, to:(b.width + (20*scale))}
10732                 }, duration, function(){
10733                     proxy.remove();
10734                 });
10735                 if(--count > 0){
10736                      animFn.defer((duration/2)*1000, this);
10737                 }else{
10738                     el.afterFx(o);
10739                 }
10740             };
10741             animFn.call(this);
10742         });
10743         return this;
10744     },
10745
10746    /**
10747     * Creates a pause before any subsequent queued effects begin.  If there are
10748     * no effects queued after the pause it will have no effect.
10749     * Usage:
10750 <pre><code>
10751 el.pause(1);
10752 </code></pre>
10753     * @param {Number} seconds The length of time to pause (in seconds)
10754     * @return {Roo.Element} The Element
10755     */
10756     pause : function(seconds){
10757         var el = this.getFxEl();
10758         var o = {};
10759
10760         el.queueFx(o, function(){
10761             setTimeout(function(){
10762                 el.afterFx(o);
10763             }, seconds * 1000);
10764         });
10765         return this;
10766     },
10767
10768    /**
10769     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10770     * using the "endOpacity" config option.
10771     * Usage:
10772 <pre><code>
10773 // default: fade in from opacity 0 to 100%
10774 el.fadeIn();
10775
10776 // custom: fade in from opacity 0 to 75% over 2 seconds
10777 el.fadeIn({ endOpacity: .75, duration: 2});
10778
10779 // common config options shown with default values
10780 el.fadeIn({
10781     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10782     easing: 'easeOut',
10783     duration: .5
10784 });
10785 </code></pre>
10786     * @param {Object} options (optional) Object literal with any of the Fx config options
10787     * @return {Roo.Element} The Element
10788     */
10789     fadeIn : function(o){
10790         var el = this.getFxEl();
10791         o = o || {};
10792         el.queueFx(o, function(){
10793             this.setOpacity(0);
10794             this.fixDisplay();
10795             this.dom.style.visibility = 'visible';
10796             var to = o.endOpacity || 1;
10797             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10798                 o, null, .5, "easeOut", function(){
10799                 if(to == 1){
10800                     this.clearOpacity();
10801                 }
10802                 el.afterFx(o);
10803             });
10804         });
10805         return this;
10806     },
10807
10808    /**
10809     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10810     * using the "endOpacity" config option.
10811     * Usage:
10812 <pre><code>
10813 // default: fade out from the element's current opacity to 0
10814 el.fadeOut();
10815
10816 // custom: fade out from the element's current opacity to 25% over 2 seconds
10817 el.fadeOut({ endOpacity: .25, duration: 2});
10818
10819 // common config options shown with default values
10820 el.fadeOut({
10821     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10822     easing: 'easeOut',
10823     duration: .5
10824     remove: false,
10825     useDisplay: false
10826 });
10827 </code></pre>
10828     * @param {Object} options (optional) Object literal with any of the Fx config options
10829     * @return {Roo.Element} The Element
10830     */
10831     fadeOut : function(o){
10832         var el = this.getFxEl();
10833         o = o || {};
10834         el.queueFx(o, function(){
10835             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10836                 o, null, .5, "easeOut", function(){
10837                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10838                      this.dom.style.display = "none";
10839                 }else{
10840                      this.dom.style.visibility = "hidden";
10841                 }
10842                 this.clearOpacity();
10843                 el.afterFx(o);
10844             });
10845         });
10846         return this;
10847     },
10848
10849    /**
10850     * Animates the transition of an element's dimensions from a starting height/width
10851     * to an ending height/width.
10852     * Usage:
10853 <pre><code>
10854 // change height and width to 100x100 pixels
10855 el.scale(100, 100);
10856
10857 // common config options shown with default values.  The height and width will default to
10858 // the element's existing values if passed as null.
10859 el.scale(
10860     [element's width],
10861     [element's height], {
10862     easing: 'easeOut',
10863     duration: .35
10864 });
10865 </code></pre>
10866     * @param {Number} width  The new width (pass undefined to keep the original width)
10867     * @param {Number} height  The new height (pass undefined to keep the original height)
10868     * @param {Object} options (optional) Object literal with any of the Fx config options
10869     * @return {Roo.Element} The Element
10870     */
10871     scale : function(w, h, o){
10872         this.shift(Roo.apply({}, o, {
10873             width: w,
10874             height: h
10875         }));
10876         return this;
10877     },
10878
10879    /**
10880     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10881     * Any of these properties not specified in the config object will not be changed.  This effect 
10882     * requires that at least one new dimension, position or opacity setting must be passed in on
10883     * the config object in order for the function to have any effect.
10884     * Usage:
10885 <pre><code>
10886 // slide the element horizontally to x position 200 while changing the height and opacity
10887 el.shift({ x: 200, height: 50, opacity: .8 });
10888
10889 // common config options shown with default values.
10890 el.shift({
10891     width: [element's width],
10892     height: [element's height],
10893     x: [element's x position],
10894     y: [element's y position],
10895     opacity: [element's opacity],
10896     easing: 'easeOut',
10897     duration: .35
10898 });
10899 </code></pre>
10900     * @param {Object} options  Object literal with any of the Fx config options
10901     * @return {Roo.Element} The Element
10902     */
10903     shift : function(o){
10904         var el = this.getFxEl();
10905         o = o || {};
10906         el.queueFx(o, function(){
10907             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10908             if(w !== undefined){
10909                 a.width = {to: this.adjustWidth(w)};
10910             }
10911             if(h !== undefined){
10912                 a.height = {to: this.adjustHeight(h)};
10913             }
10914             if(x !== undefined || y !== undefined){
10915                 a.points = {to: [
10916                     x !== undefined ? x : this.getX(),
10917                     y !== undefined ? y : this.getY()
10918                 ]};
10919             }
10920             if(op !== undefined){
10921                 a.opacity = {to: op};
10922             }
10923             if(o.xy !== undefined){
10924                 a.points = {to: o.xy};
10925             }
10926             arguments.callee.anim = this.fxanim(a,
10927                 o, 'motion', .35, "easeOut", function(){
10928                 el.afterFx(o);
10929             });
10930         });
10931         return this;
10932     },
10933
10934         /**
10935          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10936          * ending point of the effect.
10937          * Usage:
10938          *<pre><code>
10939 // default: slide the element downward while fading out
10940 el.ghost();
10941
10942 // custom: slide the element out to the right with a 2-second duration
10943 el.ghost('r', { duration: 2 });
10944
10945 // common config options shown with default values
10946 el.ghost('b', {
10947     easing: 'easeOut',
10948     duration: .5
10949     remove: false,
10950     useDisplay: false
10951 });
10952 </code></pre>
10953          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10954          * @param {Object} options (optional) Object literal with any of the Fx config options
10955          * @return {Roo.Element} The Element
10956          */
10957     ghost : function(anchor, o){
10958         var el = this.getFxEl();
10959         o = o || {};
10960
10961         el.queueFx(o, function(){
10962             anchor = anchor || "b";
10963
10964             // restore values after effect
10965             var r = this.getFxRestore();
10966             var w = this.getWidth(),
10967                 h = this.getHeight();
10968
10969             var st = this.dom.style;
10970
10971             var after = function(){
10972                 if(o.useDisplay){
10973                     el.setDisplayed(false);
10974                 }else{
10975                     el.hide();
10976                 }
10977
10978                 el.clearOpacity();
10979                 el.setPositioning(r.pos);
10980                 st.width = r.width;
10981                 st.height = r.height;
10982
10983                 el.afterFx(o);
10984             };
10985
10986             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10987             switch(anchor.toLowerCase()){
10988                 case "t":
10989                     pt.by = [0, -h];
10990                 break;
10991                 case "l":
10992                     pt.by = [-w, 0];
10993                 break;
10994                 case "r":
10995                     pt.by = [w, 0];
10996                 break;
10997                 case "b":
10998                     pt.by = [0, h];
10999                 break;
11000                 case "tl":
11001                     pt.by = [-w, -h];
11002                 break;
11003                 case "bl":
11004                     pt.by = [-w, h];
11005                 break;
11006                 case "br":
11007                     pt.by = [w, h];
11008                 break;
11009                 case "tr":
11010                     pt.by = [w, -h];
11011                 break;
11012             }
11013
11014             arguments.callee.anim = this.fxanim(a,
11015                 o,
11016                 'motion',
11017                 .5,
11018                 "easeOut", after);
11019         });
11020         return this;
11021     },
11022
11023         /**
11024          * Ensures that all effects queued after syncFx is called on the element are
11025          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11026          * @return {Roo.Element} The Element
11027          */
11028     syncFx : function(){
11029         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11030             block : false,
11031             concurrent : true,
11032             stopFx : false
11033         });
11034         return this;
11035     },
11036
11037         /**
11038          * Ensures that all effects queued after sequenceFx is called on the element are
11039          * run in sequence.  This is the opposite of {@link #syncFx}.
11040          * @return {Roo.Element} The Element
11041          */
11042     sequenceFx : function(){
11043         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11044             block : false,
11045             concurrent : false,
11046             stopFx : false
11047         });
11048         return this;
11049     },
11050
11051         /* @private */
11052     nextFx : function(){
11053         var ef = this.fxQueue[0];
11054         if(ef){
11055             ef.call(this);
11056         }
11057     },
11058
11059         /**
11060          * Returns true if the element has any effects actively running or queued, else returns false.
11061          * @return {Boolean} True if element has active effects, else false
11062          */
11063     hasActiveFx : function(){
11064         return this.fxQueue && this.fxQueue[0];
11065     },
11066
11067         /**
11068          * Stops any running effects and clears the element's internal effects queue if it contains
11069          * any additional effects that haven't started yet.
11070          * @return {Roo.Element} The Element
11071          */
11072     stopFx : function(){
11073         if(this.hasActiveFx()){
11074             var cur = this.fxQueue[0];
11075             if(cur && cur.anim && cur.anim.isAnimated()){
11076                 this.fxQueue = [cur]; // clear out others
11077                 cur.anim.stop(true);
11078             }
11079         }
11080         return this;
11081     },
11082
11083         /* @private */
11084     beforeFx : function(o){
11085         if(this.hasActiveFx() && !o.concurrent){
11086            if(o.stopFx){
11087                this.stopFx();
11088                return true;
11089            }
11090            return false;
11091         }
11092         return true;
11093     },
11094
11095         /**
11096          * Returns true if the element is currently blocking so that no other effect can be queued
11097          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11098          * used to ensure that an effect initiated by a user action runs to completion prior to the
11099          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11100          * @return {Boolean} True if blocking, else false
11101          */
11102     hasFxBlock : function(){
11103         var q = this.fxQueue;
11104         return q && q[0] && q[0].block;
11105     },
11106
11107         /* @private */
11108     queueFx : function(o, fn){
11109         if(!this.fxQueue){
11110             this.fxQueue = [];
11111         }
11112         if(!this.hasFxBlock()){
11113             Roo.applyIf(o, this.fxDefaults);
11114             if(!o.concurrent){
11115                 var run = this.beforeFx(o);
11116                 fn.block = o.block;
11117                 this.fxQueue.push(fn);
11118                 if(run){
11119                     this.nextFx();
11120                 }
11121             }else{
11122                 fn.call(this);
11123             }
11124         }
11125         return this;
11126     },
11127
11128         /* @private */
11129     fxWrap : function(pos, o, vis){
11130         var wrap;
11131         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11132             var wrapXY;
11133             if(o.fixPosition){
11134                 wrapXY = this.getXY();
11135             }
11136             var div = document.createElement("div");
11137             div.style.visibility = vis;
11138             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11139             wrap.setPositioning(pos);
11140             if(wrap.getStyle("position") == "static"){
11141                 wrap.position("relative");
11142             }
11143             this.clearPositioning('auto');
11144             wrap.clip();
11145             wrap.dom.appendChild(this.dom);
11146             if(wrapXY){
11147                 wrap.setXY(wrapXY);
11148             }
11149         }
11150         return wrap;
11151     },
11152
11153         /* @private */
11154     fxUnwrap : function(wrap, pos, o){
11155         this.clearPositioning();
11156         this.setPositioning(pos);
11157         if(!o.wrap){
11158             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11159             wrap.remove();
11160         }
11161     },
11162
11163         /* @private */
11164     getFxRestore : function(){
11165         var st = this.dom.style;
11166         return {pos: this.getPositioning(), width: st.width, height : st.height};
11167     },
11168
11169         /* @private */
11170     afterFx : function(o){
11171         if(o.afterStyle){
11172             this.applyStyles(o.afterStyle);
11173         }
11174         if(o.afterCls){
11175             this.addClass(o.afterCls);
11176         }
11177         if(o.remove === true){
11178             this.remove();
11179         }
11180         Roo.callback(o.callback, o.scope, [this]);
11181         if(!o.concurrent){
11182             this.fxQueue.shift();
11183             this.nextFx();
11184         }
11185     },
11186
11187         /* @private */
11188     getFxEl : function(){ // support for composite element fx
11189         return Roo.get(this.dom);
11190     },
11191
11192         /* @private */
11193     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11194         animType = animType || 'run';
11195         opt = opt || {};
11196         var anim = Roo.lib.Anim[animType](
11197             this.dom, args,
11198             (opt.duration || defaultDur) || .35,
11199             (opt.easing || defaultEase) || 'easeOut',
11200             function(){
11201                 Roo.callback(cb, this);
11202             },
11203             this
11204         );
11205         opt.anim = anim;
11206         return anim;
11207     }
11208 };
11209
11210 // backwords compat
11211 Roo.Fx.resize = Roo.Fx.scale;
11212
11213 //When included, Roo.Fx is automatically applied to Element so that all basic
11214 //effects are available directly via the Element API
11215 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11216  * Based on:
11217  * Ext JS Library 1.1.1
11218  * Copyright(c) 2006-2007, Ext JS, LLC.
11219  *
11220  * Originally Released Under LGPL - original licence link has changed is not relivant.
11221  *
11222  * Fork - LGPL
11223  * <script type="text/javascript">
11224  */
11225
11226
11227 /**
11228  * @class Roo.CompositeElement
11229  * Standard composite class. Creates a Roo.Element for every element in the collection.
11230  * <br><br>
11231  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11232  * actions will be performed on all the elements in this collection.</b>
11233  * <br><br>
11234  * All methods return <i>this</i> and can be chained.
11235  <pre><code>
11236  var els = Roo.select("#some-el div.some-class", true);
11237  // or select directly from an existing element
11238  var el = Roo.get('some-el');
11239  el.select('div.some-class', true);
11240
11241  els.setWidth(100); // all elements become 100 width
11242  els.hide(true); // all elements fade out and hide
11243  // or
11244  els.setWidth(100).hide(true);
11245  </code></pre>
11246  */
11247 Roo.CompositeElement = function(els){
11248     this.elements = [];
11249     this.addElements(els);
11250 };
11251 Roo.CompositeElement.prototype = {
11252     isComposite: true,
11253     addElements : function(els){
11254         if(!els) {
11255             return this;
11256         }
11257         if(typeof els == "string"){
11258             els = Roo.Element.selectorFunction(els);
11259         }
11260         var yels = this.elements;
11261         var index = yels.length-1;
11262         for(var i = 0, len = els.length; i < len; i++) {
11263                 yels[++index] = Roo.get(els[i]);
11264         }
11265         return this;
11266     },
11267
11268     /**
11269     * Clears this composite and adds the elements returned by the passed selector.
11270     * @param {String/Array} els A string CSS selector, an array of elements or an element
11271     * @return {CompositeElement} this
11272     */
11273     fill : function(els){
11274         this.elements = [];
11275         this.add(els);
11276         return this;
11277     },
11278
11279     /**
11280     * Filters this composite to only elements that match the passed selector.
11281     * @param {String} selector A string CSS selector
11282     * @param {Boolean} inverse return inverse filter (not matches)
11283     * @return {CompositeElement} this
11284     */
11285     filter : function(selector, inverse){
11286         var els = [];
11287         inverse = inverse || false;
11288         this.each(function(el){
11289             var match = inverse ? !el.is(selector) : el.is(selector);
11290             if(match){
11291                 els[els.length] = el.dom;
11292             }
11293         });
11294         this.fill(els);
11295         return this;
11296     },
11297
11298     invoke : function(fn, args){
11299         var els = this.elements;
11300         for(var i = 0, len = els.length; i < len; i++) {
11301                 Roo.Element.prototype[fn].apply(els[i], args);
11302         }
11303         return this;
11304     },
11305     /**
11306     * Adds elements to this composite.
11307     * @param {String/Array} els A string CSS selector, an array of elements or an element
11308     * @return {CompositeElement} this
11309     */
11310     add : function(els){
11311         if(typeof els == "string"){
11312             this.addElements(Roo.Element.selectorFunction(els));
11313         }else if(els.length !== undefined){
11314             this.addElements(els);
11315         }else{
11316             this.addElements([els]);
11317         }
11318         return this;
11319     },
11320     /**
11321     * Calls the passed function passing (el, this, index) for each element in this composite.
11322     * @param {Function} fn The function to call
11323     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11324     * @return {CompositeElement} this
11325     */
11326     each : function(fn, scope){
11327         var els = this.elements;
11328         for(var i = 0, len = els.length; i < len; i++){
11329             if(fn.call(scope || els[i], els[i], this, i) === false) {
11330                 break;
11331             }
11332         }
11333         return this;
11334     },
11335
11336     /**
11337      * Returns the Element object at the specified index
11338      * @param {Number} index
11339      * @return {Roo.Element}
11340      */
11341     item : function(index){
11342         return this.elements[index] || null;
11343     },
11344
11345     /**
11346      * Returns the first Element
11347      * @return {Roo.Element}
11348      */
11349     first : function(){
11350         return this.item(0);
11351     },
11352
11353     /**
11354      * Returns the last Element
11355      * @return {Roo.Element}
11356      */
11357     last : function(){
11358         return this.item(this.elements.length-1);
11359     },
11360
11361     /**
11362      * Returns the number of elements in this composite
11363      * @return Number
11364      */
11365     getCount : function(){
11366         return this.elements.length;
11367     },
11368
11369     /**
11370      * Returns true if this composite contains the passed element
11371      * @return Boolean
11372      */
11373     contains : function(el){
11374         return this.indexOf(el) !== -1;
11375     },
11376
11377     /**
11378      * Returns true if this composite contains the passed element
11379      * @return Boolean
11380      */
11381     indexOf : function(el){
11382         return this.elements.indexOf(Roo.get(el));
11383     },
11384
11385
11386     /**
11387     * Removes the specified element(s).
11388     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11389     * or an array of any of those.
11390     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11391     * @return {CompositeElement} this
11392     */
11393     removeElement : function(el, removeDom){
11394         if(el instanceof Array){
11395             for(var i = 0, len = el.length; i < len; i++){
11396                 this.removeElement(el[i]);
11397             }
11398             return this;
11399         }
11400         var index = typeof el == 'number' ? el : this.indexOf(el);
11401         if(index !== -1){
11402             if(removeDom){
11403                 var d = this.elements[index];
11404                 if(d.dom){
11405                     d.remove();
11406                 }else{
11407                     d.parentNode.removeChild(d);
11408                 }
11409             }
11410             this.elements.splice(index, 1);
11411         }
11412         return this;
11413     },
11414
11415     /**
11416     * Replaces the specified element with the passed element.
11417     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11418     * to replace.
11419     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11420     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11421     * @return {CompositeElement} this
11422     */
11423     replaceElement : function(el, replacement, domReplace){
11424         var index = typeof el == 'number' ? el : this.indexOf(el);
11425         if(index !== -1){
11426             if(domReplace){
11427                 this.elements[index].replaceWith(replacement);
11428             }else{
11429                 this.elements.splice(index, 1, Roo.get(replacement))
11430             }
11431         }
11432         return this;
11433     },
11434
11435     /**
11436      * Removes all elements.
11437      */
11438     clear : function(){
11439         this.elements = [];
11440     }
11441 };
11442 (function(){
11443     Roo.CompositeElement.createCall = function(proto, fnName){
11444         if(!proto[fnName]){
11445             proto[fnName] = function(){
11446                 return this.invoke(fnName, arguments);
11447             };
11448         }
11449     };
11450     for(var fnName in Roo.Element.prototype){
11451         if(typeof Roo.Element.prototype[fnName] == "function"){
11452             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11453         }
11454     };
11455 })();
11456 /*
11457  * Based on:
11458  * Ext JS Library 1.1.1
11459  * Copyright(c) 2006-2007, Ext JS, LLC.
11460  *
11461  * Originally Released Under LGPL - original licence link has changed is not relivant.
11462  *
11463  * Fork - LGPL
11464  * <script type="text/javascript">
11465  */
11466
11467 /**
11468  * @class Roo.CompositeElementLite
11469  * @extends Roo.CompositeElement
11470  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11471  <pre><code>
11472  var els = Roo.select("#some-el div.some-class");
11473  // or select directly from an existing element
11474  var el = Roo.get('some-el');
11475  el.select('div.some-class');
11476
11477  els.setWidth(100); // all elements become 100 width
11478  els.hide(true); // all elements fade out and hide
11479  // or
11480  els.setWidth(100).hide(true);
11481  </code></pre><br><br>
11482  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11483  * actions will be performed on all the elements in this collection.</b>
11484  */
11485 Roo.CompositeElementLite = function(els){
11486     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11487     this.el = new Roo.Element.Flyweight();
11488 };
11489 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11490     addElements : function(els){
11491         if(els){
11492             if(els instanceof Array){
11493                 this.elements = this.elements.concat(els);
11494             }else{
11495                 var yels = this.elements;
11496                 var index = yels.length-1;
11497                 for(var i = 0, len = els.length; i < len; i++) {
11498                     yels[++index] = els[i];
11499                 }
11500             }
11501         }
11502         return this;
11503     },
11504     invoke : function(fn, args){
11505         var els = this.elements;
11506         var el = this.el;
11507         for(var i = 0, len = els.length; i < len; i++) {
11508             el.dom = els[i];
11509                 Roo.Element.prototype[fn].apply(el, args);
11510         }
11511         return this;
11512     },
11513     /**
11514      * Returns a flyweight Element of the dom element object at the specified index
11515      * @param {Number} index
11516      * @return {Roo.Element}
11517      */
11518     item : function(index){
11519         if(!this.elements[index]){
11520             return null;
11521         }
11522         this.el.dom = this.elements[index];
11523         return this.el;
11524     },
11525
11526     // fixes scope with flyweight
11527     addListener : function(eventName, handler, scope, opt){
11528         var els = this.elements;
11529         for(var i = 0, len = els.length; i < len; i++) {
11530             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11531         }
11532         return this;
11533     },
11534
11535     /**
11536     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11537     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11538     * a reference to the dom node, use el.dom.</b>
11539     * @param {Function} fn The function to call
11540     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11541     * @return {CompositeElement} this
11542     */
11543     each : function(fn, scope){
11544         var els = this.elements;
11545         var el = this.el;
11546         for(var i = 0, len = els.length; i < len; i++){
11547             el.dom = els[i];
11548                 if(fn.call(scope || el, el, this, i) === false){
11549                 break;
11550             }
11551         }
11552         return this;
11553     },
11554
11555     indexOf : function(el){
11556         return this.elements.indexOf(Roo.getDom(el));
11557     },
11558
11559     replaceElement : function(el, replacement, domReplace){
11560         var index = typeof el == 'number' ? el : this.indexOf(el);
11561         if(index !== -1){
11562             replacement = Roo.getDom(replacement);
11563             if(domReplace){
11564                 var d = this.elements[index];
11565                 d.parentNode.insertBefore(replacement, d);
11566                 d.parentNode.removeChild(d);
11567             }
11568             this.elements.splice(index, 1, replacement);
11569         }
11570         return this;
11571     }
11572 });
11573 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11574
11575 /*
11576  * Based on:
11577  * Ext JS Library 1.1.1
11578  * Copyright(c) 2006-2007, Ext JS, LLC.
11579  *
11580  * Originally Released Under LGPL - original licence link has changed is not relivant.
11581  *
11582  * Fork - LGPL
11583  * <script type="text/javascript">
11584  */
11585
11586  
11587
11588 /**
11589  * @class Roo.data.Connection
11590  * @extends Roo.util.Observable
11591  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11592  * either to a configured URL, or to a URL specified at request time. 
11593  * 
11594  * Requests made by this class are asynchronous, and will return immediately. No data from
11595  * the server will be available to the statement immediately following the {@link #request} call.
11596  * To process returned data, use a callback in the request options object, or an event listener.
11597  * 
11598  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11599  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11600  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11601  * property and, if present, the IFRAME's XML document as the responseXML property.
11602  * 
11603  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11604  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11605  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11606  * standard DOM methods.
11607  * @constructor
11608  * @param {Object} config a configuration object.
11609  */
11610 Roo.data.Connection = function(config){
11611     Roo.apply(this, config);
11612     this.addEvents({
11613         /**
11614          * @event beforerequest
11615          * Fires before a network request is made to retrieve a data object.
11616          * @param {Connection} conn This Connection object.
11617          * @param {Object} options The options config object passed to the {@link #request} method.
11618          */
11619         "beforerequest" : true,
11620         /**
11621          * @event requestcomplete
11622          * Fires if the request was successfully completed.
11623          * @param {Connection} conn This Connection object.
11624          * @param {Object} response The XHR object containing the response data.
11625          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11626          * @param {Object} options The options config object passed to the {@link #request} method.
11627          */
11628         "requestcomplete" : true,
11629         /**
11630          * @event requestexception
11631          * Fires if an error HTTP status was returned from the server.
11632          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11633          * @param {Connection} conn This Connection object.
11634          * @param {Object} response The XHR object containing the response data.
11635          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11636          * @param {Object} options The options config object passed to the {@link #request} method.
11637          */
11638         "requestexception" : true
11639     });
11640     Roo.data.Connection.superclass.constructor.call(this);
11641 };
11642
11643 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11644     /**
11645      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11646      */
11647     /**
11648      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11649      * extra parameters to each request made by this object. (defaults to undefined)
11650      */
11651     /**
11652      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11653      *  to each request made by this object. (defaults to undefined)
11654      */
11655     /**
11656      * @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)
11657      */
11658     /**
11659      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11660      */
11661     timeout : 30000,
11662     /**
11663      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11664      * @type Boolean
11665      */
11666     autoAbort:false,
11667
11668     /**
11669      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11670      * @type Boolean
11671      */
11672     disableCaching: true,
11673
11674     /**
11675      * Sends an HTTP request to a remote server.
11676      * @param {Object} options An object which may contain the following properties:<ul>
11677      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11678      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11679      * request, a url encoded string or a function to call to get either.</li>
11680      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11681      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11682      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11683      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11684      * <li>options {Object} The parameter to the request call.</li>
11685      * <li>success {Boolean} True if the request succeeded.</li>
11686      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11687      * </ul></li>
11688      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11689      * The callback is passed the following parameters:<ul>
11690      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11691      * <li>options {Object} The parameter to the request call.</li>
11692      * </ul></li>
11693      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11694      * The callback is passed the following parameters:<ul>
11695      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11696      * <li>options {Object} The parameter to the request call.</li>
11697      * </ul></li>
11698      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11699      * for the callback function. Defaults to the browser window.</li>
11700      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11701      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11702      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11703      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11704      * params for the post data. Any params will be appended to the URL.</li>
11705      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11706      * </ul>
11707      * @return {Number} transactionId
11708      */
11709     request : function(o){
11710         if(this.fireEvent("beforerequest", this, o) !== false){
11711             var p = o.params;
11712
11713             if(typeof p == "function"){
11714                 p = p.call(o.scope||window, o);
11715             }
11716             if(typeof p == "object"){
11717                 p = Roo.urlEncode(o.params);
11718             }
11719             if(this.extraParams){
11720                 var extras = Roo.urlEncode(this.extraParams);
11721                 p = p ? (p + '&' + extras) : extras;
11722             }
11723
11724             var url = o.url || this.url;
11725             if(typeof url == 'function'){
11726                 url = url.call(o.scope||window, o);
11727             }
11728
11729             if(o.form){
11730                 var form = Roo.getDom(o.form);
11731                 url = url || form.action;
11732
11733                 var enctype = form.getAttribute("enctype");
11734                 
11735                 if (o.formData) {
11736                     return this.doFormDataUpload(o, url);
11737                 }
11738                 
11739                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11740                     return this.doFormUpload(o, p, url);
11741                 }
11742                 var f = Roo.lib.Ajax.serializeForm(form);
11743                 p = p ? (p + '&' + f) : f;
11744             }
11745             
11746             if (!o.form && o.formData) {
11747                 o.formData = o.formData === true ? new FormData() : o.formData;
11748                 for (var k in o.params) {
11749                     o.formData.append(k,o.params[k]);
11750                 }
11751                     
11752                 return this.doFormDataUpload(o, url);
11753             }
11754             
11755
11756             var hs = o.headers;
11757             if(this.defaultHeaders){
11758                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11759                 if(!o.headers){
11760                     o.headers = hs;
11761                 }
11762             }
11763
11764             var cb = {
11765                 success: this.handleResponse,
11766                 failure: this.handleFailure,
11767                 scope: this,
11768                 argument: {options: o},
11769                 timeout : o.timeout || this.timeout
11770             };
11771
11772             var method = o.method||this.method||(p ? "POST" : "GET");
11773
11774             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11775                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11776             }
11777
11778             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11779                 if(o.autoAbort){
11780                     this.abort();
11781                 }
11782             }else if(this.autoAbort !== false){
11783                 this.abort();
11784             }
11785
11786             if((method == 'GET' && p) || o.xmlData){
11787                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11788                 p = '';
11789             }
11790             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11791             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11792             Roo.lib.Ajax.useDefaultHeader == true;
11793             return this.transId;
11794         }else{
11795             Roo.callback(o.callback, o.scope, [o, null, null]);
11796             return null;
11797         }
11798     },
11799
11800     /**
11801      * Determine whether this object has a request outstanding.
11802      * @param {Number} transactionId (Optional) defaults to the last transaction
11803      * @return {Boolean} True if there is an outstanding request.
11804      */
11805     isLoading : function(transId){
11806         if(transId){
11807             return Roo.lib.Ajax.isCallInProgress(transId);
11808         }else{
11809             return this.transId ? true : false;
11810         }
11811     },
11812
11813     /**
11814      * Aborts any outstanding request.
11815      * @param {Number} transactionId (Optional) defaults to the last transaction
11816      */
11817     abort : function(transId){
11818         if(transId || this.isLoading()){
11819             Roo.lib.Ajax.abort(transId || this.transId);
11820         }
11821     },
11822
11823     // private
11824     handleResponse : function(response){
11825         this.transId = false;
11826         var options = response.argument.options;
11827         response.argument = options ? options.argument : null;
11828         this.fireEvent("requestcomplete", this, response, options);
11829         Roo.callback(options.success, options.scope, [response, options]);
11830         Roo.callback(options.callback, options.scope, [options, true, response]);
11831     },
11832
11833     // private
11834     handleFailure : function(response, e){
11835         this.transId = false;
11836         var options = response.argument.options;
11837         response.argument = options ? options.argument : null;
11838         this.fireEvent("requestexception", this, response, options, e);
11839         Roo.callback(options.failure, options.scope, [response, options]);
11840         Roo.callback(options.callback, options.scope, [options, false, response]);
11841     },
11842
11843     // private
11844     doFormUpload : function(o, ps, url){
11845         var id = Roo.id();
11846         var frame = document.createElement('iframe');
11847         frame.id = id;
11848         frame.name = id;
11849         frame.className = 'x-hidden';
11850         if(Roo.isIE){
11851             frame.src = Roo.SSL_SECURE_URL;
11852         }
11853         document.body.appendChild(frame);
11854
11855         if(Roo.isIE){
11856            document.frames[id].name = id;
11857         }
11858
11859         var form = Roo.getDom(o.form);
11860         form.target = id;
11861         form.method = 'POST';
11862         form.enctype = form.encoding = 'multipart/form-data';
11863         if(url){
11864             form.action = url;
11865         }
11866
11867         var hiddens, hd;
11868         if(ps){ // add dynamic params
11869             hiddens = [];
11870             ps = Roo.urlDecode(ps, false);
11871             for(var k in ps){
11872                 if(ps.hasOwnProperty(k)){
11873                     hd = document.createElement('input');
11874                     hd.type = 'hidden';
11875                     hd.name = k;
11876                     hd.value = ps[k];
11877                     form.appendChild(hd);
11878                     hiddens.push(hd);
11879                 }
11880             }
11881         }
11882
11883         function cb(){
11884             var r = {  // bogus response object
11885                 responseText : '',
11886                 responseXML : null
11887             };
11888
11889             r.argument = o ? o.argument : null;
11890
11891             try { //
11892                 var doc;
11893                 if(Roo.isIE){
11894                     doc = frame.contentWindow.document;
11895                 }else {
11896                     doc = (frame.contentDocument || window.frames[id].document);
11897                 }
11898                 if(doc && doc.body){
11899                     r.responseText = doc.body.innerHTML;
11900                 }
11901                 if(doc && doc.XMLDocument){
11902                     r.responseXML = doc.XMLDocument;
11903                 }else {
11904                     r.responseXML = doc;
11905                 }
11906             }
11907             catch(e) {
11908                 // ignore
11909             }
11910
11911             Roo.EventManager.removeListener(frame, 'load', cb, this);
11912
11913             this.fireEvent("requestcomplete", this, r, o);
11914             Roo.callback(o.success, o.scope, [r, o]);
11915             Roo.callback(o.callback, o.scope, [o, true, r]);
11916
11917             setTimeout(function(){document.body.removeChild(frame);}, 100);
11918         }
11919
11920         Roo.EventManager.on(frame, 'load', cb, this);
11921         form.submit();
11922
11923         if(hiddens){ // remove dynamic params
11924             for(var i = 0, len = hiddens.length; i < len; i++){
11925                 form.removeChild(hiddens[i]);
11926             }
11927         }
11928     },
11929     // this is a 'formdata version???'
11930     
11931     
11932     doFormDataUpload : function(o,  url)
11933     {
11934         var formData;
11935         if (o.form) {
11936             var form =  Roo.getDom(o.form);
11937             form.enctype = form.encoding = 'multipart/form-data';
11938             formData = o.formData === true ? new FormData(form) : o.formData;
11939         } else {
11940             formData = o.formData === true ? new FormData() : o.formData;
11941         }
11942         
11943       
11944         var cb = {
11945             success: this.handleResponse,
11946             failure: this.handleFailure,
11947             scope: this,
11948             argument: {options: o},
11949             timeout : o.timeout || this.timeout
11950         };
11951  
11952         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11953             if(o.autoAbort){
11954                 this.abort();
11955             }
11956         }else if(this.autoAbort !== false){
11957             this.abort();
11958         }
11959
11960         //Roo.lib.Ajax.defaultPostHeader = null;
11961         Roo.lib.Ajax.useDefaultHeader = false;
11962         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11963         Roo.lib.Ajax.useDefaultHeader = true;
11964  
11965          
11966     }
11967     
11968 });
11969 /*
11970  * Based on:
11971  * Ext JS Library 1.1.1
11972  * Copyright(c) 2006-2007, Ext JS, LLC.
11973  *
11974  * Originally Released Under LGPL - original licence link has changed is not relivant.
11975  *
11976  * Fork - LGPL
11977  * <script type="text/javascript">
11978  */
11979  
11980 /**
11981  * Global Ajax request class.
11982  * 
11983  * @class Roo.Ajax
11984  * @extends Roo.data.Connection
11985  * @static
11986  * 
11987  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11988  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11989  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11990  * @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)
11991  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11992  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11993  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11994  */
11995 Roo.Ajax = new Roo.data.Connection({
11996     // fix up the docs
11997     /**
11998      * @scope Roo.Ajax
11999      * @type {Boolear} 
12000      */
12001     autoAbort : false,
12002
12003     /**
12004      * Serialize the passed form into a url encoded string
12005      * @scope Roo.Ajax
12006      * @param {String/HTMLElement} form
12007      * @return {String}
12008      */
12009     serializeForm : function(form){
12010         return Roo.lib.Ajax.serializeForm(form);
12011     }
12012 });/*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022
12023  
12024 /**
12025  * @class Roo.UpdateManager
12026  * @extends Roo.util.Observable
12027  * Provides AJAX-style update for Element object.<br><br>
12028  * Usage:<br>
12029  * <pre><code>
12030  * // Get it from a Roo.Element object
12031  * var el = Roo.get("foo");
12032  * var mgr = el.getUpdateManager();
12033  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12034  * ...
12035  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12036  * <br>
12037  * // or directly (returns the same UpdateManager instance)
12038  * var mgr = new Roo.UpdateManager("myElementId");
12039  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12040  * mgr.on("update", myFcnNeedsToKnow);
12041  * <br>
12042    // short handed call directly from the element object
12043    Roo.get("foo").load({
12044         url: "bar.php",
12045         scripts:true,
12046         params: "for=bar",
12047         text: "Loading Foo..."
12048    });
12049  * </code></pre>
12050  * @constructor
12051  * Create new UpdateManager directly.
12052  * @param {String/HTMLElement/Roo.Element} el The element to update
12053  * @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).
12054  */
12055 Roo.UpdateManager = function(el, forceNew){
12056     el = Roo.get(el);
12057     if(!forceNew && el.updateManager){
12058         return el.updateManager;
12059     }
12060     /**
12061      * The Element object
12062      * @type Roo.Element
12063      */
12064     this.el = el;
12065     /**
12066      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12067      * @type String
12068      */
12069     this.defaultUrl = null;
12070
12071     this.addEvents({
12072         /**
12073          * @event beforeupdate
12074          * Fired before an update is made, return false from your handler and the update is cancelled.
12075          * @param {Roo.Element} el
12076          * @param {String/Object/Function} url
12077          * @param {String/Object} params
12078          */
12079         "beforeupdate": true,
12080         /**
12081          * @event update
12082          * Fired after successful update is made.
12083          * @param {Roo.Element} el
12084          * @param {Object} oResponseObject The response Object
12085          */
12086         "update": true,
12087         /**
12088          * @event failure
12089          * Fired on update failure.
12090          * @param {Roo.Element} el
12091          * @param {Object} oResponseObject The response Object
12092          */
12093         "failure": true
12094     });
12095     var d = Roo.UpdateManager.defaults;
12096     /**
12097      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12098      * @type String
12099      */
12100     this.sslBlankUrl = d.sslBlankUrl;
12101     /**
12102      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12103      * @type Boolean
12104      */
12105     this.disableCaching = d.disableCaching;
12106     /**
12107      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12108      * @type String
12109      */
12110     this.indicatorText = d.indicatorText;
12111     /**
12112      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12113      * @type String
12114      */
12115     this.showLoadIndicator = d.showLoadIndicator;
12116     /**
12117      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12118      * @type Number
12119      */
12120     this.timeout = d.timeout;
12121
12122     /**
12123      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12124      * @type Boolean
12125      */
12126     this.loadScripts = d.loadScripts;
12127
12128     /**
12129      * Transaction object of current executing transaction
12130      */
12131     this.transaction = null;
12132
12133     /**
12134      * @private
12135      */
12136     this.autoRefreshProcId = null;
12137     /**
12138      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12139      * @type Function
12140      */
12141     this.refreshDelegate = this.refresh.createDelegate(this);
12142     /**
12143      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12144      * @type Function
12145      */
12146     this.updateDelegate = this.update.createDelegate(this);
12147     /**
12148      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12149      * @type Function
12150      */
12151     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12152     /**
12153      * @private
12154      */
12155     this.successDelegate = this.processSuccess.createDelegate(this);
12156     /**
12157      * @private
12158      */
12159     this.failureDelegate = this.processFailure.createDelegate(this);
12160
12161     if(!this.renderer){
12162      /**
12163       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12164       */
12165     this.renderer = new Roo.UpdateManager.BasicRenderer();
12166     }
12167     
12168     Roo.UpdateManager.superclass.constructor.call(this);
12169 };
12170
12171 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12172     /**
12173      * Get the Element this UpdateManager is bound to
12174      * @return {Roo.Element} The element
12175      */
12176     getEl : function(){
12177         return this.el;
12178     },
12179     /**
12180      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12181      * @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:
12182 <pre><code>
12183 um.update({<br/>
12184     url: "your-url.php",<br/>
12185     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12186     callback: yourFunction,<br/>
12187     scope: yourObject, //(optional scope)  <br/>
12188     discardUrl: false, <br/>
12189     nocache: false,<br/>
12190     text: "Loading...",<br/>
12191     timeout: 30,<br/>
12192     scripts: false<br/>
12193 });
12194 </code></pre>
12195      * The only required property is url. The optional properties nocache, text and scripts
12196      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12197      * @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}
12198      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12199      * @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.
12200      */
12201     update : function(url, params, callback, discardUrl){
12202         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12203             var method = this.method,
12204                 cfg;
12205             if(typeof url == "object"){ // must be config object
12206                 cfg = url;
12207                 url = cfg.url;
12208                 params = params || cfg.params;
12209                 callback = callback || cfg.callback;
12210                 discardUrl = discardUrl || cfg.discardUrl;
12211                 if(callback && cfg.scope){
12212                     callback = callback.createDelegate(cfg.scope);
12213                 }
12214                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12215                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12216                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12217                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12218                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12219             }
12220             this.showLoading();
12221             if(!discardUrl){
12222                 this.defaultUrl = url;
12223             }
12224             if(typeof url == "function"){
12225                 url = url.call(this);
12226             }
12227
12228             method = method || (params ? "POST" : "GET");
12229             if(method == "GET"){
12230                 url = this.prepareUrl(url);
12231             }
12232
12233             var o = Roo.apply(cfg ||{}, {
12234                 url : url,
12235                 params: params,
12236                 success: this.successDelegate,
12237                 failure: this.failureDelegate,
12238                 callback: undefined,
12239                 timeout: (this.timeout*1000),
12240                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12241             });
12242             Roo.log("updated manager called with timeout of " + o.timeout);
12243             this.transaction = Roo.Ajax.request(o);
12244         }
12245     },
12246
12247     /**
12248      * 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.
12249      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12250      * @param {String/HTMLElement} form The form Id or form element
12251      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12252      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12253      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12254      */
12255     formUpdate : function(form, url, reset, callback){
12256         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12257             if(typeof url == "function"){
12258                 url = url.call(this);
12259             }
12260             form = Roo.getDom(form);
12261             this.transaction = Roo.Ajax.request({
12262                 form: form,
12263                 url:url,
12264                 success: this.successDelegate,
12265                 failure: this.failureDelegate,
12266                 timeout: (this.timeout*1000),
12267                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12268             });
12269             this.showLoading.defer(1, this);
12270         }
12271     },
12272
12273     /**
12274      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12275      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12276      */
12277     refresh : function(callback){
12278         if(this.defaultUrl == null){
12279             return;
12280         }
12281         this.update(this.defaultUrl, null, callback, true);
12282     },
12283
12284     /**
12285      * Set this element to auto refresh.
12286      * @param {Number} interval How often to update (in seconds).
12287      * @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)
12288      * @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}
12289      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12290      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12291      */
12292     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12293         if(refreshNow){
12294             this.update(url || this.defaultUrl, params, callback, true);
12295         }
12296         if(this.autoRefreshProcId){
12297             clearInterval(this.autoRefreshProcId);
12298         }
12299         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12300     },
12301
12302     /**
12303      * Stop auto refresh on this element.
12304      */
12305      stopAutoRefresh : function(){
12306         if(this.autoRefreshProcId){
12307             clearInterval(this.autoRefreshProcId);
12308             delete this.autoRefreshProcId;
12309         }
12310     },
12311
12312     isAutoRefreshing : function(){
12313        return this.autoRefreshProcId ? true : false;
12314     },
12315     /**
12316      * Called to update the element to "Loading" state. Override to perform custom action.
12317      */
12318     showLoading : function(){
12319         if(this.showLoadIndicator){
12320             this.el.update(this.indicatorText);
12321         }
12322     },
12323
12324     /**
12325      * Adds unique parameter to query string if disableCaching = true
12326      * @private
12327      */
12328     prepareUrl : function(url){
12329         if(this.disableCaching){
12330             var append = "_dc=" + (new Date().getTime());
12331             if(url.indexOf("?") !== -1){
12332                 url += "&" + append;
12333             }else{
12334                 url += "?" + append;
12335             }
12336         }
12337         return url;
12338     },
12339
12340     /**
12341      * @private
12342      */
12343     processSuccess : function(response){
12344         this.transaction = null;
12345         if(response.argument.form && response.argument.reset){
12346             try{ // put in try/catch since some older FF releases had problems with this
12347                 response.argument.form.reset();
12348             }catch(e){}
12349         }
12350         if(this.loadScripts){
12351             this.renderer.render(this.el, response, this,
12352                 this.updateComplete.createDelegate(this, [response]));
12353         }else{
12354             this.renderer.render(this.el, response, this);
12355             this.updateComplete(response);
12356         }
12357     },
12358
12359     updateComplete : function(response){
12360         this.fireEvent("update", this.el, response);
12361         if(typeof response.argument.callback == "function"){
12362             response.argument.callback(this.el, true, response);
12363         }
12364     },
12365
12366     /**
12367      * @private
12368      */
12369     processFailure : function(response){
12370         this.transaction = null;
12371         this.fireEvent("failure", this.el, response);
12372         if(typeof response.argument.callback == "function"){
12373             response.argument.callback(this.el, false, response);
12374         }
12375     },
12376
12377     /**
12378      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12379      * @param {Object} renderer The object implementing the render() method
12380      */
12381     setRenderer : function(renderer){
12382         this.renderer = renderer;
12383     },
12384
12385     getRenderer : function(){
12386        return this.renderer;
12387     },
12388
12389     /**
12390      * Set the defaultUrl used for updates
12391      * @param {String/Function} defaultUrl The url or a function to call to get the url
12392      */
12393     setDefaultUrl : function(defaultUrl){
12394         this.defaultUrl = defaultUrl;
12395     },
12396
12397     /**
12398      * Aborts the executing transaction
12399      */
12400     abort : function(){
12401         if(this.transaction){
12402             Roo.Ajax.abort(this.transaction);
12403         }
12404     },
12405
12406     /**
12407      * Returns true if an update is in progress
12408      * @return {Boolean}
12409      */
12410     isUpdating : function(){
12411         if(this.transaction){
12412             return Roo.Ajax.isLoading(this.transaction);
12413         }
12414         return false;
12415     }
12416 });
12417
12418 /**
12419  * @class Roo.UpdateManager.defaults
12420  * @static (not really - but it helps the doc tool)
12421  * The defaults collection enables customizing the default properties of UpdateManager
12422  */
12423    Roo.UpdateManager.defaults = {
12424        /**
12425          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12426          * @type Number
12427          */
12428          timeout : 30,
12429
12430          /**
12431          * True to process scripts by default (Defaults to false).
12432          * @type Boolean
12433          */
12434         loadScripts : false,
12435
12436         /**
12437         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12438         * @type String
12439         */
12440         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12441         /**
12442          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12443          * @type Boolean
12444          */
12445         disableCaching : false,
12446         /**
12447          * Whether to show indicatorText when loading (Defaults to true).
12448          * @type Boolean
12449          */
12450         showLoadIndicator : true,
12451         /**
12452          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12453          * @type String
12454          */
12455         indicatorText : '<div class="loading-indicator">Loading...</div>'
12456    };
12457
12458 /**
12459  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12460  *Usage:
12461  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12462  * @param {String/HTMLElement/Roo.Element} el The element to update
12463  * @param {String} url The url
12464  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12465  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12466  * @static
12467  * @deprecated
12468  * @member Roo.UpdateManager
12469  */
12470 Roo.UpdateManager.updateElement = function(el, url, params, options){
12471     var um = Roo.get(el, true).getUpdateManager();
12472     Roo.apply(um, options);
12473     um.update(url, params, options ? options.callback : null);
12474 };
12475 // alias for backwards compat
12476 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12477 /**
12478  * @class Roo.UpdateManager.BasicRenderer
12479  * Default Content renderer. Updates the elements innerHTML with the responseText.
12480  */
12481 Roo.UpdateManager.BasicRenderer = function(){};
12482
12483 Roo.UpdateManager.BasicRenderer.prototype = {
12484     /**
12485      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12486      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12487      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12488      * @param {Roo.Element} el The element being rendered
12489      * @param {Object} response The YUI Connect response object
12490      * @param {UpdateManager} updateManager The calling update manager
12491      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12492      */
12493      render : function(el, response, updateManager, callback){
12494         el.update(response.responseText, updateManager.loadScripts, callback);
12495     }
12496 };
12497 /*
12498  * Based on:
12499  * Roo JS
12500  * (c)) Alan Knowles
12501  * Licence : LGPL
12502  */
12503
12504
12505 /**
12506  * @class Roo.DomTemplate
12507  * @extends Roo.Template
12508  * An effort at a dom based template engine..
12509  *
12510  * Similar to XTemplate, except it uses dom parsing to create the template..
12511  *
12512  * Supported features:
12513  *
12514  *  Tags:
12515
12516 <pre><code>
12517       {a_variable} - output encoded.
12518       {a_variable.format:("Y-m-d")} - call a method on the variable
12519       {a_variable:raw} - unencoded output
12520       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12521       {a_variable:this.method_on_template(...)} - call a method on the template object.
12522  
12523 </code></pre>
12524  *  The tpl tag:
12525 <pre><code>
12526         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12527         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12528         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12529         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12530   
12531 </code></pre>
12532  *      
12533  */
12534 Roo.DomTemplate = function()
12535 {
12536      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12537      if (this.html) {
12538         this.compile();
12539      }
12540 };
12541
12542
12543 Roo.extend(Roo.DomTemplate, Roo.Template, {
12544     /**
12545      * id counter for sub templates.
12546      */
12547     id : 0,
12548     /**
12549      * flag to indicate if dom parser is inside a pre,
12550      * it will strip whitespace if not.
12551      */
12552     inPre : false,
12553     
12554     /**
12555      * The various sub templates
12556      */
12557     tpls : false,
12558     
12559     
12560     
12561     /**
12562      *
12563      * basic tag replacing syntax
12564      * WORD:WORD()
12565      *
12566      * // you can fake an object call by doing this
12567      *  x.t:(test,tesT) 
12568      * 
12569      */
12570     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12571     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12572     
12573     iterChild : function (node, method) {
12574         
12575         var oldPre = this.inPre;
12576         if (node.tagName == 'PRE') {
12577             this.inPre = true;
12578         }
12579         for( var i = 0; i < node.childNodes.length; i++) {
12580             method.call(this, node.childNodes[i]);
12581         }
12582         this.inPre = oldPre;
12583     },
12584     
12585     
12586     
12587     /**
12588      * compile the template
12589      *
12590      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12591      *
12592      */
12593     compile: function()
12594     {
12595         var s = this.html;
12596         
12597         // covert the html into DOM...
12598         var doc = false;
12599         var div =false;
12600         try {
12601             doc = document.implementation.createHTMLDocument("");
12602             doc.documentElement.innerHTML =   this.html  ;
12603             div = doc.documentElement;
12604         } catch (e) {
12605             // old IE... - nasty -- it causes all sorts of issues.. with
12606             // images getting pulled from server..
12607             div = document.createElement('div');
12608             div.innerHTML = this.html;
12609         }
12610         //doc.documentElement.innerHTML = htmlBody
12611          
12612         
12613         
12614         this.tpls = [];
12615         var _t = this;
12616         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12617         
12618         var tpls = this.tpls;
12619         
12620         // create a top level template from the snippet..
12621         
12622         //Roo.log(div.innerHTML);
12623         
12624         var tpl = {
12625             uid : 'master',
12626             id : this.id++,
12627             attr : false,
12628             value : false,
12629             body : div.innerHTML,
12630             
12631             forCall : false,
12632             execCall : false,
12633             dom : div,
12634             isTop : true
12635             
12636         };
12637         tpls.unshift(tpl);
12638         
12639         
12640         // compile them...
12641         this.tpls = [];
12642         Roo.each(tpls, function(tp){
12643             this.compileTpl(tp);
12644             this.tpls[tp.id] = tp;
12645         }, this);
12646         
12647         this.master = tpls[0];
12648         return this;
12649         
12650         
12651     },
12652     
12653     compileNode : function(node, istop) {
12654         // test for
12655         //Roo.log(node);
12656         
12657         
12658         // skip anything not a tag..
12659         if (node.nodeType != 1) {
12660             if (node.nodeType == 3 && !this.inPre) {
12661                 // reduce white space..
12662                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12663                 
12664             }
12665             return;
12666         }
12667         
12668         var tpl = {
12669             uid : false,
12670             id : false,
12671             attr : false,
12672             value : false,
12673             body : '',
12674             
12675             forCall : false,
12676             execCall : false,
12677             dom : false,
12678             isTop : istop
12679             
12680             
12681         };
12682         
12683         
12684         switch(true) {
12685             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12686             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12687             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12688             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12689             // no default..
12690         }
12691         
12692         
12693         if (!tpl.attr) {
12694             // just itterate children..
12695             this.iterChild(node,this.compileNode);
12696             return;
12697         }
12698         tpl.uid = this.id++;
12699         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12700         node.removeAttribute('roo-'+ tpl.attr);
12701         if (tpl.attr != 'name') {
12702             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12703             node.parentNode.replaceChild(placeholder,  node);
12704         } else {
12705             
12706             var placeholder =  document.createElement('span');
12707             placeholder.className = 'roo-tpl-' + tpl.value;
12708             node.parentNode.replaceChild(placeholder,  node);
12709         }
12710         
12711         // parent now sees '{domtplXXXX}
12712         this.iterChild(node,this.compileNode);
12713         
12714         // we should now have node body...
12715         var div = document.createElement('div');
12716         div.appendChild(node);
12717         tpl.dom = node;
12718         // this has the unfortunate side effect of converting tagged attributes
12719         // eg. href="{...}" into %7C...%7D
12720         // this has been fixed by searching for those combo's although it's a bit hacky..
12721         
12722         
12723         tpl.body = div.innerHTML;
12724         
12725         
12726          
12727         tpl.id = tpl.uid;
12728         switch(tpl.attr) {
12729             case 'for' :
12730                 switch (tpl.value) {
12731                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12732                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12733                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12734                 }
12735                 break;
12736             
12737             case 'exec':
12738                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12739                 break;
12740             
12741             case 'if':     
12742                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12743                 break;
12744             
12745             case 'name':
12746                 tpl.id  = tpl.value; // replace non characters???
12747                 break;
12748             
12749         }
12750         
12751         
12752         this.tpls.push(tpl);
12753         
12754         
12755         
12756     },
12757     
12758     
12759     
12760     
12761     /**
12762      * Compile a segment of the template into a 'sub-template'
12763      *
12764      * 
12765      * 
12766      *
12767      */
12768     compileTpl : function(tpl)
12769     {
12770         var fm = Roo.util.Format;
12771         var useF = this.disableFormats !== true;
12772         
12773         var sep = Roo.isGecko ? "+\n" : ",\n";
12774         
12775         var undef = function(str) {
12776             Roo.debug && Roo.log("Property not found :"  + str);
12777             return '';
12778         };
12779           
12780         //Roo.log(tpl.body);
12781         
12782         
12783         
12784         var fn = function(m, lbrace, name, format, args)
12785         {
12786             //Roo.log("ARGS");
12787             //Roo.log(arguments);
12788             args = args ? args.replace(/\\'/g,"'") : args;
12789             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12790             if (typeof(format) == 'undefined') {
12791                 format =  'htmlEncode'; 
12792             }
12793             if (format == 'raw' ) {
12794                 format = false;
12795             }
12796             
12797             if(name.substr(0, 6) == 'domtpl'){
12798                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12799             }
12800             
12801             // build an array of options to determine if value is undefined..
12802             
12803             // basically get 'xxxx.yyyy' then do
12804             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12805             //    (function () { Roo.log("Property not found"); return ''; })() :
12806             //    ......
12807             
12808             var udef_ar = [];
12809             var lookfor = '';
12810             Roo.each(name.split('.'), function(st) {
12811                 lookfor += (lookfor.length ? '.': '') + st;
12812                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12813             });
12814             
12815             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12816             
12817             
12818             if(format && useF){
12819                 
12820                 args = args ? ',' + args : "";
12821                  
12822                 if(format.substr(0, 5) != "this."){
12823                     format = "fm." + format + '(';
12824                 }else{
12825                     format = 'this.call("'+ format.substr(5) + '", ';
12826                     args = ", values";
12827                 }
12828                 
12829                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12830             }
12831              
12832             if (args && args.length) {
12833                 // called with xxyx.yuu:(test,test)
12834                 // change to ()
12835                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12836             }
12837             // raw.. - :raw modifier..
12838             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12839             
12840         };
12841         var body;
12842         // branched to use + in gecko and [].join() in others
12843         if(Roo.isGecko){
12844             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12845                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12846                     "';};};";
12847         }else{
12848             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12849             body.push(tpl.body.replace(/(\r\n|\n)/g,
12850                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12851             body.push("'].join('');};};");
12852             body = body.join('');
12853         }
12854         
12855         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12856        
12857         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12858         eval(body);
12859         
12860         return this;
12861     },
12862      
12863     /**
12864      * same as applyTemplate, except it's done to one of the subTemplates
12865      * when using named templates, you can do:
12866      *
12867      * var str = pl.applySubTemplate('your-name', values);
12868      *
12869      * 
12870      * @param {Number} id of the template
12871      * @param {Object} values to apply to template
12872      * @param {Object} parent (normaly the instance of this object)
12873      */
12874     applySubTemplate : function(id, values, parent)
12875     {
12876         
12877         
12878         var t = this.tpls[id];
12879         
12880         
12881         try { 
12882             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12883                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12884                 return '';
12885             }
12886         } catch(e) {
12887             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12888             Roo.log(values);
12889           
12890             return '';
12891         }
12892         try { 
12893             
12894             if(t.execCall && t.execCall.call(this, values, parent)){
12895                 return '';
12896             }
12897         } catch(e) {
12898             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12899             Roo.log(values);
12900             return '';
12901         }
12902         
12903         try {
12904             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12905             parent = t.target ? values : parent;
12906             if(t.forCall && vs instanceof Array){
12907                 var buf = [];
12908                 for(var i = 0, len = vs.length; i < len; i++){
12909                     try {
12910                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12911                     } catch (e) {
12912                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12913                         Roo.log(e.body);
12914                         //Roo.log(t.compiled);
12915                         Roo.log(vs[i]);
12916                     }   
12917                 }
12918                 return buf.join('');
12919             }
12920         } catch (e) {
12921             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12922             Roo.log(values);
12923             return '';
12924         }
12925         try {
12926             return t.compiled.call(this, vs, parent);
12927         } catch (e) {
12928             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12929             Roo.log(e.body);
12930             //Roo.log(t.compiled);
12931             Roo.log(values);
12932             return '';
12933         }
12934     },
12935
12936    
12937
12938     applyTemplate : function(values){
12939         return this.master.compiled.call(this, values, {});
12940         //var s = this.subs;
12941     },
12942
12943     apply : function(){
12944         return this.applyTemplate.apply(this, arguments);
12945     }
12946
12947  });
12948
12949 Roo.DomTemplate.from = function(el){
12950     el = Roo.getDom(el);
12951     return new Roo.Domtemplate(el.value || el.innerHTML);
12952 };/*
12953  * Based on:
12954  * Ext JS Library 1.1.1
12955  * Copyright(c) 2006-2007, Ext JS, LLC.
12956  *
12957  * Originally Released Under LGPL - original licence link has changed is not relivant.
12958  *
12959  * Fork - LGPL
12960  * <script type="text/javascript">
12961  */
12962
12963 /**
12964  * @class Roo.util.DelayedTask
12965  * Provides a convenient method of performing setTimeout where a new
12966  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12967  * You can use this class to buffer
12968  * the keypress events for a certain number of milliseconds, and perform only if they stop
12969  * for that amount of time.
12970  * @constructor The parameters to this constructor serve as defaults and are not required.
12971  * @param {Function} fn (optional) The default function to timeout
12972  * @param {Object} scope (optional) The default scope of that timeout
12973  * @param {Array} args (optional) The default Array of arguments
12974  */
12975 Roo.util.DelayedTask = function(fn, scope, args){
12976     var id = null, d, t;
12977
12978     var call = function(){
12979         var now = new Date().getTime();
12980         if(now - t >= d){
12981             clearInterval(id);
12982             id = null;
12983             fn.apply(scope, args || []);
12984         }
12985     };
12986     /**
12987      * Cancels any pending timeout and queues a new one
12988      * @param {Number} delay The milliseconds to delay
12989      * @param {Function} newFn (optional) Overrides function passed to constructor
12990      * @param {Object} newScope (optional) Overrides scope passed to constructor
12991      * @param {Array} newArgs (optional) Overrides args passed to constructor
12992      */
12993     this.delay = function(delay, newFn, newScope, newArgs){
12994         if(id && delay != d){
12995             this.cancel();
12996         }
12997         d = delay;
12998         t = new Date().getTime();
12999         fn = newFn || fn;
13000         scope = newScope || scope;
13001         args = newArgs || args;
13002         if(!id){
13003             id = setInterval(call, d);
13004         }
13005     };
13006
13007     /**
13008      * Cancel the last queued timeout
13009      */
13010     this.cancel = function(){
13011         if(id){
13012             clearInterval(id);
13013             id = null;
13014         }
13015     };
13016 };/*
13017  * Based on:
13018  * Ext JS Library 1.1.1
13019  * Copyright(c) 2006-2007, Ext JS, LLC.
13020  *
13021  * Originally Released Under LGPL - original licence link has changed is not relivant.
13022  *
13023  * Fork - LGPL
13024  * <script type="text/javascript">
13025  */
13026  
13027  
13028 Roo.util.TaskRunner = function(interval){
13029     interval = interval || 10;
13030     var tasks = [], removeQueue = [];
13031     var id = 0;
13032     var running = false;
13033
13034     var stopThread = function(){
13035         running = false;
13036         clearInterval(id);
13037         id = 0;
13038     };
13039
13040     var startThread = function(){
13041         if(!running){
13042             running = true;
13043             id = setInterval(runTasks, interval);
13044         }
13045     };
13046
13047     var removeTask = function(task){
13048         removeQueue.push(task);
13049         if(task.onStop){
13050             task.onStop();
13051         }
13052     };
13053
13054     var runTasks = function(){
13055         if(removeQueue.length > 0){
13056             for(var i = 0, len = removeQueue.length; i < len; i++){
13057                 tasks.remove(removeQueue[i]);
13058             }
13059             removeQueue = [];
13060             if(tasks.length < 1){
13061                 stopThread();
13062                 return;
13063             }
13064         }
13065         var now = new Date().getTime();
13066         for(var i = 0, len = tasks.length; i < len; ++i){
13067             var t = tasks[i];
13068             var itime = now - t.taskRunTime;
13069             if(t.interval <= itime){
13070                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13071                 t.taskRunTime = now;
13072                 if(rt === false || t.taskRunCount === t.repeat){
13073                     removeTask(t);
13074                     return;
13075                 }
13076             }
13077             if(t.duration && t.duration <= (now - t.taskStartTime)){
13078                 removeTask(t);
13079             }
13080         }
13081     };
13082
13083     /**
13084      * Queues a new task.
13085      * @param {Object} task
13086      */
13087     this.start = function(task){
13088         tasks.push(task);
13089         task.taskStartTime = new Date().getTime();
13090         task.taskRunTime = 0;
13091         task.taskRunCount = 0;
13092         startThread();
13093         return task;
13094     };
13095
13096     this.stop = function(task){
13097         removeTask(task);
13098         return task;
13099     };
13100
13101     this.stopAll = function(){
13102         stopThread();
13103         for(var i = 0, len = tasks.length; i < len; i++){
13104             if(tasks[i].onStop){
13105                 tasks[i].onStop();
13106             }
13107         }
13108         tasks = [];
13109         removeQueue = [];
13110     };
13111 };
13112
13113 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13114  * Based on:
13115  * Ext JS Library 1.1.1
13116  * Copyright(c) 2006-2007, Ext JS, LLC.
13117  *
13118  * Originally Released Under LGPL - original licence link has changed is not relivant.
13119  *
13120  * Fork - LGPL
13121  * <script type="text/javascript">
13122  */
13123
13124  
13125 /**
13126  * @class Roo.util.MixedCollection
13127  * @extends Roo.util.Observable
13128  * A Collection class that maintains both numeric indexes and keys and exposes events.
13129  * @constructor
13130  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13131  * collection (defaults to false)
13132  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13133  * and return the key value for that item.  This is used when available to look up the key on items that
13134  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13135  * equivalent to providing an implementation for the {@link #getKey} method.
13136  */
13137 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13138     this.items = [];
13139     this.map = {};
13140     this.keys = [];
13141     this.length = 0;
13142     this.addEvents({
13143         /**
13144          * @event clear
13145          * Fires when the collection is cleared.
13146          */
13147         "clear" : true,
13148         /**
13149          * @event add
13150          * Fires when an item is added to the collection.
13151          * @param {Number} index The index at which the item was added.
13152          * @param {Object} o The item added.
13153          * @param {String} key The key associated with the added item.
13154          */
13155         "add" : true,
13156         /**
13157          * @event replace
13158          * Fires when an item is replaced in the collection.
13159          * @param {String} key he key associated with the new added.
13160          * @param {Object} old The item being replaced.
13161          * @param {Object} new The new item.
13162          */
13163         "replace" : true,
13164         /**
13165          * @event remove
13166          * Fires when an item is removed from the collection.
13167          * @param {Object} o The item being removed.
13168          * @param {String} key (optional) The key associated with the removed item.
13169          */
13170         "remove" : true,
13171         "sort" : true
13172     });
13173     this.allowFunctions = allowFunctions === true;
13174     if(keyFn){
13175         this.getKey = keyFn;
13176     }
13177     Roo.util.MixedCollection.superclass.constructor.call(this);
13178 };
13179
13180 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13181     allowFunctions : false,
13182     
13183 /**
13184  * Adds an item to the collection.
13185  * @param {String} key The key to associate with the item
13186  * @param {Object} o The item to add.
13187  * @return {Object} The item added.
13188  */
13189     add : function(key, o){
13190         if(arguments.length == 1){
13191             o = arguments[0];
13192             key = this.getKey(o);
13193         }
13194         if(typeof key == "undefined" || key === null){
13195             this.length++;
13196             this.items.push(o);
13197             this.keys.push(null);
13198         }else{
13199             var old = this.map[key];
13200             if(old){
13201                 return this.replace(key, o);
13202             }
13203             this.length++;
13204             this.items.push(o);
13205             this.map[key] = o;
13206             this.keys.push(key);
13207         }
13208         this.fireEvent("add", this.length-1, o, key);
13209         return o;
13210     },
13211        
13212 /**
13213   * MixedCollection has a generic way to fetch keys if you implement getKey.
13214 <pre><code>
13215 // normal way
13216 var mc = new Roo.util.MixedCollection();
13217 mc.add(someEl.dom.id, someEl);
13218 mc.add(otherEl.dom.id, otherEl);
13219 //and so on
13220
13221 // using getKey
13222 var mc = new Roo.util.MixedCollection();
13223 mc.getKey = function(el){
13224    return el.dom.id;
13225 };
13226 mc.add(someEl);
13227 mc.add(otherEl);
13228
13229 // or via the constructor
13230 var mc = new Roo.util.MixedCollection(false, function(el){
13231    return el.dom.id;
13232 });
13233 mc.add(someEl);
13234 mc.add(otherEl);
13235 </code></pre>
13236  * @param o {Object} The item for which to find the key.
13237  * @return {Object} The key for the passed item.
13238  */
13239     getKey : function(o){
13240          return o.id; 
13241     },
13242    
13243 /**
13244  * Replaces an item in the collection.
13245  * @param {String} key The key associated with the item to replace, or the item to replace.
13246  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13247  * @return {Object}  The new item.
13248  */
13249     replace : function(key, o){
13250         if(arguments.length == 1){
13251             o = arguments[0];
13252             key = this.getKey(o);
13253         }
13254         var old = this.item(key);
13255         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13256              return this.add(key, o);
13257         }
13258         var index = this.indexOfKey(key);
13259         this.items[index] = o;
13260         this.map[key] = o;
13261         this.fireEvent("replace", key, old, o);
13262         return o;
13263     },
13264    
13265 /**
13266  * Adds all elements of an Array or an Object to the collection.
13267  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13268  * an Array of values, each of which are added to the collection.
13269  */
13270     addAll : function(objs){
13271         if(arguments.length > 1 || objs instanceof Array){
13272             var args = arguments.length > 1 ? arguments : objs;
13273             for(var i = 0, len = args.length; i < len; i++){
13274                 this.add(args[i]);
13275             }
13276         }else{
13277             for(var key in objs){
13278                 if(this.allowFunctions || typeof objs[key] != "function"){
13279                     this.add(key, objs[key]);
13280                 }
13281             }
13282         }
13283     },
13284    
13285 /**
13286  * Executes the specified function once for every item in the collection, passing each
13287  * item as the first and only parameter. returning false from the function will stop the iteration.
13288  * @param {Function} fn The function to execute for each item.
13289  * @param {Object} scope (optional) The scope in which to execute the function.
13290  */
13291     each : function(fn, scope){
13292         var items = [].concat(this.items); // each safe for removal
13293         for(var i = 0, len = items.length; i < len; i++){
13294             if(fn.call(scope || items[i], items[i], i, len) === false){
13295                 break;
13296             }
13297         }
13298     },
13299    
13300 /**
13301  * Executes the specified function once for every key in the collection, passing each
13302  * key, and its associated item as the first two parameters.
13303  * @param {Function} fn The function to execute for each item.
13304  * @param {Object} scope (optional) The scope in which to execute the function.
13305  */
13306     eachKey : function(fn, scope){
13307         for(var i = 0, len = this.keys.length; i < len; i++){
13308             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13309         }
13310     },
13311    
13312 /**
13313  * Returns the first item in the collection which elicits a true return value from the
13314  * passed selection function.
13315  * @param {Function} fn The selection function to execute for each item.
13316  * @param {Object} scope (optional) The scope in which to execute the function.
13317  * @return {Object} The first item in the collection which returned true from the selection function.
13318  */
13319     find : function(fn, scope){
13320         for(var i = 0, len = this.items.length; i < len; i++){
13321             if(fn.call(scope || window, this.items[i], this.keys[i])){
13322                 return this.items[i];
13323             }
13324         }
13325         return null;
13326     },
13327    
13328 /**
13329  * Inserts an item at the specified index in the collection.
13330  * @param {Number} index The index to insert the item at.
13331  * @param {String} key The key to associate with the new item, or the item itself.
13332  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13333  * @return {Object} The item inserted.
13334  */
13335     insert : function(index, key, o){
13336         if(arguments.length == 2){
13337             o = arguments[1];
13338             key = this.getKey(o);
13339         }
13340         if(index >= this.length){
13341             return this.add(key, o);
13342         }
13343         this.length++;
13344         this.items.splice(index, 0, o);
13345         if(typeof key != "undefined" && key != null){
13346             this.map[key] = o;
13347         }
13348         this.keys.splice(index, 0, key);
13349         this.fireEvent("add", index, o, key);
13350         return o;
13351     },
13352    
13353 /**
13354  * Removed an item from the collection.
13355  * @param {Object} o The item to remove.
13356  * @return {Object} The item removed.
13357  */
13358     remove : function(o){
13359         return this.removeAt(this.indexOf(o));
13360     },
13361    
13362 /**
13363  * Remove an item from a specified index in the collection.
13364  * @param {Number} index The index within the collection of the item to remove.
13365  */
13366     removeAt : function(index){
13367         if(index < this.length && index >= 0){
13368             this.length--;
13369             var o = this.items[index];
13370             this.items.splice(index, 1);
13371             var key = this.keys[index];
13372             if(typeof key != "undefined"){
13373                 delete this.map[key];
13374             }
13375             this.keys.splice(index, 1);
13376             this.fireEvent("remove", o, key);
13377         }
13378     },
13379    
13380 /**
13381  * Removed an item associated with the passed key fom the collection.
13382  * @param {String} key The key of the item to remove.
13383  */
13384     removeKey : function(key){
13385         return this.removeAt(this.indexOfKey(key));
13386     },
13387    
13388 /**
13389  * Returns the number of items in the collection.
13390  * @return {Number} the number of items in the collection.
13391  */
13392     getCount : function(){
13393         return this.length; 
13394     },
13395    
13396 /**
13397  * Returns index within the collection of the passed Object.
13398  * @param {Object} o The item to find the index of.
13399  * @return {Number} index of the item.
13400  */
13401     indexOf : function(o){
13402         if(!this.items.indexOf){
13403             for(var i = 0, len = this.items.length; i < len; i++){
13404                 if(this.items[i] == o) {
13405                     return i;
13406                 }
13407             }
13408             return -1;
13409         }else{
13410             return this.items.indexOf(o);
13411         }
13412     },
13413    
13414 /**
13415  * Returns index within the collection of the passed key.
13416  * @param {String} key The key to find the index of.
13417  * @return {Number} index of the key.
13418  */
13419     indexOfKey : function(key){
13420         if(!this.keys.indexOf){
13421             for(var i = 0, len = this.keys.length; i < len; i++){
13422                 if(this.keys[i] == key) {
13423                     return i;
13424                 }
13425             }
13426             return -1;
13427         }else{
13428             return this.keys.indexOf(key);
13429         }
13430     },
13431    
13432 /**
13433  * Returns the item associated with the passed key OR index. Key has priority over index.
13434  * @param {String/Number} key The key or index of the item.
13435  * @return {Object} The item associated with the passed key.
13436  */
13437     item : function(key){
13438         if (key === 'length') {
13439             return null;
13440         }
13441         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13442         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13443     },
13444     
13445 /**
13446  * Returns the item at the specified index.
13447  * @param {Number} index The index of the item.
13448  * @return {Object}
13449  */
13450     itemAt : function(index){
13451         return this.items[index];
13452     },
13453     
13454 /**
13455  * Returns the item associated with the passed key.
13456  * @param {String/Number} key The key of the item.
13457  * @return {Object} The item associated with the passed key.
13458  */
13459     key : function(key){
13460         return this.map[key];
13461     },
13462    
13463 /**
13464  * Returns true if the collection contains the passed Object as an item.
13465  * @param {Object} o  The Object to look for in the collection.
13466  * @return {Boolean} True if the collection contains the Object as an item.
13467  */
13468     contains : function(o){
13469         return this.indexOf(o) != -1;
13470     },
13471    
13472 /**
13473  * Returns true if the collection contains the passed Object as a key.
13474  * @param {String} key The key to look for in the collection.
13475  * @return {Boolean} True if the collection contains the Object as a key.
13476  */
13477     containsKey : function(key){
13478         return typeof this.map[key] != "undefined";
13479     },
13480    
13481 /**
13482  * Removes all items from the collection.
13483  */
13484     clear : function(){
13485         this.length = 0;
13486         this.items = [];
13487         this.keys = [];
13488         this.map = {};
13489         this.fireEvent("clear");
13490     },
13491    
13492 /**
13493  * Returns the first item in the collection.
13494  * @return {Object} the first item in the collection..
13495  */
13496     first : function(){
13497         return this.items[0]; 
13498     },
13499    
13500 /**
13501  * Returns the last item in the collection.
13502  * @return {Object} the last item in the collection..
13503  */
13504     last : function(){
13505         return this.items[this.length-1];   
13506     },
13507     
13508     _sort : function(property, dir, fn){
13509         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13510         fn = fn || function(a, b){
13511             return a-b;
13512         };
13513         var c = [], k = this.keys, items = this.items;
13514         for(var i = 0, len = items.length; i < len; i++){
13515             c[c.length] = {key: k[i], value: items[i], index: i};
13516         }
13517         c.sort(function(a, b){
13518             var v = fn(a[property], b[property]) * dsc;
13519             if(v == 0){
13520                 v = (a.index < b.index ? -1 : 1);
13521             }
13522             return v;
13523         });
13524         for(var i = 0, len = c.length; i < len; i++){
13525             items[i] = c[i].value;
13526             k[i] = c[i].key;
13527         }
13528         this.fireEvent("sort", this);
13529     },
13530     
13531     /**
13532      * Sorts this collection with the passed comparison function
13533      * @param {String} direction (optional) "ASC" or "DESC"
13534      * @param {Function} fn (optional) comparison function
13535      */
13536     sort : function(dir, fn){
13537         this._sort("value", dir, fn);
13538     },
13539     
13540     /**
13541      * Sorts this collection by keys
13542      * @param {String} direction (optional) "ASC" or "DESC"
13543      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13544      */
13545     keySort : function(dir, fn){
13546         this._sort("key", dir, fn || function(a, b){
13547             return String(a).toUpperCase()-String(b).toUpperCase();
13548         });
13549     },
13550     
13551     /**
13552      * Returns a range of items in this collection
13553      * @param {Number} startIndex (optional) defaults to 0
13554      * @param {Number} endIndex (optional) default to the last item
13555      * @return {Array} An array of items
13556      */
13557     getRange : function(start, end){
13558         var items = this.items;
13559         if(items.length < 1){
13560             return [];
13561         }
13562         start = start || 0;
13563         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13564         var r = [];
13565         if(start <= end){
13566             for(var i = start; i <= end; i++) {
13567                     r[r.length] = items[i];
13568             }
13569         }else{
13570             for(var i = start; i >= end; i--) {
13571                     r[r.length] = items[i];
13572             }
13573         }
13574         return r;
13575     },
13576         
13577     /**
13578      * Filter the <i>objects</i> in this collection by a specific property. 
13579      * Returns a new collection that has been filtered.
13580      * @param {String} property A property on your objects
13581      * @param {String/RegExp} value Either string that the property values 
13582      * should start with or a RegExp to test against the property
13583      * @return {MixedCollection} The new filtered collection
13584      */
13585     filter : function(property, value){
13586         if(!value.exec){ // not a regex
13587             value = String(value);
13588             if(value.length == 0){
13589                 return this.clone();
13590             }
13591             value = new RegExp("^" + Roo.escapeRe(value), "i");
13592         }
13593         return this.filterBy(function(o){
13594             return o && value.test(o[property]);
13595         });
13596         },
13597     
13598     /**
13599      * Filter by a function. * Returns a new collection that has been filtered.
13600      * The passed function will be called with each 
13601      * object in the collection. If the function returns true, the value is included 
13602      * otherwise it is filtered.
13603      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13604      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13605      * @return {MixedCollection} The new filtered collection
13606      */
13607     filterBy : function(fn, scope){
13608         var r = new Roo.util.MixedCollection();
13609         r.getKey = this.getKey;
13610         var k = this.keys, it = this.items;
13611         for(var i = 0, len = it.length; i < len; i++){
13612             if(fn.call(scope||this, it[i], k[i])){
13613                                 r.add(k[i], it[i]);
13614                         }
13615         }
13616         return r;
13617     },
13618     
13619     /**
13620      * Creates a duplicate of this collection
13621      * @return {MixedCollection}
13622      */
13623     clone : function(){
13624         var r = new Roo.util.MixedCollection();
13625         var k = this.keys, it = this.items;
13626         for(var i = 0, len = it.length; i < len; i++){
13627             r.add(k[i], it[i]);
13628         }
13629         r.getKey = this.getKey;
13630         return r;
13631     }
13632 });
13633 /**
13634  * Returns the item associated with the passed key or index.
13635  * @method
13636  * @param {String/Number} key The key or index of the item.
13637  * @return {Object} The item associated with the passed key.
13638  */
13639 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13640  * Based on:
13641  * Ext JS Library 1.1.1
13642  * Copyright(c) 2006-2007, Ext JS, LLC.
13643  *
13644  * Originally Released Under LGPL - original licence link has changed is not relivant.
13645  *
13646  * Fork - LGPL
13647  * <script type="text/javascript">
13648  */
13649 /**
13650  * @class Roo.util.JSON
13651  * Modified version of Douglas Crockford"s json.js that doesn"t
13652  * mess with the Object prototype 
13653  * http://www.json.org/js.html
13654  * @singleton
13655  */
13656 Roo.util.JSON = new (function(){
13657     var useHasOwn = {}.hasOwnProperty ? true : false;
13658     
13659     // crashes Safari in some instances
13660     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13661     
13662     var pad = function(n) {
13663         return n < 10 ? "0" + n : n;
13664     };
13665     
13666     var m = {
13667         "\b": '\\b',
13668         "\t": '\\t',
13669         "\n": '\\n',
13670         "\f": '\\f',
13671         "\r": '\\r',
13672         '"' : '\\"',
13673         "\\": '\\\\'
13674     };
13675
13676     var encodeString = function(s){
13677         if (/["\\\x00-\x1f]/.test(s)) {
13678             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13679                 var c = m[b];
13680                 if(c){
13681                     return c;
13682                 }
13683                 c = b.charCodeAt();
13684                 return "\\u00" +
13685                     Math.floor(c / 16).toString(16) +
13686                     (c % 16).toString(16);
13687             }) + '"';
13688         }
13689         return '"' + s + '"';
13690     };
13691     
13692     var encodeArray = function(o){
13693         var a = ["["], b, i, l = o.length, v;
13694             for (i = 0; i < l; i += 1) {
13695                 v = o[i];
13696                 switch (typeof v) {
13697                     case "undefined":
13698                     case "function":
13699                     case "unknown":
13700                         break;
13701                     default:
13702                         if (b) {
13703                             a.push(',');
13704                         }
13705                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13706                         b = true;
13707                 }
13708             }
13709             a.push("]");
13710             return a.join("");
13711     };
13712     
13713     var encodeDate = function(o){
13714         return '"' + o.getFullYear() + "-" +
13715                 pad(o.getMonth() + 1) + "-" +
13716                 pad(o.getDate()) + "T" +
13717                 pad(o.getHours()) + ":" +
13718                 pad(o.getMinutes()) + ":" +
13719                 pad(o.getSeconds()) + '"';
13720     };
13721     
13722     /**
13723      * Encodes an Object, Array or other value
13724      * @param {Mixed} o The variable to encode
13725      * @return {String} The JSON string
13726      */
13727     this.encode = function(o)
13728     {
13729         // should this be extended to fully wrap stringify..
13730         
13731         if(typeof o == "undefined" || o === null){
13732             return "null";
13733         }else if(o instanceof Array){
13734             return encodeArray(o);
13735         }else if(o instanceof Date){
13736             return encodeDate(o);
13737         }else if(typeof o == "string"){
13738             return encodeString(o);
13739         }else if(typeof o == "number"){
13740             return isFinite(o) ? String(o) : "null";
13741         }else if(typeof o == "boolean"){
13742             return String(o);
13743         }else {
13744             var a = ["{"], b, i, v;
13745             for (i in o) {
13746                 if(!useHasOwn || o.hasOwnProperty(i)) {
13747                     v = o[i];
13748                     switch (typeof v) {
13749                     case "undefined":
13750                     case "function":
13751                     case "unknown":
13752                         break;
13753                     default:
13754                         if(b){
13755                             a.push(',');
13756                         }
13757                         a.push(this.encode(i), ":",
13758                                 v === null ? "null" : this.encode(v));
13759                         b = true;
13760                     }
13761                 }
13762             }
13763             a.push("}");
13764             return a.join("");
13765         }
13766     };
13767     
13768     /**
13769      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13770      * @param {String} json The JSON string
13771      * @return {Object} The resulting object
13772      */
13773     this.decode = function(json){
13774         
13775         return  /** eval:var:json */ eval("(" + json + ')');
13776     };
13777 })();
13778 /** 
13779  * Shorthand for {@link Roo.util.JSON#encode}
13780  * @member Roo encode 
13781  * @method */
13782 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13783 /** 
13784  * Shorthand for {@link Roo.util.JSON#decode}
13785  * @member Roo decode 
13786  * @method */
13787 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13788 /*
13789  * Based on:
13790  * Ext JS Library 1.1.1
13791  * Copyright(c) 2006-2007, Ext JS, LLC.
13792  *
13793  * Originally Released Under LGPL - original licence link has changed is not relivant.
13794  *
13795  * Fork - LGPL
13796  * <script type="text/javascript">
13797  */
13798  
13799 /**
13800  * @class Roo.util.Format
13801  * Reusable data formatting functions
13802  * @singleton
13803  */
13804 Roo.util.Format = function(){
13805     var trimRe = /^\s+|\s+$/g;
13806     return {
13807         /**
13808          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13809          * @param {String} value The string to truncate
13810          * @param {Number} length The maximum length to allow before truncating
13811          * @return {String} The converted text
13812          */
13813         ellipsis : function(value, len){
13814             if(value && value.length > len){
13815                 return value.substr(0, len-3)+"...";
13816             }
13817             return value;
13818         },
13819
13820         /**
13821          * Checks a reference and converts it to empty string if it is undefined
13822          * @param {Mixed} value Reference to check
13823          * @return {Mixed} Empty string if converted, otherwise the original value
13824          */
13825         undef : function(value){
13826             return typeof value != "undefined" ? value : "";
13827         },
13828
13829         /**
13830          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13831          * @param {String} value The string to encode
13832          * @return {String} The encoded text
13833          */
13834         htmlEncode : function(value){
13835             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13836         },
13837
13838         /**
13839          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13840          * @param {String} value The string to decode
13841          * @return {String} The decoded text
13842          */
13843         htmlDecode : function(value){
13844             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13845         },
13846
13847         /**
13848          * Trims any whitespace from either side of a string
13849          * @param {String} value The text to trim
13850          * @return {String} The trimmed text
13851          */
13852         trim : function(value){
13853             return String(value).replace(trimRe, "");
13854         },
13855
13856         /**
13857          * Returns a substring from within an original string
13858          * @param {String} value The original text
13859          * @param {Number} start The start index of the substring
13860          * @param {Number} length The length of the substring
13861          * @return {String} The substring
13862          */
13863         substr : function(value, start, length){
13864             return String(value).substr(start, length);
13865         },
13866
13867         /**
13868          * Converts a string to all lower case letters
13869          * @param {String} value The text to convert
13870          * @return {String} The converted text
13871          */
13872         lowercase : function(value){
13873             return String(value).toLowerCase();
13874         },
13875
13876         /**
13877          * Converts a string to all upper case letters
13878          * @param {String} value The text to convert
13879          * @return {String} The converted text
13880          */
13881         uppercase : function(value){
13882             return String(value).toUpperCase();
13883         },
13884
13885         /**
13886          * Converts the first character only of a string to upper case
13887          * @param {String} value The text to convert
13888          * @return {String} The converted text
13889          */
13890         capitalize : function(value){
13891             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13892         },
13893
13894         // private
13895         call : function(value, fn){
13896             if(arguments.length > 2){
13897                 var args = Array.prototype.slice.call(arguments, 2);
13898                 args.unshift(value);
13899                  
13900                 return /** eval:var:value */  eval(fn).apply(window, args);
13901             }else{
13902                 /** eval:var:value */
13903                 return /** eval:var:value */ eval(fn).call(window, value);
13904             }
13905         },
13906
13907        
13908         /**
13909          * safer version of Math.toFixed..??/
13910          * @param {Number/String} value The numeric value to format
13911          * @param {Number/String} value Decimal places 
13912          * @return {String} The formatted currency string
13913          */
13914         toFixed : function(v, n)
13915         {
13916             // why not use to fixed - precision is buggered???
13917             if (!n) {
13918                 return Math.round(v-0);
13919             }
13920             var fact = Math.pow(10,n+1);
13921             v = (Math.round((v-0)*fact))/fact;
13922             var z = (''+fact).substring(2);
13923             if (v == Math.floor(v)) {
13924                 return Math.floor(v) + '.' + z;
13925             }
13926             
13927             // now just padd decimals..
13928             var ps = String(v).split('.');
13929             var fd = (ps[1] + z);
13930             var r = fd.substring(0,n); 
13931             var rm = fd.substring(n); 
13932             if (rm < 5) {
13933                 return ps[0] + '.' + r;
13934             }
13935             r*=1; // turn it into a number;
13936             r++;
13937             if (String(r).length != n) {
13938                 ps[0]*=1;
13939                 ps[0]++;
13940                 r = String(r).substring(1); // chop the end off.
13941             }
13942             
13943             return ps[0] + '.' + r;
13944              
13945         },
13946         
13947         /**
13948          * Format a number as US currency
13949          * @param {Number/String} value The numeric value to format
13950          * @return {String} The formatted currency string
13951          */
13952         usMoney : function(v){
13953             return '$' + Roo.util.Format.number(v);
13954         },
13955         
13956         /**
13957          * Format a number
13958          * eventually this should probably emulate php's number_format
13959          * @param {Number/String} value The numeric value to format
13960          * @param {Number} decimals number of decimal places
13961          * @param {String} delimiter for thousands (default comma)
13962          * @return {String} The formatted currency string
13963          */
13964         number : function(v, decimals, thousandsDelimiter)
13965         {
13966             // multiply and round.
13967             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13968             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13969             
13970             var mul = Math.pow(10, decimals);
13971             var zero = String(mul).substring(1);
13972             v = (Math.round((v-0)*mul))/mul;
13973             
13974             // if it's '0' number.. then
13975             
13976             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13977             v = String(v);
13978             var ps = v.split('.');
13979             var whole = ps[0];
13980             
13981             var r = /(\d+)(\d{3})/;
13982             // add comma's
13983             
13984             if(thousandsDelimiter.length != 0) {
13985                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13986             } 
13987             
13988             var sub = ps[1] ?
13989                     // has decimals..
13990                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13991                     // does not have decimals
13992                     (decimals ? ('.' + zero) : '');
13993             
13994             
13995             return whole + sub ;
13996         },
13997         
13998         /**
13999          * Parse a value into a formatted date using the specified format pattern.
14000          * @param {Mixed} value The value to format
14001          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14002          * @return {String} The formatted date string
14003          */
14004         date : function(v, format){
14005             if(!v){
14006                 return "";
14007             }
14008             if(!(v instanceof Date)){
14009                 v = new Date(Date.parse(v));
14010             }
14011             return v.dateFormat(format || Roo.util.Format.defaults.date);
14012         },
14013
14014         /**
14015          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14016          * @param {String} format Any valid date format string
14017          * @return {Function} The date formatting function
14018          */
14019         dateRenderer : function(format){
14020             return function(v){
14021                 return Roo.util.Format.date(v, format);  
14022             };
14023         },
14024
14025         // private
14026         stripTagsRE : /<\/?[^>]+>/gi,
14027         
14028         /**
14029          * Strips all HTML tags
14030          * @param {Mixed} value The text from which to strip tags
14031          * @return {String} The stripped text
14032          */
14033         stripTags : function(v){
14034             return !v ? v : String(v).replace(this.stripTagsRE, "");
14035         },
14036         
14037         /**
14038          * Size in Mb,Gb etc.
14039          * @param {Number} value The number to be formated
14040          * @param {number} decimals how many decimal places
14041          * @return {String} the formated string
14042          */
14043         size : function(value, decimals)
14044         {
14045             var sizes = ['b', 'k', 'M', 'G', 'T'];
14046             if (value == 0) {
14047                 return 0;
14048             }
14049             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14050             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14051         }
14052         
14053         
14054         
14055     };
14056 }();
14057 Roo.util.Format.defaults = {
14058     date : 'd/M/Y'
14059 };/*
14060  * Based on:
14061  * Ext JS Library 1.1.1
14062  * Copyright(c) 2006-2007, Ext JS, LLC.
14063  *
14064  * Originally Released Under LGPL - original licence link has changed is not relivant.
14065  *
14066  * Fork - LGPL
14067  * <script type="text/javascript">
14068  */
14069
14070
14071  
14072
14073 /**
14074  * @class Roo.MasterTemplate
14075  * @extends Roo.Template
14076  * Provides a template that can have child templates. The syntax is:
14077 <pre><code>
14078 var t = new Roo.MasterTemplate(
14079         '&lt;select name="{name}"&gt;',
14080                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14081         '&lt;/select&gt;'
14082 );
14083 t.add('options', {value: 'foo', text: 'bar'});
14084 // or you can add multiple child elements in one shot
14085 t.addAll('options', [
14086     {value: 'foo', text: 'bar'},
14087     {value: 'foo2', text: 'bar2'},
14088     {value: 'foo3', text: 'bar3'}
14089 ]);
14090 // then append, applying the master template values
14091 t.append('my-form', {name: 'my-select'});
14092 </code></pre>
14093 * A name attribute for the child template is not required if you have only one child
14094 * template or you want to refer to them by index.
14095  */
14096 Roo.MasterTemplate = function(){
14097     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14098     this.originalHtml = this.html;
14099     var st = {};
14100     var m, re = this.subTemplateRe;
14101     re.lastIndex = 0;
14102     var subIndex = 0;
14103     while(m = re.exec(this.html)){
14104         var name = m[1], content = m[2];
14105         st[subIndex] = {
14106             name: name,
14107             index: subIndex,
14108             buffer: [],
14109             tpl : new Roo.Template(content)
14110         };
14111         if(name){
14112             st[name] = st[subIndex];
14113         }
14114         st[subIndex].tpl.compile();
14115         st[subIndex].tpl.call = this.call.createDelegate(this);
14116         subIndex++;
14117     }
14118     this.subCount = subIndex;
14119     this.subs = st;
14120 };
14121 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14122     /**
14123     * The regular expression used to match sub templates
14124     * @type RegExp
14125     * @property
14126     */
14127     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14128
14129     /**
14130      * Applies the passed values to a child template.
14131      * @param {String/Number} name (optional) The name or index of the child template
14132      * @param {Array/Object} values The values to be applied to the template
14133      * @return {MasterTemplate} this
14134      */
14135      add : function(name, values){
14136         if(arguments.length == 1){
14137             values = arguments[0];
14138             name = 0;
14139         }
14140         var s = this.subs[name];
14141         s.buffer[s.buffer.length] = s.tpl.apply(values);
14142         return this;
14143     },
14144
14145     /**
14146      * Applies all the passed values to a child template.
14147      * @param {String/Number} name (optional) The name or index of the child template
14148      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14149      * @param {Boolean} reset (optional) True to reset the template first
14150      * @return {MasterTemplate} this
14151      */
14152     fill : function(name, values, reset){
14153         var a = arguments;
14154         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14155             values = a[0];
14156             name = 0;
14157             reset = a[1];
14158         }
14159         if(reset){
14160             this.reset();
14161         }
14162         for(var i = 0, len = values.length; i < len; i++){
14163             this.add(name, values[i]);
14164         }
14165         return this;
14166     },
14167
14168     /**
14169      * Resets the template for reuse
14170      * @return {MasterTemplate} this
14171      */
14172      reset : function(){
14173         var s = this.subs;
14174         for(var i = 0; i < this.subCount; i++){
14175             s[i].buffer = [];
14176         }
14177         return this;
14178     },
14179
14180     applyTemplate : function(values){
14181         var s = this.subs;
14182         var replaceIndex = -1;
14183         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14184             return s[++replaceIndex].buffer.join("");
14185         });
14186         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14187     },
14188
14189     apply : function(){
14190         return this.applyTemplate.apply(this, arguments);
14191     },
14192
14193     compile : function(){return this;}
14194 });
14195
14196 /**
14197  * Alias for fill().
14198  * @method
14199  */
14200 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14201  /**
14202  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14203  * var tpl = Roo.MasterTemplate.from('element-id');
14204  * @param {String/HTMLElement} el
14205  * @param {Object} config
14206  * @static
14207  */
14208 Roo.MasterTemplate.from = function(el, config){
14209     el = Roo.getDom(el);
14210     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14211 };/*
14212  * Based on:
14213  * Ext JS Library 1.1.1
14214  * Copyright(c) 2006-2007, Ext JS, LLC.
14215  *
14216  * Originally Released Under LGPL - original licence link has changed is not relivant.
14217  *
14218  * Fork - LGPL
14219  * <script type="text/javascript">
14220  */
14221
14222  
14223 /**
14224  * @class Roo.util.CSS
14225  * Utility class for manipulating CSS rules
14226  * @singleton
14227  */
14228 Roo.util.CSS = function(){
14229         var rules = null;
14230         var doc = document;
14231
14232     var camelRe = /(-[a-z])/gi;
14233     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14234
14235    return {
14236    /**
14237     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14238     * tag and appended to the HEAD of the document.
14239     * @param {String|Object} cssText The text containing the css rules
14240     * @param {String} id An id to add to the stylesheet for later removal
14241     * @return {StyleSheet}
14242     */
14243     createStyleSheet : function(cssText, id){
14244         var ss;
14245         var head = doc.getElementsByTagName("head")[0];
14246         var nrules = doc.createElement("style");
14247         nrules.setAttribute("type", "text/css");
14248         if(id){
14249             nrules.setAttribute("id", id);
14250         }
14251         if (typeof(cssText) != 'string') {
14252             // support object maps..
14253             // not sure if this a good idea.. 
14254             // perhaps it should be merged with the general css handling
14255             // and handle js style props.
14256             var cssTextNew = [];
14257             for(var n in cssText) {
14258                 var citems = [];
14259                 for(var k in cssText[n]) {
14260                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14261                 }
14262                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14263                 
14264             }
14265             cssText = cssTextNew.join("\n");
14266             
14267         }
14268        
14269        
14270        if(Roo.isIE){
14271            head.appendChild(nrules);
14272            ss = nrules.styleSheet;
14273            ss.cssText = cssText;
14274        }else{
14275            try{
14276                 nrules.appendChild(doc.createTextNode(cssText));
14277            }catch(e){
14278                nrules.cssText = cssText; 
14279            }
14280            head.appendChild(nrules);
14281            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14282        }
14283        this.cacheStyleSheet(ss);
14284        return ss;
14285    },
14286
14287    /**
14288     * Removes a style or link tag by id
14289     * @param {String} id The id of the tag
14290     */
14291    removeStyleSheet : function(id){
14292        var existing = doc.getElementById(id);
14293        if(existing){
14294            existing.parentNode.removeChild(existing);
14295        }
14296    },
14297
14298    /**
14299     * Dynamically swaps an existing stylesheet reference for a new one
14300     * @param {String} id The id of an existing link tag to remove
14301     * @param {String} url The href of the new stylesheet to include
14302     */
14303    swapStyleSheet : function(id, url){
14304        this.removeStyleSheet(id);
14305        var ss = doc.createElement("link");
14306        ss.setAttribute("rel", "stylesheet");
14307        ss.setAttribute("type", "text/css");
14308        ss.setAttribute("id", id);
14309        ss.setAttribute("href", url);
14310        doc.getElementsByTagName("head")[0].appendChild(ss);
14311    },
14312    
14313    /**
14314     * Refresh the rule cache if you have dynamically added stylesheets
14315     * @return {Object} An object (hash) of rules indexed by selector
14316     */
14317    refreshCache : function(){
14318        return this.getRules(true);
14319    },
14320
14321    // private
14322    cacheStyleSheet : function(stylesheet){
14323        if(!rules){
14324            rules = {};
14325        }
14326        try{// try catch for cross domain access issue
14327            var ssRules = stylesheet.cssRules || stylesheet.rules;
14328            for(var j = ssRules.length-1; j >= 0; --j){
14329                rules[ssRules[j].selectorText] = ssRules[j];
14330            }
14331        }catch(e){}
14332    },
14333    
14334    /**
14335     * Gets all css rules for the document
14336     * @param {Boolean} refreshCache true to refresh the internal cache
14337     * @return {Object} An object (hash) of rules indexed by selector
14338     */
14339    getRules : function(refreshCache){
14340                 if(rules == null || refreshCache){
14341                         rules = {};
14342                         var ds = doc.styleSheets;
14343                         for(var i =0, len = ds.length; i < len; i++){
14344                             try{
14345                         this.cacheStyleSheet(ds[i]);
14346                     }catch(e){} 
14347                 }
14348                 }
14349                 return rules;
14350         },
14351         
14352         /**
14353     * Gets an an individual CSS rule by selector(s)
14354     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14355     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14356     * @return {CSSRule} The CSS rule or null if one is not found
14357     */
14358    getRule : function(selector, refreshCache){
14359                 var rs = this.getRules(refreshCache);
14360                 if(!(selector instanceof Array)){
14361                     return rs[selector];
14362                 }
14363                 for(var i = 0; i < selector.length; i++){
14364                         if(rs[selector[i]]){
14365                                 return rs[selector[i]];
14366                         }
14367                 }
14368                 return null;
14369         },
14370         
14371         
14372         /**
14373     * Updates a rule property
14374     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14375     * @param {String} property The css property
14376     * @param {String} value The new value for the property
14377     * @return {Boolean} true If a rule was found and updated
14378     */
14379    updateRule : function(selector, property, value){
14380                 if(!(selector instanceof Array)){
14381                         var rule = this.getRule(selector);
14382                         if(rule){
14383                                 rule.style[property.replace(camelRe, camelFn)] = value;
14384                                 return true;
14385                         }
14386                 }else{
14387                         for(var i = 0; i < selector.length; i++){
14388                                 if(this.updateRule(selector[i], property, value)){
14389                                         return true;
14390                                 }
14391                         }
14392                 }
14393                 return false;
14394         }
14395    };   
14396 }();/*
14397  * Based on:
14398  * Ext JS Library 1.1.1
14399  * Copyright(c) 2006-2007, Ext JS, LLC.
14400  *
14401  * Originally Released Under LGPL - original licence link has changed is not relivant.
14402  *
14403  * Fork - LGPL
14404  * <script type="text/javascript">
14405  */
14406
14407  
14408
14409 /**
14410  * @class Roo.util.ClickRepeater
14411  * @extends Roo.util.Observable
14412  * 
14413  * A wrapper class which can be applied to any element. Fires a "click" event while the
14414  * mouse is pressed. The interval between firings may be specified in the config but
14415  * defaults to 10 milliseconds.
14416  * 
14417  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14418  * 
14419  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14420  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14421  * Similar to an autorepeat key delay.
14422  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14423  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14424  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14425  *           "interval" and "delay" are ignored. "immediate" is honored.
14426  * @cfg {Boolean} preventDefault True to prevent the default click event
14427  * @cfg {Boolean} stopDefault True to stop the default click event
14428  * 
14429  * @history
14430  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14431  *     2007-02-02 jvs Renamed to ClickRepeater
14432  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14433  *
14434  *  @constructor
14435  * @param {String/HTMLElement/Element} el The element to listen on
14436  * @param {Object} config
14437  **/
14438 Roo.util.ClickRepeater = function(el, config)
14439 {
14440     this.el = Roo.get(el);
14441     this.el.unselectable();
14442
14443     Roo.apply(this, config);
14444
14445     this.addEvents({
14446     /**
14447      * @event mousedown
14448      * Fires when the mouse button is depressed.
14449      * @param {Roo.util.ClickRepeater} this
14450      */
14451         "mousedown" : true,
14452     /**
14453      * @event click
14454      * Fires on a specified interval during the time the element is pressed.
14455      * @param {Roo.util.ClickRepeater} this
14456      */
14457         "click" : true,
14458     /**
14459      * @event mouseup
14460      * Fires when the mouse key is released.
14461      * @param {Roo.util.ClickRepeater} this
14462      */
14463         "mouseup" : true
14464     });
14465
14466     this.el.on("mousedown", this.handleMouseDown, this);
14467     if(this.preventDefault || this.stopDefault){
14468         this.el.on("click", function(e){
14469             if(this.preventDefault){
14470                 e.preventDefault();
14471             }
14472             if(this.stopDefault){
14473                 e.stopEvent();
14474             }
14475         }, this);
14476     }
14477
14478     // allow inline handler
14479     if(this.handler){
14480         this.on("click", this.handler,  this.scope || this);
14481     }
14482
14483     Roo.util.ClickRepeater.superclass.constructor.call(this);
14484 };
14485
14486 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14487     interval : 20,
14488     delay: 250,
14489     preventDefault : true,
14490     stopDefault : false,
14491     timer : 0,
14492
14493     // private
14494     handleMouseDown : function(){
14495         clearTimeout(this.timer);
14496         this.el.blur();
14497         if(this.pressClass){
14498             this.el.addClass(this.pressClass);
14499         }
14500         this.mousedownTime = new Date();
14501
14502         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14503         this.el.on("mouseout", this.handleMouseOut, this);
14504
14505         this.fireEvent("mousedown", this);
14506         this.fireEvent("click", this);
14507         
14508         this.timer = this.click.defer(this.delay || this.interval, this);
14509     },
14510
14511     // private
14512     click : function(){
14513         this.fireEvent("click", this);
14514         this.timer = this.click.defer(this.getInterval(), this);
14515     },
14516
14517     // private
14518     getInterval: function(){
14519         if(!this.accelerate){
14520             return this.interval;
14521         }
14522         var pressTime = this.mousedownTime.getElapsed();
14523         if(pressTime < 500){
14524             return 400;
14525         }else if(pressTime < 1700){
14526             return 320;
14527         }else if(pressTime < 2600){
14528             return 250;
14529         }else if(pressTime < 3500){
14530             return 180;
14531         }else if(pressTime < 4400){
14532             return 140;
14533         }else if(pressTime < 5300){
14534             return 80;
14535         }else if(pressTime < 6200){
14536             return 50;
14537         }else{
14538             return 10;
14539         }
14540     },
14541
14542     // private
14543     handleMouseOut : function(){
14544         clearTimeout(this.timer);
14545         if(this.pressClass){
14546             this.el.removeClass(this.pressClass);
14547         }
14548         this.el.on("mouseover", this.handleMouseReturn, this);
14549     },
14550
14551     // private
14552     handleMouseReturn : function(){
14553         this.el.un("mouseover", this.handleMouseReturn);
14554         if(this.pressClass){
14555             this.el.addClass(this.pressClass);
14556         }
14557         this.click();
14558     },
14559
14560     // private
14561     handleMouseUp : function(){
14562         clearTimeout(this.timer);
14563         this.el.un("mouseover", this.handleMouseReturn);
14564         this.el.un("mouseout", this.handleMouseOut);
14565         Roo.get(document).un("mouseup", this.handleMouseUp);
14566         this.el.removeClass(this.pressClass);
14567         this.fireEvent("mouseup", this);
14568     }
14569 });/**
14570  * @class Roo.util.Clipboard
14571  * @static
14572  * 
14573  * Clipboard UTILS
14574  * 
14575  **/
14576 Roo.util.Clipboard = {
14577     /**
14578      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14579      * @param {String} text to copy to clipboard
14580      */
14581     write : function(text) {
14582         // navigator clipboard api needs a secure context (https)
14583         if (navigator.clipboard && window.isSecureContext) {
14584             // navigator clipboard api method'
14585             navigator.clipboard.writeText(text);
14586             return ;
14587         } 
14588         // text area method
14589         var ta = document.createElement("textarea");
14590         ta.value = text;
14591         // make the textarea out of viewport
14592         ta.style.position = "fixed";
14593         ta.style.left = "-999999px";
14594         ta.style.top = "-999999px";
14595         document.body.appendChild(ta);
14596         ta.focus();
14597         ta.select();
14598         document.execCommand('copy');
14599         (function() {
14600             ta.remove();
14601         }).defer(100);
14602         
14603     }
14604         
14605 }
14606     /*
14607  * Based on:
14608  * Ext JS Library 1.1.1
14609  * Copyright(c) 2006-2007, Ext JS, LLC.
14610  *
14611  * Originally Released Under LGPL - original licence link has changed is not relivant.
14612  *
14613  * Fork - LGPL
14614  * <script type="text/javascript">
14615  */
14616
14617  
14618 /**
14619  * @class Roo.KeyNav
14620  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14621  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14622  * way to implement custom navigation schemes for any UI component.</p>
14623  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14624  * pageUp, pageDown, del, home, end.  Usage:</p>
14625  <pre><code>
14626 var nav = new Roo.KeyNav("my-element", {
14627     "left" : function(e){
14628         this.moveLeft(e.ctrlKey);
14629     },
14630     "right" : function(e){
14631         this.moveRight(e.ctrlKey);
14632     },
14633     "enter" : function(e){
14634         this.save();
14635     },
14636     scope : this
14637 });
14638 </code></pre>
14639  * @constructor
14640  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14641  * @param {Object} config The config
14642  */
14643 Roo.KeyNav = function(el, config){
14644     this.el = Roo.get(el);
14645     Roo.apply(this, config);
14646     if(!this.disabled){
14647         this.disabled = true;
14648         this.enable();
14649     }
14650 };
14651
14652 Roo.KeyNav.prototype = {
14653     /**
14654      * @cfg {Boolean} disabled
14655      * True to disable this KeyNav instance (defaults to false)
14656      */
14657     disabled : false,
14658     /**
14659      * @cfg {String} defaultEventAction
14660      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14661      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14662      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14663      */
14664     defaultEventAction: "stopEvent",
14665     /**
14666      * @cfg {Boolean} forceKeyDown
14667      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14668      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14669      * handle keydown instead of keypress.
14670      */
14671     forceKeyDown : false,
14672
14673     // private
14674     prepareEvent : function(e){
14675         var k = e.getKey();
14676         var h = this.keyToHandler[k];
14677         //if(h && this[h]){
14678         //    e.stopPropagation();
14679         //}
14680         if(Roo.isSafari && h && k >= 37 && k <= 40){
14681             e.stopEvent();
14682         }
14683     },
14684
14685     // private
14686     relay : function(e){
14687         var k = e.getKey();
14688         var h = this.keyToHandler[k];
14689         if(h && this[h]){
14690             if(this.doRelay(e, this[h], h) !== true){
14691                 e[this.defaultEventAction]();
14692             }
14693         }
14694     },
14695
14696     // private
14697     doRelay : function(e, h, hname){
14698         return h.call(this.scope || this, e);
14699     },
14700
14701     // possible handlers
14702     enter : false,
14703     left : false,
14704     right : false,
14705     up : false,
14706     down : false,
14707     tab : false,
14708     esc : false,
14709     pageUp : false,
14710     pageDown : false,
14711     del : false,
14712     home : false,
14713     end : false,
14714
14715     // quick lookup hash
14716     keyToHandler : {
14717         37 : "left",
14718         39 : "right",
14719         38 : "up",
14720         40 : "down",
14721         33 : "pageUp",
14722         34 : "pageDown",
14723         46 : "del",
14724         36 : "home",
14725         35 : "end",
14726         13 : "enter",
14727         27 : "esc",
14728         9  : "tab"
14729     },
14730
14731         /**
14732          * Enable this KeyNav
14733          */
14734         enable: function(){
14735                 if(this.disabled){
14736             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14737             // the EventObject will normalize Safari automatically
14738             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14739                 this.el.on("keydown", this.relay,  this);
14740             }else{
14741                 this.el.on("keydown", this.prepareEvent,  this);
14742                 this.el.on("keypress", this.relay,  this);
14743             }
14744                     this.disabled = false;
14745                 }
14746         },
14747
14748         /**
14749          * Disable this KeyNav
14750          */
14751         disable: function(){
14752                 if(!this.disabled){
14753                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14754                 this.el.un("keydown", this.relay);
14755             }else{
14756                 this.el.un("keydown", this.prepareEvent);
14757                 this.el.un("keypress", this.relay);
14758             }
14759                     this.disabled = true;
14760                 }
14761         }
14762 };/*
14763  * Based on:
14764  * Ext JS Library 1.1.1
14765  * Copyright(c) 2006-2007, Ext JS, LLC.
14766  *
14767  * Originally Released Under LGPL - original licence link has changed is not relivant.
14768  *
14769  * Fork - LGPL
14770  * <script type="text/javascript">
14771  */
14772
14773  
14774 /**
14775  * @class Roo.KeyMap
14776  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14777  * The constructor accepts the same config object as defined by {@link #addBinding}.
14778  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14779  * combination it will call the function with this signature (if the match is a multi-key
14780  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14781  * A KeyMap can also handle a string representation of keys.<br />
14782  * Usage:
14783  <pre><code>
14784 // map one key by key code
14785 var map = new Roo.KeyMap("my-element", {
14786     key: 13, // or Roo.EventObject.ENTER
14787     fn: myHandler,
14788     scope: myObject
14789 });
14790
14791 // map multiple keys to one action by string
14792 var map = new Roo.KeyMap("my-element", {
14793     key: "a\r\n\t",
14794     fn: myHandler,
14795     scope: myObject
14796 });
14797
14798 // map multiple keys to multiple actions by strings and array of codes
14799 var map = new Roo.KeyMap("my-element", [
14800     {
14801         key: [10,13],
14802         fn: function(){ alert("Return was pressed"); }
14803     }, {
14804         key: "abc",
14805         fn: function(){ alert('a, b or c was pressed'); }
14806     }, {
14807         key: "\t",
14808         ctrl:true,
14809         shift:true,
14810         fn: function(){ alert('Control + shift + tab was pressed.'); }
14811     }
14812 ]);
14813 </code></pre>
14814  * <b>Note: A KeyMap starts enabled</b>
14815  * @constructor
14816  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14817  * @param {Object} config The config (see {@link #addBinding})
14818  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14819  */
14820 Roo.KeyMap = function(el, config, eventName){
14821     this.el  = Roo.get(el);
14822     this.eventName = eventName || "keydown";
14823     this.bindings = [];
14824     if(config){
14825         this.addBinding(config);
14826     }
14827     this.enable();
14828 };
14829
14830 Roo.KeyMap.prototype = {
14831     /**
14832      * True to stop the event from bubbling and prevent the default browser action if the
14833      * key was handled by the KeyMap (defaults to false)
14834      * @type Boolean
14835      */
14836     stopEvent : false,
14837
14838     /**
14839      * Add a new binding to this KeyMap. The following config object properties are supported:
14840      * <pre>
14841 Property    Type             Description
14842 ----------  ---------------  ----------------------------------------------------------------------
14843 key         String/Array     A single keycode or an array of keycodes to handle
14844 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14845 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14846 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14847 fn          Function         The function to call when KeyMap finds the expected key combination
14848 scope       Object           The scope of the callback function
14849 </pre>
14850      *
14851      * Usage:
14852      * <pre><code>
14853 // Create a KeyMap
14854 var map = new Roo.KeyMap(document, {
14855     key: Roo.EventObject.ENTER,
14856     fn: handleKey,
14857     scope: this
14858 });
14859
14860 //Add a new binding to the existing KeyMap later
14861 map.addBinding({
14862     key: 'abc',
14863     shift: true,
14864     fn: handleKey,
14865     scope: this
14866 });
14867 </code></pre>
14868      * @param {Object/Array} config A single KeyMap config or an array of configs
14869      */
14870         addBinding : function(config){
14871         if(config instanceof Array){
14872             for(var i = 0, len = config.length; i < len; i++){
14873                 this.addBinding(config[i]);
14874             }
14875             return;
14876         }
14877         var keyCode = config.key,
14878             shift = config.shift, 
14879             ctrl = config.ctrl, 
14880             alt = config.alt,
14881             fn = config.fn,
14882             scope = config.scope;
14883         if(typeof keyCode == "string"){
14884             var ks = [];
14885             var keyString = keyCode.toUpperCase();
14886             for(var j = 0, len = keyString.length; j < len; j++){
14887                 ks.push(keyString.charCodeAt(j));
14888             }
14889             keyCode = ks;
14890         }
14891         var keyArray = keyCode instanceof Array;
14892         var handler = function(e){
14893             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14894                 var k = e.getKey();
14895                 if(keyArray){
14896                     for(var i = 0, len = keyCode.length; i < len; i++){
14897                         if(keyCode[i] == k){
14898                           if(this.stopEvent){
14899                               e.stopEvent();
14900                           }
14901                           fn.call(scope || window, k, e);
14902                           return;
14903                         }
14904                     }
14905                 }else{
14906                     if(k == keyCode){
14907                         if(this.stopEvent){
14908                            e.stopEvent();
14909                         }
14910                         fn.call(scope || window, k, e);
14911                     }
14912                 }
14913             }
14914         };
14915         this.bindings.push(handler);  
14916         },
14917
14918     /**
14919      * Shorthand for adding a single key listener
14920      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14921      * following options:
14922      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14923      * @param {Function} fn The function to call
14924      * @param {Object} scope (optional) The scope of the function
14925      */
14926     on : function(key, fn, scope){
14927         var keyCode, shift, ctrl, alt;
14928         if(typeof key == "object" && !(key instanceof Array)){
14929             keyCode = key.key;
14930             shift = key.shift;
14931             ctrl = key.ctrl;
14932             alt = key.alt;
14933         }else{
14934             keyCode = key;
14935         }
14936         this.addBinding({
14937             key: keyCode,
14938             shift: shift,
14939             ctrl: ctrl,
14940             alt: alt,
14941             fn: fn,
14942             scope: scope
14943         })
14944     },
14945
14946     // private
14947     handleKeyDown : function(e){
14948             if(this.enabled){ //just in case
14949             var b = this.bindings;
14950             for(var i = 0, len = b.length; i < len; i++){
14951                 b[i].call(this, e);
14952             }
14953             }
14954         },
14955         
14956         /**
14957          * Returns true if this KeyMap is enabled
14958          * @return {Boolean} 
14959          */
14960         isEnabled : function(){
14961             return this.enabled;  
14962         },
14963         
14964         /**
14965          * Enables this KeyMap
14966          */
14967         enable: function(){
14968                 if(!this.enabled){
14969                     this.el.on(this.eventName, this.handleKeyDown, this);
14970                     this.enabled = true;
14971                 }
14972         },
14973
14974         /**
14975          * Disable this KeyMap
14976          */
14977         disable: function(){
14978                 if(this.enabled){
14979                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14980                     this.enabled = false;
14981                 }
14982         }
14983 };/*
14984  * Based on:
14985  * Ext JS Library 1.1.1
14986  * Copyright(c) 2006-2007, Ext JS, LLC.
14987  *
14988  * Originally Released Under LGPL - original licence link has changed is not relivant.
14989  *
14990  * Fork - LGPL
14991  * <script type="text/javascript">
14992  */
14993
14994  
14995 /**
14996  * @class Roo.util.TextMetrics
14997  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14998  * wide, in pixels, a given block of text will be.
14999  * @singleton
15000  */
15001 Roo.util.TextMetrics = function(){
15002     var shared;
15003     return {
15004         /**
15005          * Measures the size of the specified text
15006          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15007          * that can affect the size of the rendered text
15008          * @param {String} text The text to measure
15009          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15010          * in order to accurately measure the text height
15011          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15012          */
15013         measure : function(el, text, fixedWidth){
15014             if(!shared){
15015                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15016             }
15017             shared.bind(el);
15018             shared.setFixedWidth(fixedWidth || 'auto');
15019             return shared.getSize(text);
15020         },
15021
15022         /**
15023          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15024          * the overhead of multiple calls to initialize the style properties on each measurement.
15025          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15026          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15027          * in order to accurately measure the text height
15028          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15029          */
15030         createInstance : function(el, fixedWidth){
15031             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15032         }
15033     };
15034 }();
15035
15036  
15037
15038 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15039     var ml = new Roo.Element(document.createElement('div'));
15040     document.body.appendChild(ml.dom);
15041     ml.position('absolute');
15042     ml.setLeftTop(-1000, -1000);
15043     ml.hide();
15044
15045     if(fixedWidth){
15046         ml.setWidth(fixedWidth);
15047     }
15048      
15049     var instance = {
15050         /**
15051          * Returns the size of the specified text based on the internal element's style and width properties
15052          * @memberOf Roo.util.TextMetrics.Instance#
15053          * @param {String} text The text to measure
15054          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15055          */
15056         getSize : function(text){
15057             ml.update(text);
15058             var s = ml.getSize();
15059             ml.update('');
15060             return s;
15061         },
15062
15063         /**
15064          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15065          * that can affect the size of the rendered text
15066          * @memberOf Roo.util.TextMetrics.Instance#
15067          * @param {String/HTMLElement} el The element, dom node or id
15068          */
15069         bind : function(el){
15070             ml.setStyle(
15071                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15072             );
15073         },
15074
15075         /**
15076          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15077          * to set a fixed width in order to accurately measure the text height.
15078          * @memberOf Roo.util.TextMetrics.Instance#
15079          * @param {Number} width The width to set on the element
15080          */
15081         setFixedWidth : function(width){
15082             ml.setWidth(width);
15083         },
15084
15085         /**
15086          * Returns the measured width of the specified text
15087          * @memberOf Roo.util.TextMetrics.Instance#
15088          * @param {String} text The text to measure
15089          * @return {Number} width The width in pixels
15090          */
15091         getWidth : function(text){
15092             ml.dom.style.width = 'auto';
15093             return this.getSize(text).width;
15094         },
15095
15096         /**
15097          * Returns the measured height of the specified text.  For multiline text, be sure to call
15098          * {@link #setFixedWidth} if necessary.
15099          * @memberOf Roo.util.TextMetrics.Instance#
15100          * @param {String} text The text to measure
15101          * @return {Number} height The height in pixels
15102          */
15103         getHeight : function(text){
15104             return this.getSize(text).height;
15105         }
15106     };
15107
15108     instance.bind(bindTo);
15109
15110     return instance;
15111 };
15112
15113 // backwards compat
15114 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15115  * Based on:
15116  * Ext JS Library 1.1.1
15117  * Copyright(c) 2006-2007, Ext JS, LLC.
15118  *
15119  * Originally Released Under LGPL - original licence link has changed is not relivant.
15120  *
15121  * Fork - LGPL
15122  * <script type="text/javascript">
15123  */
15124
15125 /**
15126  * @class Roo.state.Provider
15127  * Abstract base class for state provider implementations. This class provides methods
15128  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15129  * Provider interface.
15130  */
15131 Roo.state.Provider = function(){
15132     /**
15133      * @event statechange
15134      * Fires when a state change occurs.
15135      * @param {Provider} this This state provider
15136      * @param {String} key The state key which was changed
15137      * @param {String} value The encoded value for the state
15138      */
15139     this.addEvents({
15140         "statechange": true
15141     });
15142     this.state = {};
15143     Roo.state.Provider.superclass.constructor.call(this);
15144 };
15145 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15146     /**
15147      * Returns the current value for a key
15148      * @param {String} name The key name
15149      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15150      * @return {Mixed} The state data
15151      */
15152     get : function(name, defaultValue){
15153         return typeof this.state[name] == "undefined" ?
15154             defaultValue : this.state[name];
15155     },
15156     
15157     /**
15158      * Clears a value from the state
15159      * @param {String} name The key name
15160      */
15161     clear : function(name){
15162         delete this.state[name];
15163         this.fireEvent("statechange", this, name, null);
15164     },
15165     
15166     /**
15167      * Sets the value for a key
15168      * @param {String} name The key name
15169      * @param {Mixed} value The value to set
15170      */
15171     set : function(name, value){
15172         this.state[name] = value;
15173         this.fireEvent("statechange", this, name, value);
15174     },
15175     
15176     /**
15177      * Decodes a string previously encoded with {@link #encodeValue}.
15178      * @param {String} value The value to decode
15179      * @return {Mixed} The decoded value
15180      */
15181     decodeValue : function(cookie){
15182         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15183         var matches = re.exec(unescape(cookie));
15184         if(!matches || !matches[1]) {
15185             return; // non state cookie
15186         }
15187         var type = matches[1];
15188         var v = matches[2];
15189         switch(type){
15190             case "n":
15191                 return parseFloat(v);
15192             case "d":
15193                 return new Date(Date.parse(v));
15194             case "b":
15195                 return (v == "1");
15196             case "a":
15197                 var all = [];
15198                 var values = v.split("^");
15199                 for(var i = 0, len = values.length; i < len; i++){
15200                     all.push(this.decodeValue(values[i]));
15201                 }
15202                 return all;
15203            case "o":
15204                 var all = {};
15205                 var values = v.split("^");
15206                 for(var i = 0, len = values.length; i < len; i++){
15207                     var kv = values[i].split("=");
15208                     all[kv[0]] = this.decodeValue(kv[1]);
15209                 }
15210                 return all;
15211            default:
15212                 return v;
15213         }
15214     },
15215     
15216     /**
15217      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15218      * @param {Mixed} value The value to encode
15219      * @return {String} The encoded value
15220      */
15221     encodeValue : function(v){
15222         var enc;
15223         if(typeof v == "number"){
15224             enc = "n:" + v;
15225         }else if(typeof v == "boolean"){
15226             enc = "b:" + (v ? "1" : "0");
15227         }else if(v instanceof Date){
15228             enc = "d:" + v.toGMTString();
15229         }else if(v instanceof Array){
15230             var flat = "";
15231             for(var i = 0, len = v.length; i < len; i++){
15232                 flat += this.encodeValue(v[i]);
15233                 if(i != len-1) {
15234                     flat += "^";
15235                 }
15236             }
15237             enc = "a:" + flat;
15238         }else if(typeof v == "object"){
15239             var flat = "";
15240             for(var key in v){
15241                 if(typeof v[key] != "function"){
15242                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15243                 }
15244             }
15245             enc = "o:" + flat.substring(0, flat.length-1);
15246         }else{
15247             enc = "s:" + v;
15248         }
15249         return escape(enc);        
15250     }
15251 });
15252
15253 /*
15254  * Based on:
15255  * Ext JS Library 1.1.1
15256  * Copyright(c) 2006-2007, Ext JS, LLC.
15257  *
15258  * Originally Released Under LGPL - original licence link has changed is not relivant.
15259  *
15260  * Fork - LGPL
15261  * <script type="text/javascript">
15262  */
15263 /**
15264  * @class Roo.state.Manager
15265  * This is the global state manager. By default all components that are "state aware" check this class
15266  * for state information if you don't pass them a custom state provider. In order for this class
15267  * to be useful, it must be initialized with a provider when your application initializes.
15268  <pre><code>
15269 // in your initialization function
15270 init : function(){
15271    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15272    ...
15273    // supposed you have a {@link Roo.BorderLayout}
15274    var layout = new Roo.BorderLayout(...);
15275    layout.restoreState();
15276    // or a {Roo.BasicDialog}
15277    var dialog = new Roo.BasicDialog(...);
15278    dialog.restoreState();
15279  </code></pre>
15280  * @singleton
15281  */
15282 Roo.state.Manager = function(){
15283     var provider = new Roo.state.Provider();
15284     
15285     return {
15286         /**
15287          * Configures the default state provider for your application
15288          * @param {Provider} stateProvider The state provider to set
15289          */
15290         setProvider : function(stateProvider){
15291             provider = stateProvider;
15292         },
15293         
15294         /**
15295          * Returns the current value for a key
15296          * @param {String} name The key name
15297          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15298          * @return {Mixed} The state data
15299          */
15300         get : function(key, defaultValue){
15301             return provider.get(key, defaultValue);
15302         },
15303         
15304         /**
15305          * Sets the value for a key
15306          * @param {String} name The key name
15307          * @param {Mixed} value The state data
15308          */
15309          set : function(key, value){
15310             provider.set(key, value);
15311         },
15312         
15313         /**
15314          * Clears a value from the state
15315          * @param {String} name The key name
15316          */
15317         clear : function(key){
15318             provider.clear(key);
15319         },
15320         
15321         /**
15322          * Gets the currently configured state provider
15323          * @return {Provider} The state provider
15324          */
15325         getProvider : function(){
15326             return provider;
15327         }
15328     };
15329 }();
15330 /*
15331  * Based on:
15332  * Ext JS Library 1.1.1
15333  * Copyright(c) 2006-2007, Ext JS, LLC.
15334  *
15335  * Originally Released Under LGPL - original licence link has changed is not relivant.
15336  *
15337  * Fork - LGPL
15338  * <script type="text/javascript">
15339  */
15340 /**
15341  * @class Roo.state.CookieProvider
15342  * @extends Roo.state.Provider
15343  * The default Provider implementation which saves state via cookies.
15344  * <br />Usage:
15345  <pre><code>
15346    var cp = new Roo.state.CookieProvider({
15347        path: "/cgi-bin/",
15348        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15349        domain: "roojs.com"
15350    })
15351    Roo.state.Manager.setProvider(cp);
15352  </code></pre>
15353  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15354  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15355  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15356  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15357  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15358  * domain the page is running on including the 'www' like 'www.roojs.com')
15359  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15360  * @constructor
15361  * Create a new CookieProvider
15362  * @param {Object} config The configuration object
15363  */
15364 Roo.state.CookieProvider = function(config){
15365     Roo.state.CookieProvider.superclass.constructor.call(this);
15366     this.path = "/";
15367     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15368     this.domain = null;
15369     this.secure = false;
15370     Roo.apply(this, config);
15371     this.state = this.readCookies();
15372 };
15373
15374 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15375     // private
15376     set : function(name, value){
15377         if(typeof value == "undefined" || value === null){
15378             this.clear(name);
15379             return;
15380         }
15381         this.setCookie(name, value);
15382         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15383     },
15384
15385     // private
15386     clear : function(name){
15387         this.clearCookie(name);
15388         Roo.state.CookieProvider.superclass.clear.call(this, name);
15389     },
15390
15391     // private
15392     readCookies : function(){
15393         var cookies = {};
15394         var c = document.cookie + ";";
15395         var re = /\s?(.*?)=(.*?);/g;
15396         var matches;
15397         while((matches = re.exec(c)) != null){
15398             var name = matches[1];
15399             var value = matches[2];
15400             if(name && name.substring(0,3) == "ys-"){
15401                 cookies[name.substr(3)] = this.decodeValue(value);
15402             }
15403         }
15404         return cookies;
15405     },
15406
15407     // private
15408     setCookie : function(name, value){
15409         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15410            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15411            ((this.path == null) ? "" : ("; path=" + this.path)) +
15412            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15413            ((this.secure == true) ? "; secure" : "");
15414     },
15415
15416     // private
15417     clearCookie : function(name){
15418         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15419            ((this.path == null) ? "" : ("; path=" + this.path)) +
15420            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15421            ((this.secure == true) ? "; secure" : "");
15422     }
15423 });/*
15424  * Based on:
15425  * Ext JS Library 1.1.1
15426  * Copyright(c) 2006-2007, Ext JS, LLC.
15427  *
15428  * Originally Released Under LGPL - original licence link has changed is not relivant.
15429  *
15430  * Fork - LGPL
15431  * <script type="text/javascript">
15432  */
15433  
15434
15435 /**
15436  * @class Roo.ComponentMgr
15437  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15438  * @singleton
15439  */
15440 Roo.ComponentMgr = function(){
15441     var all = new Roo.util.MixedCollection();
15442
15443     return {
15444         /**
15445          * Registers a component.
15446          * @param {Roo.Component} c The component
15447          */
15448         register : function(c){
15449             all.add(c);
15450         },
15451
15452         /**
15453          * Unregisters a component.
15454          * @param {Roo.Component} c The component
15455          */
15456         unregister : function(c){
15457             all.remove(c);
15458         },
15459
15460         /**
15461          * Returns a component by id
15462          * @param {String} id The component id
15463          */
15464         get : function(id){
15465             return all.get(id);
15466         },
15467
15468         /**
15469          * Registers a function that will be called when a specified component is added to ComponentMgr
15470          * @param {String} id The component id
15471          * @param {Funtction} fn The callback function
15472          * @param {Object} scope The scope of the callback
15473          */
15474         onAvailable : function(id, fn, scope){
15475             all.on("add", function(index, o){
15476                 if(o.id == id){
15477                     fn.call(scope || o, o);
15478                     all.un("add", fn, scope);
15479                 }
15480             });
15481         }
15482     };
15483 }();/*
15484  * Based on:
15485  * Ext JS Library 1.1.1
15486  * Copyright(c) 2006-2007, Ext JS, LLC.
15487  *
15488  * Originally Released Under LGPL - original licence link has changed is not relivant.
15489  *
15490  * Fork - LGPL
15491  * <script type="text/javascript">
15492  */
15493  
15494 /**
15495  * @class Roo.Component
15496  * @extends Roo.util.Observable
15497  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15498  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15499  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15500  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15501  * All visual components (widgets) that require rendering into a layout should subclass Component.
15502  * @constructor
15503  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15504  * 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
15505  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15506  */
15507 Roo.Component = function(config){
15508     config = config || {};
15509     if(config.tagName || config.dom || typeof config == "string"){ // element object
15510         config = {el: config, id: config.id || config};
15511     }
15512     this.initialConfig = config;
15513
15514     Roo.apply(this, config);
15515     this.addEvents({
15516         /**
15517          * @event disable
15518          * Fires after the component is disabled.
15519              * @param {Roo.Component} this
15520              */
15521         disable : true,
15522         /**
15523          * @event enable
15524          * Fires after the component is enabled.
15525              * @param {Roo.Component} this
15526              */
15527         enable : true,
15528         /**
15529          * @event beforeshow
15530          * Fires before the component is shown.  Return false to stop the show.
15531              * @param {Roo.Component} this
15532              */
15533         beforeshow : true,
15534         /**
15535          * @event show
15536          * Fires after the component is shown.
15537              * @param {Roo.Component} this
15538              */
15539         show : true,
15540         /**
15541          * @event beforehide
15542          * Fires before the component is hidden. Return false to stop the hide.
15543              * @param {Roo.Component} this
15544              */
15545         beforehide : true,
15546         /**
15547          * @event hide
15548          * Fires after the component is hidden.
15549              * @param {Roo.Component} this
15550              */
15551         hide : true,
15552         /**
15553          * @event beforerender
15554          * Fires before the component is rendered. Return false to stop the render.
15555              * @param {Roo.Component} this
15556              */
15557         beforerender : true,
15558         /**
15559          * @event render
15560          * Fires after the component is rendered.
15561              * @param {Roo.Component} this
15562              */
15563         render : true,
15564         /**
15565          * @event beforedestroy
15566          * Fires before the component is destroyed. Return false to stop the destroy.
15567              * @param {Roo.Component} this
15568              */
15569         beforedestroy : true,
15570         /**
15571          * @event destroy
15572          * Fires after the component is destroyed.
15573              * @param {Roo.Component} this
15574              */
15575         destroy : true
15576     });
15577     if(!this.id){
15578         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15579     }
15580     Roo.ComponentMgr.register(this);
15581     Roo.Component.superclass.constructor.call(this);
15582     this.initComponent();
15583     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15584         this.render(this.renderTo);
15585         delete this.renderTo;
15586     }
15587 };
15588
15589 /** @private */
15590 Roo.Component.AUTO_ID = 1000;
15591
15592 Roo.extend(Roo.Component, Roo.util.Observable, {
15593     /**
15594      * @scope Roo.Component.prototype
15595      * @type {Boolean}
15596      * true if this component is hidden. Read-only.
15597      */
15598     hidden : false,
15599     /**
15600      * @type {Boolean}
15601      * true if this component is disabled. Read-only.
15602      */
15603     disabled : false,
15604     /**
15605      * @type {Boolean}
15606      * true if this component has been rendered. Read-only.
15607      */
15608     rendered : false,
15609     
15610     /** @cfg {String} disableClass
15611      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15612      */
15613     disabledClass : "x-item-disabled",
15614         /** @cfg {Boolean} allowDomMove
15615          * Whether the component can move the Dom node when rendering (defaults to true).
15616          */
15617     allowDomMove : true,
15618     /** @cfg {String} hideMode (display|visibility)
15619      * How this component should hidden. Supported values are
15620      * "visibility" (css visibility), "offsets" (negative offset position) and
15621      * "display" (css display) - defaults to "display".
15622      */
15623     hideMode: 'display',
15624
15625     /** @private */
15626     ctype : "Roo.Component",
15627
15628     /**
15629      * @cfg {String} actionMode 
15630      * which property holds the element that used for  hide() / show() / disable() / enable()
15631      * default is 'el' for forms you probably want to set this to fieldEl 
15632      */
15633     actionMode : "el",
15634
15635     /** @private */
15636     getActionEl : function(){
15637         return this[this.actionMode];
15638     },
15639
15640     initComponent : Roo.emptyFn,
15641     /**
15642      * If this is a lazy rendering component, render it to its container element.
15643      * @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.
15644      */
15645     render : function(container, position){
15646         
15647         if(this.rendered){
15648             return this;
15649         }
15650         
15651         if(this.fireEvent("beforerender", this) === false){
15652             return false;
15653         }
15654         
15655         if(!container && this.el){
15656             this.el = Roo.get(this.el);
15657             container = this.el.dom.parentNode;
15658             this.allowDomMove = false;
15659         }
15660         this.container = Roo.get(container);
15661         this.rendered = true;
15662         if(position !== undefined){
15663             if(typeof position == 'number'){
15664                 position = this.container.dom.childNodes[position];
15665             }else{
15666                 position = Roo.getDom(position);
15667             }
15668         }
15669         this.onRender(this.container, position || null);
15670         if(this.cls){
15671             this.el.addClass(this.cls);
15672             delete this.cls;
15673         }
15674         if(this.style){
15675             this.el.applyStyles(this.style);
15676             delete this.style;
15677         }
15678         this.fireEvent("render", this);
15679         this.afterRender(this.container);
15680         if(this.hidden){
15681             this.hide();
15682         }
15683         if(this.disabled){
15684             this.disable();
15685         }
15686
15687         return this;
15688         
15689     },
15690
15691     /** @private */
15692     // default function is not really useful
15693     onRender : function(ct, position){
15694         if(this.el){
15695             this.el = Roo.get(this.el);
15696             if(this.allowDomMove !== false){
15697                 ct.dom.insertBefore(this.el.dom, position);
15698             }
15699         }
15700     },
15701
15702     /** @private */
15703     getAutoCreate : function(){
15704         var cfg = typeof this.autoCreate == "object" ?
15705                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15706         if(this.id && !cfg.id){
15707             cfg.id = this.id;
15708         }
15709         return cfg;
15710     },
15711
15712     /** @private */
15713     afterRender : Roo.emptyFn,
15714
15715     /**
15716      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15717      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15718      */
15719     destroy : function(){
15720         if(this.fireEvent("beforedestroy", this) !== false){
15721             this.purgeListeners();
15722             this.beforeDestroy();
15723             if(this.rendered){
15724                 this.el.removeAllListeners();
15725                 this.el.remove();
15726                 if(this.actionMode == "container"){
15727                     this.container.remove();
15728                 }
15729             }
15730             this.onDestroy();
15731             Roo.ComponentMgr.unregister(this);
15732             this.fireEvent("destroy", this);
15733         }
15734     },
15735
15736         /** @private */
15737     beforeDestroy : function(){
15738
15739     },
15740
15741         /** @private */
15742         onDestroy : function(){
15743
15744     },
15745
15746     /**
15747      * Returns the underlying {@link Roo.Element}.
15748      * @return {Roo.Element} The element
15749      */
15750     getEl : function(){
15751         return this.el;
15752     },
15753
15754     /**
15755      * Returns the id of this component.
15756      * @return {String}
15757      */
15758     getId : function(){
15759         return this.id;
15760     },
15761
15762     /**
15763      * Try to focus this component.
15764      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15765      * @return {Roo.Component} this
15766      */
15767     focus : function(selectText){
15768         if(this.rendered){
15769             this.el.focus();
15770             if(selectText === true){
15771                 this.el.dom.select();
15772             }
15773         }
15774         return this;
15775     },
15776
15777     /** @private */
15778     blur : function(){
15779         if(this.rendered){
15780             this.el.blur();
15781         }
15782         return this;
15783     },
15784
15785     /**
15786      * Disable this component.
15787      * @return {Roo.Component} this
15788      */
15789     disable : function(){
15790         if(this.rendered){
15791             this.onDisable();
15792         }
15793         this.disabled = true;
15794         this.fireEvent("disable", this);
15795         return this;
15796     },
15797
15798         // private
15799     onDisable : function(){
15800         this.getActionEl().addClass(this.disabledClass);
15801         this.el.dom.disabled = true;
15802     },
15803
15804     /**
15805      * Enable this component.
15806      * @return {Roo.Component} this
15807      */
15808     enable : function(){
15809         if(this.rendered){
15810             this.onEnable();
15811         }
15812         this.disabled = false;
15813         this.fireEvent("enable", this);
15814         return this;
15815     },
15816
15817         // private
15818     onEnable : function(){
15819         this.getActionEl().removeClass(this.disabledClass);
15820         this.el.dom.disabled = false;
15821     },
15822
15823     /**
15824      * Convenience function for setting disabled/enabled by boolean.
15825      * @param {Boolean} disabled
15826      */
15827     setDisabled : function(disabled){
15828         this[disabled ? "disable" : "enable"]();
15829     },
15830
15831     /**
15832      * Show this component.
15833      * @return {Roo.Component} this
15834      */
15835     show: function(){
15836         if(this.fireEvent("beforeshow", this) !== false){
15837             this.hidden = false;
15838             if(this.rendered){
15839                 this.onShow();
15840             }
15841             this.fireEvent("show", this);
15842         }
15843         return this;
15844     },
15845
15846     // private
15847     onShow : function(){
15848         var ae = this.getActionEl();
15849         if(this.hideMode == 'visibility'){
15850             ae.dom.style.visibility = "visible";
15851         }else if(this.hideMode == 'offsets'){
15852             ae.removeClass('x-hidden');
15853         }else{
15854             ae.dom.style.display = "";
15855         }
15856     },
15857
15858     /**
15859      * Hide this component.
15860      * @return {Roo.Component} this
15861      */
15862     hide: function(){
15863         if(this.fireEvent("beforehide", this) !== false){
15864             this.hidden = true;
15865             if(this.rendered){
15866                 this.onHide();
15867             }
15868             this.fireEvent("hide", this);
15869         }
15870         return this;
15871     },
15872
15873     // private
15874     onHide : function(){
15875         var ae = this.getActionEl();
15876         if(this.hideMode == 'visibility'){
15877             ae.dom.style.visibility = "hidden";
15878         }else if(this.hideMode == 'offsets'){
15879             ae.addClass('x-hidden');
15880         }else{
15881             ae.dom.style.display = "none";
15882         }
15883     },
15884
15885     /**
15886      * Convenience function to hide or show this component by boolean.
15887      * @param {Boolean} visible True to show, false to hide
15888      * @return {Roo.Component} this
15889      */
15890     setVisible: function(visible){
15891         if(visible) {
15892             this.show();
15893         }else{
15894             this.hide();
15895         }
15896         return this;
15897     },
15898
15899     /**
15900      * Returns true if this component is visible.
15901      */
15902     isVisible : function(){
15903         return this.getActionEl().isVisible();
15904     },
15905
15906     cloneConfig : function(overrides){
15907         overrides = overrides || {};
15908         var id = overrides.id || Roo.id();
15909         var cfg = Roo.applyIf(overrides, this.initialConfig);
15910         cfg.id = id; // prevent dup id
15911         return new this.constructor(cfg);
15912     }
15913 });/*
15914  * Based on:
15915  * Ext JS Library 1.1.1
15916  * Copyright(c) 2006-2007, Ext JS, LLC.
15917  *
15918  * Originally Released Under LGPL - original licence link has changed is not relivant.
15919  *
15920  * Fork - LGPL
15921  * <script type="text/javascript">
15922  */
15923
15924 /**
15925  * @class Roo.BoxComponent
15926  * @extends Roo.Component
15927  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15928  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15929  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15930  * layout containers.
15931  * @constructor
15932  * @param {Roo.Element/String/Object} config The configuration options.
15933  */
15934 Roo.BoxComponent = function(config){
15935     Roo.Component.call(this, config);
15936     this.addEvents({
15937         /**
15938          * @event resize
15939          * Fires after the component is resized.
15940              * @param {Roo.Component} this
15941              * @param {Number} adjWidth The box-adjusted width that was set
15942              * @param {Number} adjHeight The box-adjusted height that was set
15943              * @param {Number} rawWidth The width that was originally specified
15944              * @param {Number} rawHeight The height that was originally specified
15945              */
15946         resize : true,
15947         /**
15948          * @event move
15949          * Fires after the component is moved.
15950              * @param {Roo.Component} this
15951              * @param {Number} x The new x position
15952              * @param {Number} y The new y position
15953              */
15954         move : true
15955     });
15956 };
15957
15958 Roo.extend(Roo.BoxComponent, Roo.Component, {
15959     // private, set in afterRender to signify that the component has been rendered
15960     boxReady : false,
15961     // private, used to defer height settings to subclasses
15962     deferHeight: false,
15963     /** @cfg {Number} width
15964      * width (optional) size of component
15965      */
15966      /** @cfg {Number} height
15967      * height (optional) size of component
15968      */
15969      
15970     /**
15971      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15972      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15973      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15974      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15975      * @return {Roo.BoxComponent} this
15976      */
15977     setSize : function(w, h){
15978         // support for standard size objects
15979         if(typeof w == 'object'){
15980             h = w.height;
15981             w = w.width;
15982         }
15983         // not rendered
15984         if(!this.boxReady){
15985             this.width = w;
15986             this.height = h;
15987             return this;
15988         }
15989
15990         // prevent recalcs when not needed
15991         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15992             return this;
15993         }
15994         this.lastSize = {width: w, height: h};
15995
15996         var adj = this.adjustSize(w, h);
15997         var aw = adj.width, ah = adj.height;
15998         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15999             var rz = this.getResizeEl();
16000             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16001                 rz.setSize(aw, ah);
16002             }else if(!this.deferHeight && ah !== undefined){
16003                 rz.setHeight(ah);
16004             }else if(aw !== undefined){
16005                 rz.setWidth(aw);
16006             }
16007             this.onResize(aw, ah, w, h);
16008             this.fireEvent('resize', this, aw, ah, w, h);
16009         }
16010         return this;
16011     },
16012
16013     /**
16014      * Gets the current size of the component's underlying element.
16015      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16016      */
16017     getSize : function(){
16018         return this.el.getSize();
16019     },
16020
16021     /**
16022      * Gets the current XY position of the component's underlying element.
16023      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16024      * @return {Array} The XY position of the element (e.g., [100, 200])
16025      */
16026     getPosition : function(local){
16027         if(local === true){
16028             return [this.el.getLeft(true), this.el.getTop(true)];
16029         }
16030         return this.xy || this.el.getXY();
16031     },
16032
16033     /**
16034      * Gets the current box measurements of the component's underlying element.
16035      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16036      * @returns {Object} box An object in the format {x, y, width, height}
16037      */
16038     getBox : function(local){
16039         var s = this.el.getSize();
16040         if(local){
16041             s.x = this.el.getLeft(true);
16042             s.y = this.el.getTop(true);
16043         }else{
16044             var xy = this.xy || this.el.getXY();
16045             s.x = xy[0];
16046             s.y = xy[1];
16047         }
16048         return s;
16049     },
16050
16051     /**
16052      * Sets the current box measurements of the component's underlying element.
16053      * @param {Object} box An object in the format {x, y, width, height}
16054      * @returns {Roo.BoxComponent} this
16055      */
16056     updateBox : function(box){
16057         this.setSize(box.width, box.height);
16058         this.setPagePosition(box.x, box.y);
16059         return this;
16060     },
16061
16062     // protected
16063     getResizeEl : function(){
16064         return this.resizeEl || this.el;
16065     },
16066
16067     // protected
16068     getPositionEl : function(){
16069         return this.positionEl || this.el;
16070     },
16071
16072     /**
16073      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16074      * This method fires the move event.
16075      * @param {Number} left The new left
16076      * @param {Number} top The new top
16077      * @returns {Roo.BoxComponent} this
16078      */
16079     setPosition : function(x, y){
16080         this.x = x;
16081         this.y = y;
16082         if(!this.boxReady){
16083             return this;
16084         }
16085         var adj = this.adjustPosition(x, y);
16086         var ax = adj.x, ay = adj.y;
16087
16088         var el = this.getPositionEl();
16089         if(ax !== undefined || ay !== undefined){
16090             if(ax !== undefined && ay !== undefined){
16091                 el.setLeftTop(ax, ay);
16092             }else if(ax !== undefined){
16093                 el.setLeft(ax);
16094             }else if(ay !== undefined){
16095                 el.setTop(ay);
16096             }
16097             this.onPosition(ax, ay);
16098             this.fireEvent('move', this, ax, ay);
16099         }
16100         return this;
16101     },
16102
16103     /**
16104      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16105      * This method fires the move event.
16106      * @param {Number} x The new x position
16107      * @param {Number} y The new y position
16108      * @returns {Roo.BoxComponent} this
16109      */
16110     setPagePosition : function(x, y){
16111         this.pageX = x;
16112         this.pageY = y;
16113         if(!this.boxReady){
16114             return;
16115         }
16116         if(x === undefined || y === undefined){ // cannot translate undefined points
16117             return;
16118         }
16119         var p = this.el.translatePoints(x, y);
16120         this.setPosition(p.left, p.top);
16121         return this;
16122     },
16123
16124     // private
16125     onRender : function(ct, position){
16126         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16127         if(this.resizeEl){
16128             this.resizeEl = Roo.get(this.resizeEl);
16129         }
16130         if(this.positionEl){
16131             this.positionEl = Roo.get(this.positionEl);
16132         }
16133     },
16134
16135     // private
16136     afterRender : function(){
16137         Roo.BoxComponent.superclass.afterRender.call(this);
16138         this.boxReady = true;
16139         this.setSize(this.width, this.height);
16140         if(this.x || this.y){
16141             this.setPosition(this.x, this.y);
16142         }
16143         if(this.pageX || this.pageY){
16144             this.setPagePosition(this.pageX, this.pageY);
16145         }
16146     },
16147
16148     /**
16149      * Force the component's size to recalculate based on the underlying element's current height and width.
16150      * @returns {Roo.BoxComponent} this
16151      */
16152     syncSize : function(){
16153         delete this.lastSize;
16154         this.setSize(this.el.getWidth(), this.el.getHeight());
16155         return this;
16156     },
16157
16158     /**
16159      * Called after the component is resized, this method is empty by default but can be implemented by any
16160      * subclass that needs to perform custom logic after a resize occurs.
16161      * @param {Number} adjWidth The box-adjusted width that was set
16162      * @param {Number} adjHeight The box-adjusted height that was set
16163      * @param {Number} rawWidth The width that was originally specified
16164      * @param {Number} rawHeight The height that was originally specified
16165      */
16166     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16167
16168     },
16169
16170     /**
16171      * Called after the component is moved, this method is empty by default but can be implemented by any
16172      * subclass that needs to perform custom logic after a move occurs.
16173      * @param {Number} x The new x position
16174      * @param {Number} y The new y position
16175      */
16176     onPosition : function(x, y){
16177
16178     },
16179
16180     // private
16181     adjustSize : function(w, h){
16182         if(this.autoWidth){
16183             w = 'auto';
16184         }
16185         if(this.autoHeight){
16186             h = 'auto';
16187         }
16188         return {width : w, height: h};
16189     },
16190
16191     // private
16192     adjustPosition : function(x, y){
16193         return {x : x, y: y};
16194     }
16195 });/*
16196  * Based on:
16197  * Ext JS Library 1.1.1
16198  * Copyright(c) 2006-2007, Ext JS, LLC.
16199  *
16200  * Originally Released Under LGPL - original licence link has changed is not relivant.
16201  *
16202  * Fork - LGPL
16203  * <script type="text/javascript">
16204  */
16205  (function(){ 
16206 /**
16207  * @class Roo.Layer
16208  * @extends Roo.Element
16209  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16210  * automatic maintaining of shadow/shim positions.
16211  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16212  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16213  * you can pass a string with a CSS class name. False turns off the shadow.
16214  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16215  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16216  * @cfg {String} cls CSS class to add to the element
16217  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16218  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16219  * @constructor
16220  * @param {Object} config An object with config options.
16221  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16222  */
16223
16224 Roo.Layer = function(config, existingEl){
16225     config = config || {};
16226     var dh = Roo.DomHelper;
16227     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16228     if(existingEl){
16229         this.dom = Roo.getDom(existingEl);
16230     }
16231     if(!this.dom){
16232         var o = config.dh || {tag: "div", cls: "x-layer"};
16233         this.dom = dh.append(pel, o);
16234     }
16235     if(config.cls){
16236         this.addClass(config.cls);
16237     }
16238     this.constrain = config.constrain !== false;
16239     this.visibilityMode = Roo.Element.VISIBILITY;
16240     if(config.id){
16241         this.id = this.dom.id = config.id;
16242     }else{
16243         this.id = Roo.id(this.dom);
16244     }
16245     this.zindex = config.zindex || this.getZIndex();
16246     this.position("absolute", this.zindex);
16247     if(config.shadow){
16248         this.shadowOffset = config.shadowOffset || 4;
16249         this.shadow = new Roo.Shadow({
16250             offset : this.shadowOffset,
16251             mode : config.shadow
16252         });
16253     }else{
16254         this.shadowOffset = 0;
16255     }
16256     this.useShim = config.shim !== false && Roo.useShims;
16257     this.useDisplay = config.useDisplay;
16258     this.hide();
16259 };
16260
16261 var supr = Roo.Element.prototype;
16262
16263 // shims are shared among layer to keep from having 100 iframes
16264 var shims = [];
16265
16266 Roo.extend(Roo.Layer, Roo.Element, {
16267
16268     getZIndex : function(){
16269         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16270     },
16271
16272     getShim : function(){
16273         if(!this.useShim){
16274             return null;
16275         }
16276         if(this.shim){
16277             return this.shim;
16278         }
16279         var shim = shims.shift();
16280         if(!shim){
16281             shim = this.createShim();
16282             shim.enableDisplayMode('block');
16283             shim.dom.style.display = 'none';
16284             shim.dom.style.visibility = 'visible';
16285         }
16286         var pn = this.dom.parentNode;
16287         if(shim.dom.parentNode != pn){
16288             pn.insertBefore(shim.dom, this.dom);
16289         }
16290         shim.setStyle('z-index', this.getZIndex()-2);
16291         this.shim = shim;
16292         return shim;
16293     },
16294
16295     hideShim : function(){
16296         if(this.shim){
16297             this.shim.setDisplayed(false);
16298             shims.push(this.shim);
16299             delete this.shim;
16300         }
16301     },
16302
16303     disableShadow : function(){
16304         if(this.shadow){
16305             this.shadowDisabled = true;
16306             this.shadow.hide();
16307             this.lastShadowOffset = this.shadowOffset;
16308             this.shadowOffset = 0;
16309         }
16310     },
16311
16312     enableShadow : function(show){
16313         if(this.shadow){
16314             this.shadowDisabled = false;
16315             this.shadowOffset = this.lastShadowOffset;
16316             delete this.lastShadowOffset;
16317             if(show){
16318                 this.sync(true);
16319             }
16320         }
16321     },
16322
16323     // private
16324     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16325     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16326     sync : function(doShow){
16327         var sw = this.shadow;
16328         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16329             var sh = this.getShim();
16330
16331             var w = this.getWidth(),
16332                 h = this.getHeight();
16333
16334             var l = this.getLeft(true),
16335                 t = this.getTop(true);
16336
16337             if(sw && !this.shadowDisabled){
16338                 if(doShow && !sw.isVisible()){
16339                     sw.show(this);
16340                 }else{
16341                     sw.realign(l, t, w, h);
16342                 }
16343                 if(sh){
16344                     if(doShow){
16345                        sh.show();
16346                     }
16347                     // fit the shim behind the shadow, so it is shimmed too
16348                     var a = sw.adjusts, s = sh.dom.style;
16349                     s.left = (Math.min(l, l+a.l))+"px";
16350                     s.top = (Math.min(t, t+a.t))+"px";
16351                     s.width = (w+a.w)+"px";
16352                     s.height = (h+a.h)+"px";
16353                 }
16354             }else if(sh){
16355                 if(doShow){
16356                    sh.show();
16357                 }
16358                 sh.setSize(w, h);
16359                 sh.setLeftTop(l, t);
16360             }
16361             
16362         }
16363     },
16364
16365     // private
16366     destroy : function(){
16367         this.hideShim();
16368         if(this.shadow){
16369             this.shadow.hide();
16370         }
16371         this.removeAllListeners();
16372         var pn = this.dom.parentNode;
16373         if(pn){
16374             pn.removeChild(this.dom);
16375         }
16376         Roo.Element.uncache(this.id);
16377     },
16378
16379     remove : function(){
16380         this.destroy();
16381     },
16382
16383     // private
16384     beginUpdate : function(){
16385         this.updating = true;
16386     },
16387
16388     // private
16389     endUpdate : function(){
16390         this.updating = false;
16391         this.sync(true);
16392     },
16393
16394     // private
16395     hideUnders : function(negOffset){
16396         if(this.shadow){
16397             this.shadow.hide();
16398         }
16399         this.hideShim();
16400     },
16401
16402     // private
16403     constrainXY : function(){
16404         if(this.constrain){
16405             var vw = Roo.lib.Dom.getViewWidth(),
16406                 vh = Roo.lib.Dom.getViewHeight();
16407             var s = Roo.get(document).getScroll();
16408
16409             var xy = this.getXY();
16410             var x = xy[0], y = xy[1];   
16411             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16412             // only move it if it needs it
16413             var moved = false;
16414             // first validate right/bottom
16415             if((x + w) > vw+s.left){
16416                 x = vw - w - this.shadowOffset;
16417                 moved = true;
16418             }
16419             if((y + h) > vh+s.top){
16420                 y = vh - h - this.shadowOffset;
16421                 moved = true;
16422             }
16423             // then make sure top/left isn't negative
16424             if(x < s.left){
16425                 x = s.left;
16426                 moved = true;
16427             }
16428             if(y < s.top){
16429                 y = s.top;
16430                 moved = true;
16431             }
16432             if(moved){
16433                 if(this.avoidY){
16434                     var ay = this.avoidY;
16435                     if(y <= ay && (y+h) >= ay){
16436                         y = ay-h-5;   
16437                     }
16438                 }
16439                 xy = [x, y];
16440                 this.storeXY(xy);
16441                 supr.setXY.call(this, xy);
16442                 this.sync();
16443             }
16444         }
16445     },
16446
16447     isVisible : function(){
16448         return this.visible;    
16449     },
16450
16451     // private
16452     showAction : function(){
16453         this.visible = true; // track visibility to prevent getStyle calls
16454         if(this.useDisplay === true){
16455             this.setDisplayed("");
16456         }else if(this.lastXY){
16457             supr.setXY.call(this, this.lastXY);
16458         }else if(this.lastLT){
16459             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16460         }
16461     },
16462
16463     // private
16464     hideAction : function(){
16465         this.visible = false;
16466         if(this.useDisplay === true){
16467             this.setDisplayed(false);
16468         }else{
16469             this.setLeftTop(-10000,-10000);
16470         }
16471     },
16472
16473     // overridden Element method
16474     setVisible : function(v, a, d, c, e){
16475         if(v){
16476             this.showAction();
16477         }
16478         if(a && v){
16479             var cb = function(){
16480                 this.sync(true);
16481                 if(c){
16482                     c();
16483                 }
16484             }.createDelegate(this);
16485             supr.setVisible.call(this, true, true, d, cb, e);
16486         }else{
16487             if(!v){
16488                 this.hideUnders(true);
16489             }
16490             var cb = c;
16491             if(a){
16492                 cb = function(){
16493                     this.hideAction();
16494                     if(c){
16495                         c();
16496                     }
16497                 }.createDelegate(this);
16498             }
16499             supr.setVisible.call(this, v, a, d, cb, e);
16500             if(v){
16501                 this.sync(true);
16502             }else if(!a){
16503                 this.hideAction();
16504             }
16505         }
16506     },
16507
16508     storeXY : function(xy){
16509         delete this.lastLT;
16510         this.lastXY = xy;
16511     },
16512
16513     storeLeftTop : function(left, top){
16514         delete this.lastXY;
16515         this.lastLT = [left, top];
16516     },
16517
16518     // private
16519     beforeFx : function(){
16520         this.beforeAction();
16521         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16522     },
16523
16524     // private
16525     afterFx : function(){
16526         Roo.Layer.superclass.afterFx.apply(this, arguments);
16527         this.sync(this.isVisible());
16528     },
16529
16530     // private
16531     beforeAction : function(){
16532         if(!this.updating && this.shadow){
16533             this.shadow.hide();
16534         }
16535     },
16536
16537     // overridden Element method
16538     setLeft : function(left){
16539         this.storeLeftTop(left, this.getTop(true));
16540         supr.setLeft.apply(this, arguments);
16541         this.sync();
16542     },
16543
16544     setTop : function(top){
16545         this.storeLeftTop(this.getLeft(true), top);
16546         supr.setTop.apply(this, arguments);
16547         this.sync();
16548     },
16549
16550     setLeftTop : function(left, top){
16551         this.storeLeftTop(left, top);
16552         supr.setLeftTop.apply(this, arguments);
16553         this.sync();
16554     },
16555
16556     setXY : function(xy, a, d, c, e){
16557         this.fixDisplay();
16558         this.beforeAction();
16559         this.storeXY(xy);
16560         var cb = this.createCB(c);
16561         supr.setXY.call(this, xy, a, d, cb, e);
16562         if(!a){
16563             cb();
16564         }
16565     },
16566
16567     // private
16568     createCB : function(c){
16569         var el = this;
16570         return function(){
16571             el.constrainXY();
16572             el.sync(true);
16573             if(c){
16574                 c();
16575             }
16576         };
16577     },
16578
16579     // overridden Element method
16580     setX : function(x, a, d, c, e){
16581         this.setXY([x, this.getY()], a, d, c, e);
16582     },
16583
16584     // overridden Element method
16585     setY : function(y, a, d, c, e){
16586         this.setXY([this.getX(), y], a, d, c, e);
16587     },
16588
16589     // overridden Element method
16590     setSize : function(w, h, a, d, c, e){
16591         this.beforeAction();
16592         var cb = this.createCB(c);
16593         supr.setSize.call(this, w, h, a, d, cb, e);
16594         if(!a){
16595             cb();
16596         }
16597     },
16598
16599     // overridden Element method
16600     setWidth : function(w, a, d, c, e){
16601         this.beforeAction();
16602         var cb = this.createCB(c);
16603         supr.setWidth.call(this, w, a, d, cb, e);
16604         if(!a){
16605             cb();
16606         }
16607     },
16608
16609     // overridden Element method
16610     setHeight : function(h, a, d, c, e){
16611         this.beforeAction();
16612         var cb = this.createCB(c);
16613         supr.setHeight.call(this, h, a, d, cb, e);
16614         if(!a){
16615             cb();
16616         }
16617     },
16618
16619     // overridden Element method
16620     setBounds : function(x, y, w, h, a, d, c, e){
16621         this.beforeAction();
16622         var cb = this.createCB(c);
16623         if(!a){
16624             this.storeXY([x, y]);
16625             supr.setXY.call(this, [x, y]);
16626             supr.setSize.call(this, w, h, a, d, cb, e);
16627             cb();
16628         }else{
16629             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16630         }
16631         return this;
16632     },
16633     
16634     /**
16635      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16636      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16637      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16638      * @param {Number} zindex The new z-index to set
16639      * @return {this} The Layer
16640      */
16641     setZIndex : function(zindex){
16642         this.zindex = zindex;
16643         this.setStyle("z-index", zindex + 2);
16644         if(this.shadow){
16645             this.shadow.setZIndex(zindex + 1);
16646         }
16647         if(this.shim){
16648             this.shim.setStyle("z-index", zindex);
16649         }
16650     }
16651 });
16652 })();/*
16653  * Original code for Roojs - LGPL
16654  * <script type="text/javascript">
16655  */
16656  
16657 /**
16658  * @class Roo.XComponent
16659  * A delayed Element creator...
16660  * Or a way to group chunks of interface together.
16661  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16662  *  used in conjunction with XComponent.build() it will create an instance of each element,
16663  *  then call addxtype() to build the User interface.
16664  * 
16665  * Mypart.xyx = new Roo.XComponent({
16666
16667     parent : 'Mypart.xyz', // empty == document.element.!!
16668     order : '001',
16669     name : 'xxxx'
16670     region : 'xxxx'
16671     disabled : function() {} 
16672      
16673     tree : function() { // return an tree of xtype declared components
16674         var MODULE = this;
16675         return 
16676         {
16677             xtype : 'NestedLayoutPanel',
16678             // technicall
16679         }
16680      ]
16681  *})
16682  *
16683  *
16684  * It can be used to build a big heiracy, with parent etc.
16685  * or you can just use this to render a single compoent to a dom element
16686  * MYPART.render(Roo.Element | String(id) | dom_element )
16687  *
16688  *
16689  * Usage patterns.
16690  *
16691  * Classic Roo
16692  *
16693  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16694  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16695  *
16696  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16697  *
16698  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16699  * - if mulitple topModules exist, the last one is defined as the top module.
16700  *
16701  * Embeded Roo
16702  * 
16703  * When the top level or multiple modules are to embedded into a existing HTML page,
16704  * the parent element can container '#id' of the element where the module will be drawn.
16705  *
16706  * Bootstrap Roo
16707  *
16708  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16709  * it relies more on a include mechanism, where sub modules are included into an outer page.
16710  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16711  * 
16712  * Bootstrap Roo Included elements
16713  *
16714  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16715  * hence confusing the component builder as it thinks there are multiple top level elements. 
16716  *
16717  * String Over-ride & Translations
16718  *
16719  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16720  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16721  * are needed. @see Roo.XComponent.overlayString  
16722  * 
16723  * 
16724  * 
16725  * @extends Roo.util.Observable
16726  * @constructor
16727  * @param cfg {Object} configuration of component
16728  * 
16729  */
16730 Roo.XComponent = function(cfg) {
16731     Roo.apply(this, cfg);
16732     this.addEvents({ 
16733         /**
16734              * @event built
16735              * Fires when this the componnt is built
16736              * @param {Roo.XComponent} c the component
16737              */
16738         'built' : true
16739         
16740     });
16741     this.region = this.region || 'center'; // default..
16742     Roo.XComponent.register(this);
16743     this.modules = false;
16744     this.el = false; // where the layout goes..
16745     
16746     
16747 }
16748 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16749     /**
16750      * @property el
16751      * The created element (with Roo.factory())
16752      * @type {Roo.Layout}
16753      */
16754     el  : false,
16755     
16756     /**
16757      * @property el
16758      * for BC  - use el in new code
16759      * @type {Roo.Layout}
16760      */
16761     panel : false,
16762     
16763     /**
16764      * @property layout
16765      * for BC  - use el in new code
16766      * @type {Roo.Layout}
16767      */
16768     layout : false,
16769     
16770      /**
16771      * @cfg {Function|boolean} disabled
16772      * If this module is disabled by some rule, return true from the funtion
16773      */
16774     disabled : false,
16775     
16776     /**
16777      * @cfg {String} parent 
16778      * Name of parent element which it get xtype added to..
16779      */
16780     parent: false,
16781     
16782     /**
16783      * @cfg {String} order
16784      * Used to set the order in which elements are created (usefull for multiple tabs)
16785      */
16786     
16787     order : false,
16788     /**
16789      * @cfg {String} name
16790      * String to display while loading.
16791      */
16792     name : false,
16793     /**
16794      * @cfg {String} region
16795      * Region to render component to (defaults to center)
16796      */
16797     region : 'center',
16798     
16799     /**
16800      * @cfg {Array} items
16801      * A single item array - the first element is the root of the tree..
16802      * It's done this way to stay compatible with the Xtype system...
16803      */
16804     items : false,
16805     
16806     /**
16807      * @property _tree
16808      * The method that retuns the tree of parts that make up this compoennt 
16809      * @type {function}
16810      */
16811     _tree  : false,
16812     
16813      /**
16814      * render
16815      * render element to dom or tree
16816      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16817      */
16818     
16819     render : function(el)
16820     {
16821         
16822         el = el || false;
16823         var hp = this.parent ? 1 : 0;
16824         Roo.debug &&  Roo.log(this);
16825         
16826         var tree = this._tree ? this._tree() : this.tree();
16827
16828         
16829         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16830             // if parent is a '#.....' string, then let's use that..
16831             var ename = this.parent.substr(1);
16832             this.parent = false;
16833             Roo.debug && Roo.log(ename);
16834             switch (ename) {
16835                 case 'bootstrap-body':
16836                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16837                         // this is the BorderLayout standard?
16838                        this.parent = { el : true };
16839                        break;
16840                     }
16841                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16842                         // need to insert stuff...
16843                         this.parent =  {
16844                              el : new Roo.bootstrap.layout.Border({
16845                                  el : document.body, 
16846                      
16847                                  center: {
16848                                     titlebar: false,
16849                                     autoScroll:false,
16850                                     closeOnTab: true,
16851                                     tabPosition: 'top',
16852                                       //resizeTabs: true,
16853                                     alwaysShowTabs: true,
16854                                     hideTabs: false
16855                                      //minTabWidth: 140
16856                                  }
16857                              })
16858                         
16859                          };
16860                          break;
16861                     }
16862                          
16863                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16864                         this.parent = { el :  new  Roo.bootstrap.Body() };
16865                         Roo.debug && Roo.log("setting el to doc body");
16866                          
16867                     } else {
16868                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16869                     }
16870                     break;
16871                 case 'bootstrap':
16872                     this.parent = { el : true};
16873                     // fall through
16874                 default:
16875                     el = Roo.get(ename);
16876                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16877                         this.parent = { el : true};
16878                     }
16879                     
16880                     break;
16881             }
16882                 
16883             
16884             if (!el && !this.parent) {
16885                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16886                 return;
16887             }
16888         }
16889         
16890         Roo.debug && Roo.log("EL:");
16891         Roo.debug && Roo.log(el);
16892         Roo.debug && Roo.log("this.parent.el:");
16893         Roo.debug && Roo.log(this.parent.el);
16894         
16895
16896         // altertive root elements ??? - we need a better way to indicate these.
16897         var is_alt = Roo.XComponent.is_alt ||
16898                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16899                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16900                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16901         
16902         
16903         
16904         if (!this.parent && is_alt) {
16905             //el = Roo.get(document.body);
16906             this.parent = { el : true };
16907         }
16908             
16909             
16910         
16911         if (!this.parent) {
16912             
16913             Roo.debug && Roo.log("no parent - creating one");
16914             
16915             el = el ? Roo.get(el) : false;      
16916             
16917             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16918                 
16919                 this.parent =  {
16920                     el : new Roo.bootstrap.layout.Border({
16921                         el: el || document.body,
16922                     
16923                         center: {
16924                             titlebar: false,
16925                             autoScroll:false,
16926                             closeOnTab: true,
16927                             tabPosition: 'top',
16928                              //resizeTabs: true,
16929                             alwaysShowTabs: false,
16930                             hideTabs: true,
16931                             minTabWidth: 140,
16932                             overflow: 'visible'
16933                          }
16934                      })
16935                 };
16936             } else {
16937             
16938                 // it's a top level one..
16939                 this.parent =  {
16940                     el : new Roo.BorderLayout(el || document.body, {
16941                         center: {
16942                             titlebar: false,
16943                             autoScroll:false,
16944                             closeOnTab: true,
16945                             tabPosition: 'top',
16946                              //resizeTabs: true,
16947                             alwaysShowTabs: el && hp? false :  true,
16948                             hideTabs: el || !hp ? true :  false,
16949                             minTabWidth: 140
16950                          }
16951                     })
16952                 };
16953             }
16954         }
16955         
16956         if (!this.parent.el) {
16957                 // probably an old style ctor, which has been disabled.
16958                 return;
16959
16960         }
16961                 // The 'tree' method is  '_tree now' 
16962             
16963         tree.region = tree.region || this.region;
16964         var is_body = false;
16965         if (this.parent.el === true) {
16966             // bootstrap... - body..
16967             if (el) {
16968                 tree.el = el;
16969             }
16970             this.parent.el = Roo.factory(tree);
16971             is_body = true;
16972         }
16973         
16974         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16975         this.fireEvent('built', this);
16976         
16977         this.panel = this.el;
16978         this.layout = this.panel.layout;
16979         this.parentLayout = this.parent.layout  || false;  
16980          
16981     }
16982     
16983 });
16984
16985 Roo.apply(Roo.XComponent, {
16986     /**
16987      * @property  hideProgress
16988      * true to disable the building progress bar.. usefull on single page renders.
16989      * @type Boolean
16990      */
16991     hideProgress : false,
16992     /**
16993      * @property  buildCompleted
16994      * True when the builder has completed building the interface.
16995      * @type Boolean
16996      */
16997     buildCompleted : false,
16998      
16999     /**
17000      * @property  topModule
17001      * the upper most module - uses document.element as it's constructor.
17002      * @type Object
17003      */
17004      
17005     topModule  : false,
17006       
17007     /**
17008      * @property  modules
17009      * array of modules to be created by registration system.
17010      * @type {Array} of Roo.XComponent
17011      */
17012     
17013     modules : [],
17014     /**
17015      * @property  elmodules
17016      * array of modules to be created by which use #ID 
17017      * @type {Array} of Roo.XComponent
17018      */
17019      
17020     elmodules : [],
17021
17022      /**
17023      * @property  is_alt
17024      * Is an alternative Root - normally used by bootstrap or other systems,
17025      *    where the top element in the tree can wrap 'body' 
17026      * @type {boolean}  (default false)
17027      */
17028      
17029     is_alt : false,
17030     /**
17031      * @property  build_from_html
17032      * Build elements from html - used by bootstrap HTML stuff 
17033      *    - this is cleared after build is completed
17034      * @type {boolean}    (default false)
17035      */
17036      
17037     build_from_html : false,
17038     /**
17039      * Register components to be built later.
17040      *
17041      * This solves the following issues
17042      * - Building is not done on page load, but after an authentication process has occured.
17043      * - Interface elements are registered on page load
17044      * - Parent Interface elements may not be loaded before child, so this handles that..
17045      * 
17046      *
17047      * example:
17048      * 
17049      * MyApp.register({
17050           order : '000001',
17051           module : 'Pman.Tab.projectMgr',
17052           region : 'center',
17053           parent : 'Pman.layout',
17054           disabled : false,  // or use a function..
17055         })
17056      
17057      * * @param {Object} details about module
17058      */
17059     register : function(obj) {
17060                 
17061         Roo.XComponent.event.fireEvent('register', obj);
17062         switch(typeof(obj.disabled) ) {
17063                 
17064             case 'undefined':
17065                 break;
17066             
17067             case 'function':
17068                 if ( obj.disabled() ) {
17069                         return;
17070                 }
17071                 break;
17072             
17073             default:
17074                 if (obj.disabled || obj.region == '#disabled') {
17075                         return;
17076                 }
17077                 break;
17078         }
17079                 
17080         this.modules.push(obj);
17081          
17082     },
17083     /**
17084      * convert a string to an object..
17085      * eg. 'AAA.BBB' -> finds AAA.BBB
17086
17087      */
17088     
17089     toObject : function(str)
17090     {
17091         if (!str || typeof(str) == 'object') {
17092             return str;
17093         }
17094         if (str.substring(0,1) == '#') {
17095             return str;
17096         }
17097
17098         var ar = str.split('.');
17099         var rt, o;
17100         rt = ar.shift();
17101             /** eval:var:o */
17102         try {
17103             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17104         } catch (e) {
17105             throw "Module not found : " + str;
17106         }
17107         
17108         if (o === false) {
17109             throw "Module not found : " + str;
17110         }
17111         Roo.each(ar, function(e) {
17112             if (typeof(o[e]) == 'undefined') {
17113                 throw "Module not found : " + str;
17114             }
17115             o = o[e];
17116         });
17117         
17118         return o;
17119         
17120     },
17121     
17122     
17123     /**
17124      * move modules into their correct place in the tree..
17125      * 
17126      */
17127     preBuild : function ()
17128     {
17129         var _t = this;
17130         Roo.each(this.modules , function (obj)
17131         {
17132             Roo.XComponent.event.fireEvent('beforebuild', obj);
17133             
17134             var opar = obj.parent;
17135             try { 
17136                 obj.parent = this.toObject(opar);
17137             } catch(e) {
17138                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17139                 return;
17140             }
17141             
17142             if (!obj.parent) {
17143                 Roo.debug && Roo.log("GOT top level module");
17144                 Roo.debug && Roo.log(obj);
17145                 obj.modules = new Roo.util.MixedCollection(false, 
17146                     function(o) { return o.order + '' }
17147                 );
17148                 this.topModule = obj;
17149                 return;
17150             }
17151                         // parent is a string (usually a dom element name..)
17152             if (typeof(obj.parent) == 'string') {
17153                 this.elmodules.push(obj);
17154                 return;
17155             }
17156             if (obj.parent.constructor != Roo.XComponent) {
17157                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17158             }
17159             if (!obj.parent.modules) {
17160                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17161                     function(o) { return o.order + '' }
17162                 );
17163             }
17164             if (obj.parent.disabled) {
17165                 obj.disabled = true;
17166             }
17167             obj.parent.modules.add(obj);
17168         }, this);
17169     },
17170     
17171      /**
17172      * make a list of modules to build.
17173      * @return {Array} list of modules. 
17174      */ 
17175     
17176     buildOrder : function()
17177     {
17178         var _this = this;
17179         var cmp = function(a,b) {   
17180             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17181         };
17182         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17183             throw "No top level modules to build";
17184         }
17185         
17186         // make a flat list in order of modules to build.
17187         var mods = this.topModule ? [ this.topModule ] : [];
17188                 
17189         
17190         // elmodules (is a list of DOM based modules )
17191         Roo.each(this.elmodules, function(e) {
17192             mods.push(e);
17193             if (!this.topModule &&
17194                 typeof(e.parent) == 'string' &&
17195                 e.parent.substring(0,1) == '#' &&
17196                 Roo.get(e.parent.substr(1))
17197                ) {
17198                 
17199                 _this.topModule = e;
17200             }
17201             
17202         });
17203
17204         
17205         // add modules to their parents..
17206         var addMod = function(m) {
17207             Roo.debug && Roo.log("build Order: add: " + m.name);
17208                 
17209             mods.push(m);
17210             if (m.modules && !m.disabled) {
17211                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17212                 m.modules.keySort('ASC',  cmp );
17213                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17214     
17215                 m.modules.each(addMod);
17216             } else {
17217                 Roo.debug && Roo.log("build Order: no child modules");
17218             }
17219             // not sure if this is used any more..
17220             if (m.finalize) {
17221                 m.finalize.name = m.name + " (clean up) ";
17222                 mods.push(m.finalize);
17223             }
17224             
17225         }
17226         if (this.topModule && this.topModule.modules) { 
17227             this.topModule.modules.keySort('ASC',  cmp );
17228             this.topModule.modules.each(addMod);
17229         } 
17230         return mods;
17231     },
17232     
17233      /**
17234      * Build the registered modules.
17235      * @param {Object} parent element.
17236      * @param {Function} optional method to call after module has been added.
17237      * 
17238      */ 
17239    
17240     build : function(opts) 
17241     {
17242         
17243         if (typeof(opts) != 'undefined') {
17244             Roo.apply(this,opts);
17245         }
17246         
17247         this.preBuild();
17248         var mods = this.buildOrder();
17249       
17250         //this.allmods = mods;
17251         //Roo.debug && Roo.log(mods);
17252         //return;
17253         if (!mods.length) { // should not happen
17254             throw "NO modules!!!";
17255         }
17256         
17257         
17258         var msg = "Building Interface...";
17259         // flash it up as modal - so we store the mask!?
17260         if (!this.hideProgress && Roo.MessageBox) {
17261             Roo.MessageBox.show({ title: 'loading' });
17262             Roo.MessageBox.show({
17263                title: "Please wait...",
17264                msg: msg,
17265                width:450,
17266                progress:true,
17267                buttons : false,
17268                closable:false,
17269                modal: false
17270               
17271             });
17272         }
17273         var total = mods.length;
17274         
17275         var _this = this;
17276         var progressRun = function() {
17277             if (!mods.length) {
17278                 Roo.debug && Roo.log('hide?');
17279                 if (!this.hideProgress && Roo.MessageBox) {
17280                     Roo.MessageBox.hide();
17281                 }
17282                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17283                 
17284                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17285                 
17286                 // THE END...
17287                 return false;   
17288             }
17289             
17290             var m = mods.shift();
17291             
17292             
17293             Roo.debug && Roo.log(m);
17294             // not sure if this is supported any more.. - modules that are are just function
17295             if (typeof(m) == 'function') { 
17296                 m.call(this);
17297                 return progressRun.defer(10, _this);
17298             } 
17299             
17300             
17301             msg = "Building Interface " + (total  - mods.length) + 
17302                     " of " + total + 
17303                     (m.name ? (' - ' + m.name) : '');
17304                         Roo.debug && Roo.log(msg);
17305             if (!_this.hideProgress &&  Roo.MessageBox) { 
17306                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17307             }
17308             
17309          
17310             // is the module disabled?
17311             var disabled = (typeof(m.disabled) == 'function') ?
17312                 m.disabled.call(m.module.disabled) : m.disabled;    
17313             
17314             
17315             if (disabled) {
17316                 return progressRun(); // we do not update the display!
17317             }
17318             
17319             // now build 
17320             
17321                         
17322                         
17323             m.render();
17324             // it's 10 on top level, and 1 on others??? why...
17325             return progressRun.defer(10, _this);
17326              
17327         }
17328         progressRun.defer(1, _this);
17329      
17330         
17331         
17332     },
17333     /**
17334      * Overlay a set of modified strings onto a component
17335      * This is dependant on our builder exporting the strings and 'named strings' elements.
17336      * 
17337      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17338      * @param {Object} associative array of 'named' string and it's new value.
17339      * 
17340      */
17341         overlayStrings : function( component, strings )
17342     {
17343         if (typeof(component['_named_strings']) == 'undefined') {
17344             throw "ERROR: component does not have _named_strings";
17345         }
17346         for ( var k in strings ) {
17347             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17348             if (md !== false) {
17349                 component['_strings'][md] = strings[k];
17350             } else {
17351                 Roo.log('could not find named string: ' + k + ' in');
17352                 Roo.log(component);
17353             }
17354             
17355         }
17356         
17357     },
17358     
17359         
17360         /**
17361          * Event Object.
17362          *
17363          *
17364          */
17365         event: false, 
17366     /**
17367          * wrapper for event.on - aliased later..  
17368          * Typically use to register a event handler for register:
17369          *
17370          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17371          *
17372          */
17373     on : false
17374    
17375     
17376     
17377 });
17378
17379 Roo.XComponent.event = new Roo.util.Observable({
17380                 events : { 
17381                         /**
17382                          * @event register
17383                          * Fires when an Component is registered,
17384                          * set the disable property on the Component to stop registration.
17385                          * @param {Roo.XComponent} c the component being registerd.
17386                          * 
17387                          */
17388                         'register' : true,
17389             /**
17390                          * @event beforebuild
17391                          * Fires before each Component is built
17392                          * can be used to apply permissions.
17393                          * @param {Roo.XComponent} c the component being registerd.
17394                          * 
17395                          */
17396                         'beforebuild' : true,
17397                         /**
17398                          * @event buildcomplete
17399                          * Fires on the top level element when all elements have been built
17400                          * @param {Roo.XComponent} the top level component.
17401                          */
17402                         'buildcomplete' : true
17403                         
17404                 }
17405 });
17406
17407 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17408  //
17409  /**
17410  * marked - a markdown parser
17411  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17412  * https://github.com/chjj/marked
17413  */
17414
17415
17416 /**
17417  *
17418  * Roo.Markdown - is a very crude wrapper around marked..
17419  *
17420  * usage:
17421  * 
17422  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17423  * 
17424  * Note: move the sample code to the bottom of this
17425  * file before uncommenting it.
17426  *
17427  */
17428
17429 Roo.Markdown = {};
17430 Roo.Markdown.toHtml = function(text) {
17431     
17432     var c = new Roo.Markdown.marked.setOptions({
17433             renderer: new Roo.Markdown.marked.Renderer(),
17434             gfm: true,
17435             tables: true,
17436             breaks: false,
17437             pedantic: false,
17438             sanitize: false,
17439             smartLists: true,
17440             smartypants: false
17441           });
17442     // A FEW HACKS!!?
17443     
17444     text = text.replace(/\\\n/g,' ');
17445     return Roo.Markdown.marked(text);
17446 };
17447 //
17448 // converter
17449 //
17450 // Wraps all "globals" so that the only thing
17451 // exposed is makeHtml().
17452 //
17453 (function() {
17454     
17455      /**
17456          * eval:var:escape
17457          * eval:var:unescape
17458          * eval:var:replace
17459          */
17460       
17461     /**
17462      * Helpers
17463      */
17464     
17465     var escape = function (html, encode) {
17466       return html
17467         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17468         .replace(/</g, '&lt;')
17469         .replace(/>/g, '&gt;')
17470         .replace(/"/g, '&quot;')
17471         .replace(/'/g, '&#39;');
17472     }
17473     
17474     var unescape = function (html) {
17475         // explicitly match decimal, hex, and named HTML entities 
17476       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17477         n = n.toLowerCase();
17478         if (n === 'colon') { return ':'; }
17479         if (n.charAt(0) === '#') {
17480           return n.charAt(1) === 'x'
17481             ? String.fromCharCode(parseInt(n.substring(2), 16))
17482             : String.fromCharCode(+n.substring(1));
17483         }
17484         return '';
17485       });
17486     }
17487     
17488     var replace = function (regex, opt) {
17489       regex = regex.source;
17490       opt = opt || '';
17491       return function self(name, val) {
17492         if (!name) { return new RegExp(regex, opt); }
17493         val = val.source || val;
17494         val = val.replace(/(^|[^\[])\^/g, '$1');
17495         regex = regex.replace(name, val);
17496         return self;
17497       };
17498     }
17499
17500
17501          /**
17502          * eval:var:noop
17503     */
17504     var noop = function () {}
17505     noop.exec = noop;
17506     
17507          /**
17508          * eval:var:merge
17509     */
17510     var merge = function (obj) {
17511       var i = 1
17512         , target
17513         , key;
17514     
17515       for (; i < arguments.length; i++) {
17516         target = arguments[i];
17517         for (key in target) {
17518           if (Object.prototype.hasOwnProperty.call(target, key)) {
17519             obj[key] = target[key];
17520           }
17521         }
17522       }
17523     
17524       return obj;
17525     }
17526     
17527     
17528     /**
17529      * Block-Level Grammar
17530      */
17531     
17532     
17533     
17534     
17535     var block = {
17536       newline: /^\n+/,
17537       code: /^( {4}[^\n]+\n*)+/,
17538       fences: noop,
17539       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17540       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17541       nptable: noop,
17542       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17543       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17544       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17545       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17546       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17547       table: noop,
17548       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17549       text: /^[^\n]+/
17550     };
17551     
17552     block.bullet = /(?:[*+-]|\d+\.)/;
17553     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17554     block.item = replace(block.item, 'gm')
17555       (/bull/g, block.bullet)
17556       ();
17557     
17558     block.list = replace(block.list)
17559       (/bull/g, block.bullet)
17560       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17561       ('def', '\\n+(?=' + block.def.source + ')')
17562       ();
17563     
17564     block.blockquote = replace(block.blockquote)
17565       ('def', block.def)
17566       ();
17567     
17568     block._tag = '(?!(?:'
17569       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17570       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17571       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17572     
17573     block.html = replace(block.html)
17574       ('comment', /<!--[\s\S]*?-->/)
17575       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17576       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17577       (/tag/g, block._tag)
17578       ();
17579     
17580     block.paragraph = replace(block.paragraph)
17581       ('hr', block.hr)
17582       ('heading', block.heading)
17583       ('lheading', block.lheading)
17584       ('blockquote', block.blockquote)
17585       ('tag', '<' + block._tag)
17586       ('def', block.def)
17587       ();
17588     
17589     /**
17590      * Normal Block Grammar
17591      */
17592     
17593     block.normal = merge({}, block);
17594     
17595     /**
17596      * GFM Block Grammar
17597      */
17598     
17599     block.gfm = merge({}, block.normal, {
17600       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17601       paragraph: /^/,
17602       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17603     });
17604     
17605     block.gfm.paragraph = replace(block.paragraph)
17606       ('(?!', '(?!'
17607         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17608         + block.list.source.replace('\\1', '\\3') + '|')
17609       ();
17610     
17611     /**
17612      * GFM + Tables Block Grammar
17613      */
17614     
17615     block.tables = merge({}, block.gfm, {
17616       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17617       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17618     });
17619     
17620     /**
17621      * Block Lexer
17622      */
17623     
17624     var Lexer = function (options) {
17625       this.tokens = [];
17626       this.tokens.links = {};
17627       this.options = options || marked.defaults;
17628       this.rules = block.normal;
17629     
17630       if (this.options.gfm) {
17631         if (this.options.tables) {
17632           this.rules = block.tables;
17633         } else {
17634           this.rules = block.gfm;
17635         }
17636       }
17637     }
17638     
17639     /**
17640      * Expose Block Rules
17641      */
17642     
17643     Lexer.rules = block;
17644     
17645     /**
17646      * Static Lex Method
17647      */
17648     
17649     Lexer.lex = function(src, options) {
17650       var lexer = new Lexer(options);
17651       return lexer.lex(src);
17652     };
17653     
17654     /**
17655      * Preprocessing
17656      */
17657     
17658     Lexer.prototype.lex = function(src) {
17659       src = src
17660         .replace(/\r\n|\r/g, '\n')
17661         .replace(/\t/g, '    ')
17662         .replace(/\u00a0/g, ' ')
17663         .replace(/\u2424/g, '\n');
17664     
17665       return this.token(src, true);
17666     };
17667     
17668     /**
17669      * Lexing
17670      */
17671     
17672     Lexer.prototype.token = function(src, top, bq) {
17673       var src = src.replace(/^ +$/gm, '')
17674         , next
17675         , loose
17676         , cap
17677         , bull
17678         , b
17679         , item
17680         , space
17681         , i
17682         , l;
17683     
17684       while (src) {
17685         // newline
17686         if (cap = this.rules.newline.exec(src)) {
17687           src = src.substring(cap[0].length);
17688           if (cap[0].length > 1) {
17689             this.tokens.push({
17690               type: 'space'
17691             });
17692           }
17693         }
17694     
17695         // code
17696         if (cap = this.rules.code.exec(src)) {
17697           src = src.substring(cap[0].length);
17698           cap = cap[0].replace(/^ {4}/gm, '');
17699           this.tokens.push({
17700             type: 'code',
17701             text: !this.options.pedantic
17702               ? cap.replace(/\n+$/, '')
17703               : cap
17704           });
17705           continue;
17706         }
17707     
17708         // fences (gfm)
17709         if (cap = this.rules.fences.exec(src)) {
17710           src = src.substring(cap[0].length);
17711           this.tokens.push({
17712             type: 'code',
17713             lang: cap[2],
17714             text: cap[3] || ''
17715           });
17716           continue;
17717         }
17718     
17719         // heading
17720         if (cap = this.rules.heading.exec(src)) {
17721           src = src.substring(cap[0].length);
17722           this.tokens.push({
17723             type: 'heading',
17724             depth: cap[1].length,
17725             text: cap[2]
17726           });
17727           continue;
17728         }
17729     
17730         // table no leading pipe (gfm)
17731         if (top && (cap = this.rules.nptable.exec(src))) {
17732           src = src.substring(cap[0].length);
17733     
17734           item = {
17735             type: 'table',
17736             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17737             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17738             cells: cap[3].replace(/\n$/, '').split('\n')
17739           };
17740     
17741           for (i = 0; i < item.align.length; i++) {
17742             if (/^ *-+: *$/.test(item.align[i])) {
17743               item.align[i] = 'right';
17744             } else if (/^ *:-+: *$/.test(item.align[i])) {
17745               item.align[i] = 'center';
17746             } else if (/^ *:-+ *$/.test(item.align[i])) {
17747               item.align[i] = 'left';
17748             } else {
17749               item.align[i] = null;
17750             }
17751           }
17752     
17753           for (i = 0; i < item.cells.length; i++) {
17754             item.cells[i] = item.cells[i].split(/ *\| */);
17755           }
17756     
17757           this.tokens.push(item);
17758     
17759           continue;
17760         }
17761     
17762         // lheading
17763         if (cap = this.rules.lheading.exec(src)) {
17764           src = src.substring(cap[0].length);
17765           this.tokens.push({
17766             type: 'heading',
17767             depth: cap[2] === '=' ? 1 : 2,
17768             text: cap[1]
17769           });
17770           continue;
17771         }
17772     
17773         // hr
17774         if (cap = this.rules.hr.exec(src)) {
17775           src = src.substring(cap[0].length);
17776           this.tokens.push({
17777             type: 'hr'
17778           });
17779           continue;
17780         }
17781     
17782         // blockquote
17783         if (cap = this.rules.blockquote.exec(src)) {
17784           src = src.substring(cap[0].length);
17785     
17786           this.tokens.push({
17787             type: 'blockquote_start'
17788           });
17789     
17790           cap = cap[0].replace(/^ *> ?/gm, '');
17791     
17792           // Pass `top` to keep the current
17793           // "toplevel" state. This is exactly
17794           // how markdown.pl works.
17795           this.token(cap, top, true);
17796     
17797           this.tokens.push({
17798             type: 'blockquote_end'
17799           });
17800     
17801           continue;
17802         }
17803     
17804         // list
17805         if (cap = this.rules.list.exec(src)) {
17806           src = src.substring(cap[0].length);
17807           bull = cap[2];
17808     
17809           this.tokens.push({
17810             type: 'list_start',
17811             ordered: bull.length > 1
17812           });
17813     
17814           // Get each top-level item.
17815           cap = cap[0].match(this.rules.item);
17816     
17817           next = false;
17818           l = cap.length;
17819           i = 0;
17820     
17821           for (; i < l; i++) {
17822             item = cap[i];
17823     
17824             // Remove the list item's bullet
17825             // so it is seen as the next token.
17826             space = item.length;
17827             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17828     
17829             // Outdent whatever the
17830             // list item contains. Hacky.
17831             if (~item.indexOf('\n ')) {
17832               space -= item.length;
17833               item = !this.options.pedantic
17834                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17835                 : item.replace(/^ {1,4}/gm, '');
17836             }
17837     
17838             // Determine whether the next list item belongs here.
17839             // Backpedal if it does not belong in this list.
17840             if (this.options.smartLists && i !== l - 1) {
17841               b = block.bullet.exec(cap[i + 1])[0];
17842               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17843                 src = cap.slice(i + 1).join('\n') + src;
17844                 i = l - 1;
17845               }
17846             }
17847     
17848             // Determine whether item is loose or not.
17849             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17850             // for discount behavior.
17851             loose = next || /\n\n(?!\s*$)/.test(item);
17852             if (i !== l - 1) {
17853               next = item.charAt(item.length - 1) === '\n';
17854               if (!loose) { loose = next; }
17855             }
17856     
17857             this.tokens.push({
17858               type: loose
17859                 ? 'loose_item_start'
17860                 : 'list_item_start'
17861             });
17862     
17863             // Recurse.
17864             this.token(item, false, bq);
17865     
17866             this.tokens.push({
17867               type: 'list_item_end'
17868             });
17869           }
17870     
17871           this.tokens.push({
17872             type: 'list_end'
17873           });
17874     
17875           continue;
17876         }
17877     
17878         // html
17879         if (cap = this.rules.html.exec(src)) {
17880           src = src.substring(cap[0].length);
17881           this.tokens.push({
17882             type: this.options.sanitize
17883               ? 'paragraph'
17884               : 'html',
17885             pre: !this.options.sanitizer
17886               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17887             text: cap[0]
17888           });
17889           continue;
17890         }
17891     
17892         // def
17893         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17894           src = src.substring(cap[0].length);
17895           this.tokens.links[cap[1].toLowerCase()] = {
17896             href: cap[2],
17897             title: cap[3]
17898           };
17899           continue;
17900         }
17901     
17902         // table (gfm)
17903         if (top && (cap = this.rules.table.exec(src))) {
17904           src = src.substring(cap[0].length);
17905     
17906           item = {
17907             type: 'table',
17908             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17909             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17910             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17911           };
17912     
17913           for (i = 0; i < item.align.length; i++) {
17914             if (/^ *-+: *$/.test(item.align[i])) {
17915               item.align[i] = 'right';
17916             } else if (/^ *:-+: *$/.test(item.align[i])) {
17917               item.align[i] = 'center';
17918             } else if (/^ *:-+ *$/.test(item.align[i])) {
17919               item.align[i] = 'left';
17920             } else {
17921               item.align[i] = null;
17922             }
17923           }
17924     
17925           for (i = 0; i < item.cells.length; i++) {
17926             item.cells[i] = item.cells[i]
17927               .replace(/^ *\| *| *\| *$/g, '')
17928               .split(/ *\| */);
17929           }
17930     
17931           this.tokens.push(item);
17932     
17933           continue;
17934         }
17935     
17936         // top-level paragraph
17937         if (top && (cap = this.rules.paragraph.exec(src))) {
17938           src = src.substring(cap[0].length);
17939           this.tokens.push({
17940             type: 'paragraph',
17941             text: cap[1].charAt(cap[1].length - 1) === '\n'
17942               ? cap[1].slice(0, -1)
17943               : cap[1]
17944           });
17945           continue;
17946         }
17947     
17948         // text
17949         if (cap = this.rules.text.exec(src)) {
17950           // Top-level should never reach here.
17951           src = src.substring(cap[0].length);
17952           this.tokens.push({
17953             type: 'text',
17954             text: cap[0]
17955           });
17956           continue;
17957         }
17958     
17959         if (src) {
17960           throw new
17961             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17962         }
17963       }
17964     
17965       return this.tokens;
17966     };
17967     
17968     /**
17969      * Inline-Level Grammar
17970      */
17971     
17972     var inline = {
17973       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17974       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17975       url: noop,
17976       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17977       link: /^!?\[(inside)\]\(href\)/,
17978       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17979       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17980       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17981       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17982       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17983       br: /^ {2,}\n(?!\s*$)/,
17984       del: noop,
17985       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17986     };
17987     
17988     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17989     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17990     
17991     inline.link = replace(inline.link)
17992       ('inside', inline._inside)
17993       ('href', inline._href)
17994       ();
17995     
17996     inline.reflink = replace(inline.reflink)
17997       ('inside', inline._inside)
17998       ();
17999     
18000     /**
18001      * Normal Inline Grammar
18002      */
18003     
18004     inline.normal = merge({}, inline);
18005     
18006     /**
18007      * Pedantic Inline Grammar
18008      */
18009     
18010     inline.pedantic = merge({}, inline.normal, {
18011       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18012       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18013     });
18014     
18015     /**
18016      * GFM Inline Grammar
18017      */
18018     
18019     inline.gfm = merge({}, inline.normal, {
18020       escape: replace(inline.escape)('])', '~|])')(),
18021       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18022       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18023       text: replace(inline.text)
18024         (']|', '~]|')
18025         ('|', '|https?://|')
18026         ()
18027     });
18028     
18029     /**
18030      * GFM + Line Breaks Inline Grammar
18031      */
18032     
18033     inline.breaks = merge({}, inline.gfm, {
18034       br: replace(inline.br)('{2,}', '*')(),
18035       text: replace(inline.gfm.text)('{2,}', '*')()
18036     });
18037     
18038     /**
18039      * Inline Lexer & Compiler
18040      */
18041     
18042     var InlineLexer  = function (links, options) {
18043       this.options = options || marked.defaults;
18044       this.links = links;
18045       this.rules = inline.normal;
18046       this.renderer = this.options.renderer || new Renderer;
18047       this.renderer.options = this.options;
18048     
18049       if (!this.links) {
18050         throw new
18051           Error('Tokens array requires a `links` property.');
18052       }
18053     
18054       if (this.options.gfm) {
18055         if (this.options.breaks) {
18056           this.rules = inline.breaks;
18057         } else {
18058           this.rules = inline.gfm;
18059         }
18060       } else if (this.options.pedantic) {
18061         this.rules = inline.pedantic;
18062       }
18063     }
18064     
18065     /**
18066      * Expose Inline Rules
18067      */
18068     
18069     InlineLexer.rules = inline;
18070     
18071     /**
18072      * Static Lexing/Compiling Method
18073      */
18074     
18075     InlineLexer.output = function(src, links, options) {
18076       var inline = new InlineLexer(links, options);
18077       return inline.output(src);
18078     };
18079     
18080     /**
18081      * Lexing/Compiling
18082      */
18083     
18084     InlineLexer.prototype.output = function(src) {
18085       var out = ''
18086         , link
18087         , text
18088         , href
18089         , cap;
18090     
18091       while (src) {
18092         // escape
18093         if (cap = this.rules.escape.exec(src)) {
18094           src = src.substring(cap[0].length);
18095           out += cap[1];
18096           continue;
18097         }
18098     
18099         // autolink
18100         if (cap = this.rules.autolink.exec(src)) {
18101           src = src.substring(cap[0].length);
18102           if (cap[2] === '@') {
18103             text = cap[1].charAt(6) === ':'
18104               ? this.mangle(cap[1].substring(7))
18105               : this.mangle(cap[1]);
18106             href = this.mangle('mailto:') + text;
18107           } else {
18108             text = escape(cap[1]);
18109             href = text;
18110           }
18111           out += this.renderer.link(href, null, text);
18112           continue;
18113         }
18114     
18115         // url (gfm)
18116         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18117           src = src.substring(cap[0].length);
18118           text = escape(cap[1]);
18119           href = text;
18120           out += this.renderer.link(href, null, text);
18121           continue;
18122         }
18123     
18124         // tag
18125         if (cap = this.rules.tag.exec(src)) {
18126           if (!this.inLink && /^<a /i.test(cap[0])) {
18127             this.inLink = true;
18128           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18129             this.inLink = false;
18130           }
18131           src = src.substring(cap[0].length);
18132           out += this.options.sanitize
18133             ? this.options.sanitizer
18134               ? this.options.sanitizer(cap[0])
18135               : escape(cap[0])
18136             : cap[0];
18137           continue;
18138         }
18139     
18140         // link
18141         if (cap = this.rules.link.exec(src)) {
18142           src = src.substring(cap[0].length);
18143           this.inLink = true;
18144           out += this.outputLink(cap, {
18145             href: cap[2],
18146             title: cap[3]
18147           });
18148           this.inLink = false;
18149           continue;
18150         }
18151     
18152         // reflink, nolink
18153         if ((cap = this.rules.reflink.exec(src))
18154             || (cap = this.rules.nolink.exec(src))) {
18155           src = src.substring(cap[0].length);
18156           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18157           link = this.links[link.toLowerCase()];
18158           if (!link || !link.href) {
18159             out += cap[0].charAt(0);
18160             src = cap[0].substring(1) + src;
18161             continue;
18162           }
18163           this.inLink = true;
18164           out += this.outputLink(cap, link);
18165           this.inLink = false;
18166           continue;
18167         }
18168     
18169         // strong
18170         if (cap = this.rules.strong.exec(src)) {
18171           src = src.substring(cap[0].length);
18172           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18173           continue;
18174         }
18175     
18176         // em
18177         if (cap = this.rules.em.exec(src)) {
18178           src = src.substring(cap[0].length);
18179           out += this.renderer.em(this.output(cap[2] || cap[1]));
18180           continue;
18181         }
18182     
18183         // code
18184         if (cap = this.rules.code.exec(src)) {
18185           src = src.substring(cap[0].length);
18186           out += this.renderer.codespan(escape(cap[2], true));
18187           continue;
18188         }
18189     
18190         // br
18191         if (cap = this.rules.br.exec(src)) {
18192           src = src.substring(cap[0].length);
18193           out += this.renderer.br();
18194           continue;
18195         }
18196     
18197         // del (gfm)
18198         if (cap = this.rules.del.exec(src)) {
18199           src = src.substring(cap[0].length);
18200           out += this.renderer.del(this.output(cap[1]));
18201           continue;
18202         }
18203     
18204         // text
18205         if (cap = this.rules.text.exec(src)) {
18206           src = src.substring(cap[0].length);
18207           out += this.renderer.text(escape(this.smartypants(cap[0])));
18208           continue;
18209         }
18210     
18211         if (src) {
18212           throw new
18213             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18214         }
18215       }
18216     
18217       return out;
18218     };
18219     
18220     /**
18221      * Compile Link
18222      */
18223     
18224     InlineLexer.prototype.outputLink = function(cap, link) {
18225       var href = escape(link.href)
18226         , title = link.title ? escape(link.title) : null;
18227     
18228       return cap[0].charAt(0) !== '!'
18229         ? this.renderer.link(href, title, this.output(cap[1]))
18230         : this.renderer.image(href, title, escape(cap[1]));
18231     };
18232     
18233     /**
18234      * Smartypants Transformations
18235      */
18236     
18237     InlineLexer.prototype.smartypants = function(text) {
18238       if (!this.options.smartypants)  { return text; }
18239       return text
18240         // em-dashes
18241         .replace(/---/g, '\u2014')
18242         // en-dashes
18243         .replace(/--/g, '\u2013')
18244         // opening singles
18245         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18246         // closing singles & apostrophes
18247         .replace(/'/g, '\u2019')
18248         // opening doubles
18249         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18250         // closing doubles
18251         .replace(/"/g, '\u201d')
18252         // ellipses
18253         .replace(/\.{3}/g, '\u2026');
18254     };
18255     
18256     /**
18257      * Mangle Links
18258      */
18259     
18260     InlineLexer.prototype.mangle = function(text) {
18261       if (!this.options.mangle) { return text; }
18262       var out = ''
18263         , l = text.length
18264         , i = 0
18265         , ch;
18266     
18267       for (; i < l; i++) {
18268         ch = text.charCodeAt(i);
18269         if (Math.random() > 0.5) {
18270           ch = 'x' + ch.toString(16);
18271         }
18272         out += '&#' + ch + ';';
18273       }
18274     
18275       return out;
18276     };
18277     
18278     /**
18279      * Renderer
18280      */
18281     
18282      /**
18283          * eval:var:Renderer
18284     */
18285     
18286     var Renderer   = function (options) {
18287       this.options = options || {};
18288     }
18289     
18290     Renderer.prototype.code = function(code, lang, escaped) {
18291       if (this.options.highlight) {
18292         var out = this.options.highlight(code, lang);
18293         if (out != null && out !== code) {
18294           escaped = true;
18295           code = out;
18296         }
18297       } else {
18298             // hack!!! - it's already escapeD?
18299             escaped = true;
18300       }
18301     
18302       if (!lang) {
18303         return '<pre><code>'
18304           + (escaped ? code : escape(code, true))
18305           + '\n</code></pre>';
18306       }
18307     
18308       return '<pre><code class="'
18309         + this.options.langPrefix
18310         + escape(lang, true)
18311         + '">'
18312         + (escaped ? code : escape(code, true))
18313         + '\n</code></pre>\n';
18314     };
18315     
18316     Renderer.prototype.blockquote = function(quote) {
18317       return '<blockquote>\n' + quote + '</blockquote>\n';
18318     };
18319     
18320     Renderer.prototype.html = function(html) {
18321       return html;
18322     };
18323     
18324     Renderer.prototype.heading = function(text, level, raw) {
18325       return '<h'
18326         + level
18327         + ' id="'
18328         + this.options.headerPrefix
18329         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18330         + '">'
18331         + text
18332         + '</h'
18333         + level
18334         + '>\n';
18335     };
18336     
18337     Renderer.prototype.hr = function() {
18338       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18339     };
18340     
18341     Renderer.prototype.list = function(body, ordered) {
18342       var type = ordered ? 'ol' : 'ul';
18343       return '<' + type + '>\n' + body + '</' + type + '>\n';
18344     };
18345     
18346     Renderer.prototype.listitem = function(text) {
18347       return '<li>' + text + '</li>\n';
18348     };
18349     
18350     Renderer.prototype.paragraph = function(text) {
18351       return '<p>' + text + '</p>\n';
18352     };
18353     
18354     Renderer.prototype.table = function(header, body) {
18355       return '<table class="table table-striped">\n'
18356         + '<thead>\n'
18357         + header
18358         + '</thead>\n'
18359         + '<tbody>\n'
18360         + body
18361         + '</tbody>\n'
18362         + '</table>\n';
18363     };
18364     
18365     Renderer.prototype.tablerow = function(content) {
18366       return '<tr>\n' + content + '</tr>\n';
18367     };
18368     
18369     Renderer.prototype.tablecell = function(content, flags) {
18370       var type = flags.header ? 'th' : 'td';
18371       var tag = flags.align
18372         ? '<' + type + ' style="text-align:' + flags.align + '">'
18373         : '<' + type + '>';
18374       return tag + content + '</' + type + '>\n';
18375     };
18376     
18377     // span level renderer
18378     Renderer.prototype.strong = function(text) {
18379       return '<strong>' + text + '</strong>';
18380     };
18381     
18382     Renderer.prototype.em = function(text) {
18383       return '<em>' + text + '</em>';
18384     };
18385     
18386     Renderer.prototype.codespan = function(text) {
18387       return '<code>' + text + '</code>';
18388     };
18389     
18390     Renderer.prototype.br = function() {
18391       return this.options.xhtml ? '<br/>' : '<br>';
18392     };
18393     
18394     Renderer.prototype.del = function(text) {
18395       return '<del>' + text + '</del>';
18396     };
18397     
18398     Renderer.prototype.link = function(href, title, text) {
18399       if (this.options.sanitize) {
18400         try {
18401           var prot = decodeURIComponent(unescape(href))
18402             .replace(/[^\w:]/g, '')
18403             .toLowerCase();
18404         } catch (e) {
18405           return '';
18406         }
18407         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18408           return '';
18409         }
18410       }
18411       var out = '<a href="' + href + '"';
18412       if (title) {
18413         out += ' title="' + title + '"';
18414       }
18415       out += '>' + text + '</a>';
18416       return out;
18417     };
18418     
18419     Renderer.prototype.image = function(href, title, text) {
18420       var out = '<img src="' + href + '" alt="' + text + '"';
18421       if (title) {
18422         out += ' title="' + title + '"';
18423       }
18424       out += this.options.xhtml ? '/>' : '>';
18425       return out;
18426     };
18427     
18428     Renderer.prototype.text = function(text) {
18429       return text;
18430     };
18431     
18432     /**
18433      * Parsing & Compiling
18434      */
18435          /**
18436          * eval:var:Parser
18437     */
18438     
18439     var Parser= function (options) {
18440       this.tokens = [];
18441       this.token = null;
18442       this.options = options || marked.defaults;
18443       this.options.renderer = this.options.renderer || new Renderer;
18444       this.renderer = this.options.renderer;
18445       this.renderer.options = this.options;
18446     }
18447     
18448     /**
18449      * Static Parse Method
18450      */
18451     
18452     Parser.parse = function(src, options, renderer) {
18453       var parser = new Parser(options, renderer);
18454       return parser.parse(src);
18455     };
18456     
18457     /**
18458      * Parse Loop
18459      */
18460     
18461     Parser.prototype.parse = function(src) {
18462       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18463       this.tokens = src.reverse();
18464     
18465       var out = '';
18466       while (this.next()) {
18467         out += this.tok();
18468       }
18469     
18470       return out;
18471     };
18472     
18473     /**
18474      * Next Token
18475      */
18476     
18477     Parser.prototype.next = function() {
18478       return this.token = this.tokens.pop();
18479     };
18480     
18481     /**
18482      * Preview Next Token
18483      */
18484     
18485     Parser.prototype.peek = function() {
18486       return this.tokens[this.tokens.length - 1] || 0;
18487     };
18488     
18489     /**
18490      * Parse Text Tokens
18491      */
18492     
18493     Parser.prototype.parseText = function() {
18494       var body = this.token.text;
18495     
18496       while (this.peek().type === 'text') {
18497         body += '\n' + this.next().text;
18498       }
18499     
18500       return this.inline.output(body);
18501     };
18502     
18503     /**
18504      * Parse Current Token
18505      */
18506     
18507     Parser.prototype.tok = function() {
18508       switch (this.token.type) {
18509         case 'space': {
18510           return '';
18511         }
18512         case 'hr': {
18513           return this.renderer.hr();
18514         }
18515         case 'heading': {
18516           return this.renderer.heading(
18517             this.inline.output(this.token.text),
18518             this.token.depth,
18519             this.token.text);
18520         }
18521         case 'code': {
18522           return this.renderer.code(this.token.text,
18523             this.token.lang,
18524             this.token.escaped);
18525         }
18526         case 'table': {
18527           var header = ''
18528             , body = ''
18529             , i
18530             , row
18531             , cell
18532             , flags
18533             , j;
18534     
18535           // header
18536           cell = '';
18537           for (i = 0; i < this.token.header.length; i++) {
18538             flags = { header: true, align: this.token.align[i] };
18539             cell += this.renderer.tablecell(
18540               this.inline.output(this.token.header[i]),
18541               { header: true, align: this.token.align[i] }
18542             );
18543           }
18544           header += this.renderer.tablerow(cell);
18545     
18546           for (i = 0; i < this.token.cells.length; i++) {
18547             row = this.token.cells[i];
18548     
18549             cell = '';
18550             for (j = 0; j < row.length; j++) {
18551               cell += this.renderer.tablecell(
18552                 this.inline.output(row[j]),
18553                 { header: false, align: this.token.align[j] }
18554               );
18555             }
18556     
18557             body += this.renderer.tablerow(cell);
18558           }
18559           return this.renderer.table(header, body);
18560         }
18561         case 'blockquote_start': {
18562           var body = '';
18563     
18564           while (this.next().type !== 'blockquote_end') {
18565             body += this.tok();
18566           }
18567     
18568           return this.renderer.blockquote(body);
18569         }
18570         case 'list_start': {
18571           var body = ''
18572             , ordered = this.token.ordered;
18573     
18574           while (this.next().type !== 'list_end') {
18575             body += this.tok();
18576           }
18577     
18578           return this.renderer.list(body, ordered);
18579         }
18580         case 'list_item_start': {
18581           var body = '';
18582     
18583           while (this.next().type !== 'list_item_end') {
18584             body += this.token.type === 'text'
18585               ? this.parseText()
18586               : this.tok();
18587           }
18588     
18589           return this.renderer.listitem(body);
18590         }
18591         case 'loose_item_start': {
18592           var body = '';
18593     
18594           while (this.next().type !== 'list_item_end') {
18595             body += this.tok();
18596           }
18597     
18598           return this.renderer.listitem(body);
18599         }
18600         case 'html': {
18601           var html = !this.token.pre && !this.options.pedantic
18602             ? this.inline.output(this.token.text)
18603             : this.token.text;
18604           return this.renderer.html(html);
18605         }
18606         case 'paragraph': {
18607           return this.renderer.paragraph(this.inline.output(this.token.text));
18608         }
18609         case 'text': {
18610           return this.renderer.paragraph(this.parseText());
18611         }
18612       }
18613     };
18614   
18615     
18616     /**
18617      * Marked
18618      */
18619          /**
18620          * eval:var:marked
18621     */
18622     var marked = function (src, opt, callback) {
18623       if (callback || typeof opt === 'function') {
18624         if (!callback) {
18625           callback = opt;
18626           opt = null;
18627         }
18628     
18629         opt = merge({}, marked.defaults, opt || {});
18630     
18631         var highlight = opt.highlight
18632           , tokens
18633           , pending
18634           , i = 0;
18635     
18636         try {
18637           tokens = Lexer.lex(src, opt)
18638         } catch (e) {
18639           return callback(e);
18640         }
18641     
18642         pending = tokens.length;
18643          /**
18644          * eval:var:done
18645     */
18646         var done = function(err) {
18647           if (err) {
18648             opt.highlight = highlight;
18649             return callback(err);
18650           }
18651     
18652           var out;
18653     
18654           try {
18655             out = Parser.parse(tokens, opt);
18656           } catch (e) {
18657             err = e;
18658           }
18659     
18660           opt.highlight = highlight;
18661     
18662           return err
18663             ? callback(err)
18664             : callback(null, out);
18665         };
18666     
18667         if (!highlight || highlight.length < 3) {
18668           return done();
18669         }
18670     
18671         delete opt.highlight;
18672     
18673         if (!pending) { return done(); }
18674     
18675         for (; i < tokens.length; i++) {
18676           (function(token) {
18677             if (token.type !== 'code') {
18678               return --pending || done();
18679             }
18680             return highlight(token.text, token.lang, function(err, code) {
18681               if (err) { return done(err); }
18682               if (code == null || code === token.text) {
18683                 return --pending || done();
18684               }
18685               token.text = code;
18686               token.escaped = true;
18687               --pending || done();
18688             });
18689           })(tokens[i]);
18690         }
18691     
18692         return;
18693       }
18694       try {
18695         if (opt) { opt = merge({}, marked.defaults, opt); }
18696         return Parser.parse(Lexer.lex(src, opt), opt);
18697       } catch (e) {
18698         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18699         if ((opt || marked.defaults).silent) {
18700           return '<p>An error occured:</p><pre>'
18701             + escape(e.message + '', true)
18702             + '</pre>';
18703         }
18704         throw e;
18705       }
18706     }
18707     
18708     /**
18709      * Options
18710      */
18711     
18712     marked.options =
18713     marked.setOptions = function(opt) {
18714       merge(marked.defaults, opt);
18715       return marked;
18716     };
18717     
18718     marked.defaults = {
18719       gfm: true,
18720       tables: true,
18721       breaks: false,
18722       pedantic: false,
18723       sanitize: false,
18724       sanitizer: null,
18725       mangle: true,
18726       smartLists: false,
18727       silent: false,
18728       highlight: null,
18729       langPrefix: 'lang-',
18730       smartypants: false,
18731       headerPrefix: '',
18732       renderer: new Renderer,
18733       xhtml: false
18734     };
18735     
18736     /**
18737      * Expose
18738      */
18739     
18740     marked.Parser = Parser;
18741     marked.parser = Parser.parse;
18742     
18743     marked.Renderer = Renderer;
18744     
18745     marked.Lexer = Lexer;
18746     marked.lexer = Lexer.lex;
18747     
18748     marked.InlineLexer = InlineLexer;
18749     marked.inlineLexer = InlineLexer.output;
18750     
18751     marked.parse = marked;
18752     
18753     Roo.Markdown.marked = marked;
18754
18755 })();/*
18756  * Based on:
18757  * Ext JS Library 1.1.1
18758  * Copyright(c) 2006-2007, Ext JS, LLC.
18759  *
18760  * Originally Released Under LGPL - original licence link has changed is not relivant.
18761  *
18762  * Fork - LGPL
18763  * <script type="text/javascript">
18764  */
18765
18766
18767
18768 /*
18769  * These classes are derivatives of the similarly named classes in the YUI Library.
18770  * The original license:
18771  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18772  * Code licensed under the BSD License:
18773  * http://developer.yahoo.net/yui/license.txt
18774  */
18775
18776 (function() {
18777
18778 var Event=Roo.EventManager;
18779 var Dom=Roo.lib.Dom;
18780
18781 /**
18782  * @class Roo.dd.DragDrop
18783  * @extends Roo.util.Observable
18784  * Defines the interface and base operation of items that that can be
18785  * dragged or can be drop targets.  It was designed to be extended, overriding
18786  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18787  * Up to three html elements can be associated with a DragDrop instance:
18788  * <ul>
18789  * <li>linked element: the element that is passed into the constructor.
18790  * This is the element which defines the boundaries for interaction with
18791  * other DragDrop objects.</li>
18792  * <li>handle element(s): The drag operation only occurs if the element that
18793  * was clicked matches a handle element.  By default this is the linked
18794  * element, but there are times that you will want only a portion of the
18795  * linked element to initiate the drag operation, and the setHandleElId()
18796  * method provides a way to define this.</li>
18797  * <li>drag element: this represents the element that would be moved along
18798  * with the cursor during a drag operation.  By default, this is the linked
18799  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18800  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18801  * </li>
18802  * </ul>
18803  * This class should not be instantiated until the onload event to ensure that
18804  * the associated elements are available.
18805  * The following would define a DragDrop obj that would interact with any
18806  * other DragDrop obj in the "group1" group:
18807  * <pre>
18808  *  dd = new Roo.dd.DragDrop("div1", "group1");
18809  * </pre>
18810  * Since none of the event handlers have been implemented, nothing would
18811  * actually happen if you were to run the code above.  Normally you would
18812  * override this class or one of the default implementations, but you can
18813  * also override the methods you want on an instance of the class...
18814  * <pre>
18815  *  dd.onDragDrop = function(e, id) {
18816  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18817  *  }
18818  * </pre>
18819  * @constructor
18820  * @param {String} id of the element that is linked to this instance
18821  * @param {String} sGroup the group of related DragDrop objects
18822  * @param {object} config an object containing configurable attributes
18823  *                Valid properties for DragDrop:
18824  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18825  */
18826 Roo.dd.DragDrop = function(id, sGroup, config) {
18827     if (id) {
18828         this.init(id, sGroup, config);
18829     }
18830     
18831 };
18832
18833 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18834
18835     /**
18836      * The id of the element associated with this object.  This is what we
18837      * refer to as the "linked element" because the size and position of
18838      * this element is used to determine when the drag and drop objects have
18839      * interacted.
18840      * @property id
18841      * @type String
18842      */
18843     id: null,
18844
18845     /**
18846      * Configuration attributes passed into the constructor
18847      * @property config
18848      * @type object
18849      */
18850     config: null,
18851
18852     /**
18853      * The id of the element that will be dragged.  By default this is same
18854      * as the linked element , but could be changed to another element. Ex:
18855      * Roo.dd.DDProxy
18856      * @property dragElId
18857      * @type String
18858      * @private
18859      */
18860     dragElId: null,
18861
18862     /**
18863      * the id of the element that initiates the drag operation.  By default
18864      * this is the linked element, but could be changed to be a child of this
18865      * element.  This lets us do things like only starting the drag when the
18866      * header element within the linked html element is clicked.
18867      * @property handleElId
18868      * @type String
18869      * @private
18870      */
18871     handleElId: null,
18872
18873     /**
18874      * An associative array of HTML tags that will be ignored if clicked.
18875      * @property invalidHandleTypes
18876      * @type {string: string}
18877      */
18878     invalidHandleTypes: null,
18879
18880     /**
18881      * An associative array of ids for elements that will be ignored if clicked
18882      * @property invalidHandleIds
18883      * @type {string: string}
18884      */
18885     invalidHandleIds: null,
18886
18887     /**
18888      * An indexted array of css class names for elements that will be ignored
18889      * if clicked.
18890      * @property invalidHandleClasses
18891      * @type string[]
18892      */
18893     invalidHandleClasses: null,
18894
18895     /**
18896      * The linked element's absolute X position at the time the drag was
18897      * started
18898      * @property startPageX
18899      * @type int
18900      * @private
18901      */
18902     startPageX: 0,
18903
18904     /**
18905      * The linked element's absolute X position at the time the drag was
18906      * started
18907      * @property startPageY
18908      * @type int
18909      * @private
18910      */
18911     startPageY: 0,
18912
18913     /**
18914      * The group defines a logical collection of DragDrop objects that are
18915      * related.  Instances only get events when interacting with other
18916      * DragDrop object in the same group.  This lets us define multiple
18917      * groups using a single DragDrop subclass if we want.
18918      * @property groups
18919      * @type {string: string}
18920      */
18921     groups: null,
18922
18923     /**
18924      * Individual drag/drop instances can be locked.  This will prevent
18925      * onmousedown start drag.
18926      * @property locked
18927      * @type boolean
18928      * @private
18929      */
18930     locked: false,
18931
18932     /**
18933      * Lock this instance
18934      * @method lock
18935      */
18936     lock: function() { this.locked = true; },
18937
18938     /**
18939      * Unlock this instace
18940      * @method unlock
18941      */
18942     unlock: function() { this.locked = false; },
18943
18944     /**
18945      * By default, all insances can be a drop target.  This can be disabled by
18946      * setting isTarget to false.
18947      * @method isTarget
18948      * @type boolean
18949      */
18950     isTarget: true,
18951
18952     /**
18953      * The padding configured for this drag and drop object for calculating
18954      * the drop zone intersection with this object.
18955      * @method padding
18956      * @type int[]
18957      */
18958     padding: null,
18959
18960     /**
18961      * Cached reference to the linked element
18962      * @property _domRef
18963      * @private
18964      */
18965     _domRef: null,
18966
18967     /**
18968      * Internal typeof flag
18969      * @property __ygDragDrop
18970      * @private
18971      */
18972     __ygDragDrop: true,
18973
18974     /**
18975      * Set to true when horizontal contraints are applied
18976      * @property constrainX
18977      * @type boolean
18978      * @private
18979      */
18980     constrainX: false,
18981
18982     /**
18983      * Set to true when vertical contraints are applied
18984      * @property constrainY
18985      * @type boolean
18986      * @private
18987      */
18988     constrainY: false,
18989
18990     /**
18991      * The left constraint
18992      * @property minX
18993      * @type int
18994      * @private
18995      */
18996     minX: 0,
18997
18998     /**
18999      * The right constraint
19000      * @property maxX
19001      * @type int
19002      * @private
19003      */
19004     maxX: 0,
19005
19006     /**
19007      * The up constraint
19008      * @property minY
19009      * @type int
19010      * @type int
19011      * @private
19012      */
19013     minY: 0,
19014
19015     /**
19016      * The down constraint
19017      * @property maxY
19018      * @type int
19019      * @private
19020      */
19021     maxY: 0,
19022
19023     /**
19024      * Maintain offsets when we resetconstraints.  Set to true when you want
19025      * the position of the element relative to its parent to stay the same
19026      * when the page changes
19027      *
19028      * @property maintainOffset
19029      * @type boolean
19030      */
19031     maintainOffset: false,
19032
19033     /**
19034      * Array of pixel locations the element will snap to if we specified a
19035      * horizontal graduation/interval.  This array is generated automatically
19036      * when you define a tick interval.
19037      * @property xTicks
19038      * @type int[]
19039      */
19040     xTicks: null,
19041
19042     /**
19043      * Array of pixel locations the element will snap to if we specified a
19044      * vertical graduation/interval.  This array is generated automatically
19045      * when you define a tick interval.
19046      * @property yTicks
19047      * @type int[]
19048      */
19049     yTicks: null,
19050
19051     /**
19052      * By default the drag and drop instance will only respond to the primary
19053      * button click (left button for a right-handed mouse).  Set to true to
19054      * allow drag and drop to start with any mouse click that is propogated
19055      * by the browser
19056      * @property primaryButtonOnly
19057      * @type boolean
19058      */
19059     primaryButtonOnly: true,
19060
19061     /**
19062      * The availabe property is false until the linked dom element is accessible.
19063      * @property available
19064      * @type boolean
19065      */
19066     available: false,
19067
19068     /**
19069      * By default, drags can only be initiated if the mousedown occurs in the
19070      * region the linked element is.  This is done in part to work around a
19071      * bug in some browsers that mis-report the mousedown if the previous
19072      * mouseup happened outside of the window.  This property is set to true
19073      * if outer handles are defined.
19074      *
19075      * @property hasOuterHandles
19076      * @type boolean
19077      * @default false
19078      */
19079     hasOuterHandles: false,
19080
19081     /**
19082      * Code that executes immediately before the startDrag event
19083      * @method b4StartDrag
19084      * @private
19085      */
19086     b4StartDrag: function(x, y) { },
19087
19088     /**
19089      * Abstract method called after a drag/drop object is clicked
19090      * and the drag or mousedown time thresholds have beeen met.
19091      * @method startDrag
19092      * @param {int} X click location
19093      * @param {int} Y click location
19094      */
19095     startDrag: function(x, y) { /* override this */ },
19096
19097     /**
19098      * Code that executes immediately before the onDrag event
19099      * @method b4Drag
19100      * @private
19101      */
19102     b4Drag: function(e) { },
19103
19104     /**
19105      * Abstract method called during the onMouseMove event while dragging an
19106      * object.
19107      * @method onDrag
19108      * @param {Event} e the mousemove event
19109      */
19110     onDrag: function(e) { /* override this */ },
19111
19112     /**
19113      * Abstract method called when this element fist begins hovering over
19114      * another DragDrop obj
19115      * @method onDragEnter
19116      * @param {Event} e the mousemove event
19117      * @param {String|DragDrop[]} id In POINT mode, the element
19118      * id this is hovering over.  In INTERSECT mode, an array of one or more
19119      * dragdrop items being hovered over.
19120      */
19121     onDragEnter: function(e, id) { /* override this */ },
19122
19123     /**
19124      * Code that executes immediately before the onDragOver event
19125      * @method b4DragOver
19126      * @private
19127      */
19128     b4DragOver: function(e) { },
19129
19130     /**
19131      * Abstract method called when this element is hovering over another
19132      * DragDrop obj
19133      * @method onDragOver
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 dd items
19137      * being hovered over.
19138      */
19139     onDragOver: function(e, id) { /* override this */ },
19140
19141     /**
19142      * Code that executes immediately before the onDragOut event
19143      * @method b4DragOut
19144      * @private
19145      */
19146     b4DragOut: function(e) { },
19147
19148     /**
19149      * Abstract method called when we are no longer hovering over an element
19150      * @method onDragOut
19151      * @param {Event} e the mousemove event
19152      * @param {String|DragDrop[]} id In POINT mode, the element
19153      * id this was hovering over.  In INTERSECT mode, an array of dd items
19154      * that the mouse is no longer over.
19155      */
19156     onDragOut: function(e, id) { /* override this */ },
19157
19158     /**
19159      * Code that executes immediately before the onDragDrop event
19160      * @method b4DragDrop
19161      * @private
19162      */
19163     b4DragDrop: function(e) { },
19164
19165     /**
19166      * Abstract method called when this item is dropped on another DragDrop
19167      * obj
19168      * @method onDragDrop
19169      * @param {Event} e the mouseup event
19170      * @param {String|DragDrop[]} id In POINT mode, the element
19171      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19172      * was dropped on.
19173      */
19174     onDragDrop: function(e, id) { /* override this */ },
19175
19176     /**
19177      * Abstract method called when this item is dropped on an area with no
19178      * drop target
19179      * @method onInvalidDrop
19180      * @param {Event} e the mouseup event
19181      */
19182     onInvalidDrop: function(e) { /* override this */ },
19183
19184     /**
19185      * Code that executes immediately before the endDrag event
19186      * @method b4EndDrag
19187      * @private
19188      */
19189     b4EndDrag: function(e) { },
19190
19191     /**
19192      * Fired when we are done dragging the object
19193      * @method endDrag
19194      * @param {Event} e the mouseup event
19195      */
19196     endDrag: function(e) { /* override this */ },
19197
19198     /**
19199      * Code executed immediately before the onMouseDown event
19200      * @method b4MouseDown
19201      * @param {Event} e the mousedown event
19202      * @private
19203      */
19204     b4MouseDown: function(e) {  },
19205
19206     /**
19207      * Event handler that fires when a drag/drop obj gets a mousedown
19208      * @method onMouseDown
19209      * @param {Event} e the mousedown event
19210      */
19211     onMouseDown: function(e) { /* override this */ },
19212
19213     /**
19214      * Event handler that fires when a drag/drop obj gets a mouseup
19215      * @method onMouseUp
19216      * @param {Event} e the mouseup event
19217      */
19218     onMouseUp: function(e) { /* override this */ },
19219
19220     /**
19221      * Override the onAvailable method to do what is needed after the initial
19222      * position was determined.
19223      * @method onAvailable
19224      */
19225     onAvailable: function () {
19226     },
19227
19228     /*
19229      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19230      * @type Object
19231      */
19232     defaultPadding : {left:0, right:0, top:0, bottom:0},
19233
19234     /*
19235      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19236  *
19237  * Usage:
19238  <pre><code>
19239  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19240                 { dragElId: "existingProxyDiv" });
19241  dd.startDrag = function(){
19242      this.constrainTo("parent-id");
19243  };
19244  </code></pre>
19245  * Or you can initalize it using the {@link Roo.Element} object:
19246  <pre><code>
19247  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19248      startDrag : function(){
19249          this.constrainTo("parent-id");
19250      }
19251  });
19252  </code></pre>
19253      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19254      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19255      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19256      * an object containing the sides to pad. For example: {right:10, bottom:10}
19257      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19258      */
19259     constrainTo : function(constrainTo, pad, inContent){
19260         if(typeof pad == "number"){
19261             pad = {left: pad, right:pad, top:pad, bottom:pad};
19262         }
19263         pad = pad || this.defaultPadding;
19264         var b = Roo.get(this.getEl()).getBox();
19265         var ce = Roo.get(constrainTo);
19266         var s = ce.getScroll();
19267         var c, cd = ce.dom;
19268         if(cd == document.body){
19269             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19270         }else{
19271             xy = ce.getXY();
19272             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19273         }
19274
19275
19276         var topSpace = b.y - c.y;
19277         var leftSpace = b.x - c.x;
19278
19279         this.resetConstraints();
19280         this.setXConstraint(leftSpace - (pad.left||0), // left
19281                 c.width - leftSpace - b.width - (pad.right||0) //right
19282         );
19283         this.setYConstraint(topSpace - (pad.top||0), //top
19284                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19285         );
19286     },
19287
19288     /**
19289      * Returns a reference to the linked element
19290      * @method getEl
19291      * @return {HTMLElement} the html element
19292      */
19293     getEl: function() {
19294         if (!this._domRef) {
19295             this._domRef = Roo.getDom(this.id);
19296         }
19297
19298         return this._domRef;
19299     },
19300
19301     /**
19302      * Returns a reference to the actual element to drag.  By default this is
19303      * the same as the html element, but it can be assigned to another
19304      * element. An example of this can be found in Roo.dd.DDProxy
19305      * @method getDragEl
19306      * @return {HTMLElement} the html element
19307      */
19308     getDragEl: function() {
19309         return Roo.getDom(this.dragElId);
19310     },
19311
19312     /**
19313      * Sets up the DragDrop object.  Must be called in the constructor of any
19314      * Roo.dd.DragDrop subclass
19315      * @method init
19316      * @param id the id of the linked element
19317      * @param {String} sGroup the group of related items
19318      * @param {object} config configuration attributes
19319      */
19320     init: function(id, sGroup, config) {
19321         this.initTarget(id, sGroup, config);
19322         if (!Roo.isTouch) {
19323             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19324         }
19325         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19326         // Event.on(this.id, "selectstart", Event.preventDefault);
19327     },
19328
19329     /**
19330      * Initializes Targeting functionality only... the object does not
19331      * get a mousedown handler.
19332      * @method initTarget
19333      * @param id the id of the linked element
19334      * @param {String} sGroup the group of related items
19335      * @param {object} config configuration attributes
19336      */
19337     initTarget: function(id, sGroup, config) {
19338
19339         // configuration attributes
19340         this.config = config || {};
19341
19342         // create a local reference to the drag and drop manager
19343         this.DDM = Roo.dd.DDM;
19344         // initialize the groups array
19345         this.groups = {};
19346
19347         // assume that we have an element reference instead of an id if the
19348         // parameter is not a string
19349         if (typeof id !== "string") {
19350             id = Roo.id(id);
19351         }
19352
19353         // set the id
19354         this.id = id;
19355
19356         // add to an interaction group
19357         this.addToGroup((sGroup) ? sGroup : "default");
19358
19359         // We don't want to register this as the handle with the manager
19360         // so we just set the id rather than calling the setter.
19361         this.handleElId = id;
19362
19363         // the linked element is the element that gets dragged by default
19364         this.setDragElId(id);
19365
19366         // by default, clicked anchors will not start drag operations.
19367         this.invalidHandleTypes = { A: "A" };
19368         this.invalidHandleIds = {};
19369         this.invalidHandleClasses = [];
19370
19371         this.applyConfig();
19372
19373         this.handleOnAvailable();
19374     },
19375
19376     /**
19377      * Applies the configuration parameters that were passed into the constructor.
19378      * This is supposed to happen at each level through the inheritance chain.  So
19379      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19380      * DragDrop in order to get all of the parameters that are available in
19381      * each object.
19382      * @method applyConfig
19383      */
19384     applyConfig: function() {
19385
19386         // configurable properties:
19387         //    padding, isTarget, maintainOffset, primaryButtonOnly
19388         this.padding           = this.config.padding || [0, 0, 0, 0];
19389         this.isTarget          = (this.config.isTarget !== false);
19390         this.maintainOffset    = (this.config.maintainOffset);
19391         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19392
19393     },
19394
19395     /**
19396      * Executed when the linked element is available
19397      * @method handleOnAvailable
19398      * @private
19399      */
19400     handleOnAvailable: function() {
19401         this.available = true;
19402         this.resetConstraints();
19403         this.onAvailable();
19404     },
19405
19406      /**
19407      * Configures the padding for the target zone in px.  Effectively expands
19408      * (or reduces) the virtual object size for targeting calculations.
19409      * Supports css-style shorthand; if only one parameter is passed, all sides
19410      * will have that padding, and if only two are passed, the top and bottom
19411      * will have the first param, the left and right the second.
19412      * @method setPadding
19413      * @param {int} iTop    Top pad
19414      * @param {int} iRight  Right pad
19415      * @param {int} iBot    Bot pad
19416      * @param {int} iLeft   Left pad
19417      */
19418     setPadding: function(iTop, iRight, iBot, iLeft) {
19419         // this.padding = [iLeft, iRight, iTop, iBot];
19420         if (!iRight && 0 !== iRight) {
19421             this.padding = [iTop, iTop, iTop, iTop];
19422         } else if (!iBot && 0 !== iBot) {
19423             this.padding = [iTop, iRight, iTop, iRight];
19424         } else {
19425             this.padding = [iTop, iRight, iBot, iLeft];
19426         }
19427     },
19428
19429     /**
19430      * Stores the initial placement of the linked element.
19431      * @method setInitialPosition
19432      * @param {int} diffX   the X offset, default 0
19433      * @param {int} diffY   the Y offset, default 0
19434      */
19435     setInitPosition: function(diffX, diffY) {
19436         var el = this.getEl();
19437
19438         if (!this.DDM.verifyEl(el)) {
19439             return;
19440         }
19441
19442         var dx = diffX || 0;
19443         var dy = diffY || 0;
19444
19445         var p = Dom.getXY( el );
19446
19447         this.initPageX = p[0] - dx;
19448         this.initPageY = p[1] - dy;
19449
19450         this.lastPageX = p[0];
19451         this.lastPageY = p[1];
19452
19453
19454         this.setStartPosition(p);
19455     },
19456
19457     /**
19458      * Sets the start position of the element.  This is set when the obj
19459      * is initialized, the reset when a drag is started.
19460      * @method setStartPosition
19461      * @param pos current position (from previous lookup)
19462      * @private
19463      */
19464     setStartPosition: function(pos) {
19465         var p = pos || Dom.getXY( this.getEl() );
19466         this.deltaSetXY = null;
19467
19468         this.startPageX = p[0];
19469         this.startPageY = p[1];
19470     },
19471
19472     /**
19473      * Add this instance to a group of related drag/drop objects.  All
19474      * instances belong to at least one group, and can belong to as many
19475      * groups as needed.
19476      * @method addToGroup
19477      * @param sGroup {string} the name of the group
19478      */
19479     addToGroup: function(sGroup) {
19480         this.groups[sGroup] = true;
19481         this.DDM.regDragDrop(this, sGroup);
19482     },
19483
19484     /**
19485      * Remove's this instance from the supplied interaction group
19486      * @method removeFromGroup
19487      * @param {string}  sGroup  The group to drop
19488      */
19489     removeFromGroup: function(sGroup) {
19490         if (this.groups[sGroup]) {
19491             delete this.groups[sGroup];
19492         }
19493
19494         this.DDM.removeDDFromGroup(this, sGroup);
19495     },
19496
19497     /**
19498      * Allows you to specify that an element other than the linked element
19499      * will be moved with the cursor during a drag
19500      * @method setDragElId
19501      * @param id {string} the id of the element that will be used to initiate the drag
19502      */
19503     setDragElId: function(id) {
19504         this.dragElId = id;
19505     },
19506
19507     /**
19508      * Allows you to specify a child of the linked element that should be
19509      * used to initiate the drag operation.  An example of this would be if
19510      * you have a content div with text and links.  Clicking anywhere in the
19511      * content area would normally start the drag operation.  Use this method
19512      * to specify that an element inside of the content div is the element
19513      * that starts the drag operation.
19514      * @method setHandleElId
19515      * @param id {string} the id of the element that will be used to
19516      * initiate the drag.
19517      */
19518     setHandleElId: function(id) {
19519         if (typeof id !== "string") {
19520             id = Roo.id(id);
19521         }
19522         this.handleElId = id;
19523         this.DDM.regHandle(this.id, id);
19524     },
19525
19526     /**
19527      * Allows you to set an element outside of the linked element as a drag
19528      * handle
19529      * @method setOuterHandleElId
19530      * @param id the id of the element that will be used to initiate the drag
19531      */
19532     setOuterHandleElId: function(id) {
19533         if (typeof id !== "string") {
19534             id = Roo.id(id);
19535         }
19536         Event.on(id, "mousedown",
19537                 this.handleMouseDown, this);
19538         this.setHandleElId(id);
19539
19540         this.hasOuterHandles = true;
19541     },
19542
19543     /**
19544      * Remove all drag and drop hooks for this element
19545      * @method unreg
19546      */
19547     unreg: function() {
19548         Event.un(this.id, "mousedown",
19549                 this.handleMouseDown);
19550         Event.un(this.id, "touchstart",
19551                 this.handleMouseDown);
19552         this._domRef = null;
19553         this.DDM._remove(this);
19554     },
19555
19556     destroy : function(){
19557         this.unreg();
19558     },
19559
19560     /**
19561      * Returns true if this instance is locked, or the drag drop mgr is locked
19562      * (meaning that all drag/drop is disabled on the page.)
19563      * @method isLocked
19564      * @return {boolean} true if this obj or all drag/drop is locked, else
19565      * false
19566      */
19567     isLocked: function() {
19568         return (this.DDM.isLocked() || this.locked);
19569     },
19570
19571     /**
19572      * Fired when this object is clicked
19573      * @method handleMouseDown
19574      * @param {Event} e
19575      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19576      * @private
19577      */
19578     handleMouseDown: function(e, oDD){
19579      
19580         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19581             //Roo.log('not touch/ button !=0');
19582             return;
19583         }
19584         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19585             return; // double touch..
19586         }
19587         
19588
19589         if (this.isLocked()) {
19590             //Roo.log('locked');
19591             return;
19592         }
19593
19594         this.DDM.refreshCache(this.groups);
19595 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19596         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19597         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19598             //Roo.log('no outer handes or not over target');
19599                 // do nothing.
19600         } else {
19601 //            Roo.log('check validator');
19602             if (this.clickValidator(e)) {
19603 //                Roo.log('validate success');
19604                 // set the initial element position
19605                 this.setStartPosition();
19606
19607
19608                 this.b4MouseDown(e);
19609                 this.onMouseDown(e);
19610
19611                 this.DDM.handleMouseDown(e, this);
19612
19613                 this.DDM.stopEvent(e);
19614             } else {
19615
19616
19617             }
19618         }
19619     },
19620
19621     clickValidator: function(e) {
19622         var target = e.getTarget();
19623         return ( this.isValidHandleChild(target) &&
19624                     (this.id == this.handleElId ||
19625                         this.DDM.handleWasClicked(target, this.id)) );
19626     },
19627
19628     /**
19629      * Allows you to specify a tag name that should not start a drag operation
19630      * when clicked.  This is designed to facilitate embedding links within a
19631      * drag handle that do something other than start the drag.
19632      * @method addInvalidHandleType
19633      * @param {string} tagName the type of element to exclude
19634      */
19635     addInvalidHandleType: function(tagName) {
19636         var type = tagName.toUpperCase();
19637         this.invalidHandleTypes[type] = type;
19638     },
19639
19640     /**
19641      * Lets you to specify an element id for a child of a drag handle
19642      * that should not initiate a drag
19643      * @method addInvalidHandleId
19644      * @param {string} id the element id of the element you wish to ignore
19645      */
19646     addInvalidHandleId: function(id) {
19647         if (typeof id !== "string") {
19648             id = Roo.id(id);
19649         }
19650         this.invalidHandleIds[id] = id;
19651     },
19652
19653     /**
19654      * Lets you specify a css class of elements that will not initiate a drag
19655      * @method addInvalidHandleClass
19656      * @param {string} cssClass the class of the elements you wish to ignore
19657      */
19658     addInvalidHandleClass: function(cssClass) {
19659         this.invalidHandleClasses.push(cssClass);
19660     },
19661
19662     /**
19663      * Unsets an excluded tag name set by addInvalidHandleType
19664      * @method removeInvalidHandleType
19665      * @param {string} tagName the type of element to unexclude
19666      */
19667     removeInvalidHandleType: function(tagName) {
19668         var type = tagName.toUpperCase();
19669         // this.invalidHandleTypes[type] = null;
19670         delete this.invalidHandleTypes[type];
19671     },
19672
19673     /**
19674      * Unsets an invalid handle id
19675      * @method removeInvalidHandleId
19676      * @param {string} id the id of the element to re-enable
19677      */
19678     removeInvalidHandleId: function(id) {
19679         if (typeof id !== "string") {
19680             id = Roo.id(id);
19681         }
19682         delete this.invalidHandleIds[id];
19683     },
19684
19685     /**
19686      * Unsets an invalid css class
19687      * @method removeInvalidHandleClass
19688      * @param {string} cssClass the class of the element(s) you wish to
19689      * re-enable
19690      */
19691     removeInvalidHandleClass: function(cssClass) {
19692         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19693             if (this.invalidHandleClasses[i] == cssClass) {
19694                 delete this.invalidHandleClasses[i];
19695             }
19696         }
19697     },
19698
19699     /**
19700      * Checks the tag exclusion list to see if this click should be ignored
19701      * @method isValidHandleChild
19702      * @param {HTMLElement} node the HTMLElement to evaluate
19703      * @return {boolean} true if this is a valid tag type, false if not
19704      */
19705     isValidHandleChild: function(node) {
19706
19707         var valid = true;
19708         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19709         var nodeName;
19710         try {
19711             nodeName = node.nodeName.toUpperCase();
19712         } catch(e) {
19713             nodeName = node.nodeName;
19714         }
19715         valid = valid && !this.invalidHandleTypes[nodeName];
19716         valid = valid && !this.invalidHandleIds[node.id];
19717
19718         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19719             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19720         }
19721
19722
19723         return valid;
19724
19725     },
19726
19727     /**
19728      * Create the array of horizontal tick marks if an interval was specified
19729      * in setXConstraint().
19730      * @method setXTicks
19731      * @private
19732      */
19733     setXTicks: function(iStartX, iTickSize) {
19734         this.xTicks = [];
19735         this.xTickSize = iTickSize;
19736
19737         var tickMap = {};
19738
19739         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19740             if (!tickMap[i]) {
19741                 this.xTicks[this.xTicks.length] = i;
19742                 tickMap[i] = true;
19743             }
19744         }
19745
19746         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19747             if (!tickMap[i]) {
19748                 this.xTicks[this.xTicks.length] = i;
19749                 tickMap[i] = true;
19750             }
19751         }
19752
19753         this.xTicks.sort(this.DDM.numericSort) ;
19754     },
19755
19756     /**
19757      * Create the array of vertical tick marks if an interval was specified in
19758      * setYConstraint().
19759      * @method setYTicks
19760      * @private
19761      */
19762     setYTicks: function(iStartY, iTickSize) {
19763         this.yTicks = [];
19764         this.yTickSize = iTickSize;
19765
19766         var tickMap = {};
19767
19768         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19769             if (!tickMap[i]) {
19770                 this.yTicks[this.yTicks.length] = i;
19771                 tickMap[i] = true;
19772             }
19773         }
19774
19775         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19776             if (!tickMap[i]) {
19777                 this.yTicks[this.yTicks.length] = i;
19778                 tickMap[i] = true;
19779             }
19780         }
19781
19782         this.yTicks.sort(this.DDM.numericSort) ;
19783     },
19784
19785     /**
19786      * By default, the element can be dragged any place on the screen.  Use
19787      * this method to limit the horizontal travel of the element.  Pass in
19788      * 0,0 for the parameters if you want to lock the drag to the y axis.
19789      * @method setXConstraint
19790      * @param {int} iLeft the number of pixels the element can move to the left
19791      * @param {int} iRight the number of pixels the element can move to the
19792      * right
19793      * @param {int} iTickSize optional parameter for specifying that the
19794      * element
19795      * should move iTickSize pixels at a time.
19796      */
19797     setXConstraint: function(iLeft, iRight, iTickSize) {
19798         this.leftConstraint = iLeft;
19799         this.rightConstraint = iRight;
19800
19801         this.minX = this.initPageX - iLeft;
19802         this.maxX = this.initPageX + iRight;
19803         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19804
19805         this.constrainX = true;
19806     },
19807
19808     /**
19809      * Clears any constraints applied to this instance.  Also clears ticks
19810      * since they can't exist independent of a constraint at this time.
19811      * @method clearConstraints
19812      */
19813     clearConstraints: function() {
19814         this.constrainX = false;
19815         this.constrainY = false;
19816         this.clearTicks();
19817     },
19818
19819     /**
19820      * Clears any tick interval defined for this instance
19821      * @method clearTicks
19822      */
19823     clearTicks: function() {
19824         this.xTicks = null;
19825         this.yTicks = null;
19826         this.xTickSize = 0;
19827         this.yTickSize = 0;
19828     },
19829
19830     /**
19831      * By default, the element can be dragged any place on the screen.  Set
19832      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19833      * parameters if you want to lock the drag to the x axis.
19834      * @method setYConstraint
19835      * @param {int} iUp the number of pixels the element can move up
19836      * @param {int} iDown the number of pixels the element can move down
19837      * @param {int} iTickSize optional parameter for specifying that the
19838      * element should move iTickSize pixels at a time.
19839      */
19840     setYConstraint: function(iUp, iDown, iTickSize) {
19841         this.topConstraint = iUp;
19842         this.bottomConstraint = iDown;
19843
19844         this.minY = this.initPageY - iUp;
19845         this.maxY = this.initPageY + iDown;
19846         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19847
19848         this.constrainY = true;
19849
19850     },
19851
19852     /**
19853      * resetConstraints must be called if you manually reposition a dd element.
19854      * @method resetConstraints
19855      * @param {boolean} maintainOffset
19856      */
19857     resetConstraints: function() {
19858
19859
19860         // Maintain offsets if necessary
19861         if (this.initPageX || this.initPageX === 0) {
19862             // figure out how much this thing has moved
19863             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19864             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19865
19866             this.setInitPosition(dx, dy);
19867
19868         // This is the first time we have detected the element's position
19869         } else {
19870             this.setInitPosition();
19871         }
19872
19873         if (this.constrainX) {
19874             this.setXConstraint( this.leftConstraint,
19875                                  this.rightConstraint,
19876                                  this.xTickSize        );
19877         }
19878
19879         if (this.constrainY) {
19880             this.setYConstraint( this.topConstraint,
19881                                  this.bottomConstraint,
19882                                  this.yTickSize         );
19883         }
19884     },
19885
19886     /**
19887      * Normally the drag element is moved pixel by pixel, but we can specify
19888      * that it move a number of pixels at a time.  This method resolves the
19889      * location when we have it set up like this.
19890      * @method getTick
19891      * @param {int} val where we want to place the object
19892      * @param {int[]} tickArray sorted array of valid points
19893      * @return {int} the closest tick
19894      * @private
19895      */
19896     getTick: function(val, tickArray) {
19897
19898         if (!tickArray) {
19899             // If tick interval is not defined, it is effectively 1 pixel,
19900             // so we return the value passed to us.
19901             return val;
19902         } else if (tickArray[0] >= val) {
19903             // The value is lower than the first tick, so we return the first
19904             // tick.
19905             return tickArray[0];
19906         } else {
19907             for (var i=0, len=tickArray.length; i<len; ++i) {
19908                 var next = i + 1;
19909                 if (tickArray[next] && tickArray[next] >= val) {
19910                     var diff1 = val - tickArray[i];
19911                     var diff2 = tickArray[next] - val;
19912                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19913                 }
19914             }
19915
19916             // The value is larger than the last tick, so we return the last
19917             // tick.
19918             return tickArray[tickArray.length - 1];
19919         }
19920     },
19921
19922     /**
19923      * toString method
19924      * @method toString
19925      * @return {string} string representation of the dd obj
19926      */
19927     toString: function() {
19928         return ("DragDrop " + this.id);
19929     }
19930
19931 });
19932
19933 })();
19934 /*
19935  * Based on:
19936  * Ext JS Library 1.1.1
19937  * Copyright(c) 2006-2007, Ext JS, LLC.
19938  *
19939  * Originally Released Under LGPL - original licence link has changed is not relivant.
19940  *
19941  * Fork - LGPL
19942  * <script type="text/javascript">
19943  */
19944
19945
19946 /**
19947  * The drag and drop utility provides a framework for building drag and drop
19948  * applications.  In addition to enabling drag and drop for specific elements,
19949  * the drag and drop elements are tracked by the manager class, and the
19950  * interactions between the various elements are tracked during the drag and
19951  * the implementing code is notified about these important moments.
19952  */
19953
19954 // Only load the library once.  Rewriting the manager class would orphan
19955 // existing drag and drop instances.
19956 if (!Roo.dd.DragDropMgr) {
19957
19958 /**
19959  * @class Roo.dd.DragDropMgr
19960  * DragDropMgr is a singleton that tracks the element interaction for
19961  * all DragDrop items in the window.  Generally, you will not call
19962  * this class directly, but it does have helper methods that could
19963  * be useful in your DragDrop implementations.
19964  * @singleton
19965  */
19966 Roo.dd.DragDropMgr = function() {
19967
19968     var Event = Roo.EventManager;
19969
19970     return {
19971
19972         /**
19973          * Two dimensional Array of registered DragDrop objects.  The first
19974          * dimension is the DragDrop item group, the second the DragDrop
19975          * object.
19976          * @property ids
19977          * @type {string: string}
19978          * @private
19979          * @static
19980          */
19981         ids: {},
19982
19983         /**
19984          * Array of element ids defined as drag handles.  Used to determine
19985          * if the element that generated the mousedown event is actually the
19986          * handle and not the html element itself.
19987          * @property handleIds
19988          * @type {string: string}
19989          * @private
19990          * @static
19991          */
19992         handleIds: {},
19993
19994         /**
19995          * the DragDrop object that is currently being dragged
19996          * @property dragCurrent
19997          * @type DragDrop
19998          * @private
19999          * @static
20000          **/
20001         dragCurrent: null,
20002
20003         /**
20004          * the DragDrop object(s) that are being hovered over
20005          * @property dragOvers
20006          * @type Array
20007          * @private
20008          * @static
20009          */
20010         dragOvers: {},
20011
20012         /**
20013          * the X distance between the cursor and the object being dragged
20014          * @property deltaX
20015          * @type int
20016          * @private
20017          * @static
20018          */
20019         deltaX: 0,
20020
20021         /**
20022          * the Y distance between the cursor and the object being dragged
20023          * @property deltaY
20024          * @type int
20025          * @private
20026          * @static
20027          */
20028         deltaY: 0,
20029
20030         /**
20031          * Flag to determine if we should prevent the default behavior of the
20032          * events we define. By default this is true, but this can be set to
20033          * false if you need the default behavior (not recommended)
20034          * @property preventDefault
20035          * @type boolean
20036          * @static
20037          */
20038         preventDefault: true,
20039
20040         /**
20041          * Flag to determine if we should stop the propagation of the events
20042          * we generate. This is true by default but you may want to set it to
20043          * false if the html element contains other features that require the
20044          * mouse click.
20045          * @property stopPropagation
20046          * @type boolean
20047          * @static
20048          */
20049         stopPropagation: true,
20050
20051         /**
20052          * Internal flag that is set to true when drag and drop has been
20053          * intialized
20054          * @property initialized
20055          * @private
20056          * @static
20057          */
20058         initalized: false,
20059
20060         /**
20061          * All drag and drop can be disabled.
20062          * @property locked
20063          * @private
20064          * @static
20065          */
20066         locked: false,
20067
20068         /**
20069          * Called the first time an element is registered.
20070          * @method init
20071          * @private
20072          * @static
20073          */
20074         init: function() {
20075             this.initialized = true;
20076         },
20077
20078         /**
20079          * In point mode, drag and drop interaction is defined by the
20080          * location of the cursor during the drag/drop
20081          * @property POINT
20082          * @type int
20083          * @static
20084          */
20085         POINT: 0,
20086
20087         /**
20088          * In intersect mode, drag and drop interactio nis defined by the
20089          * overlap of two or more drag and drop objects.
20090          * @property INTERSECT
20091          * @type int
20092          * @static
20093          */
20094         INTERSECT: 1,
20095
20096         /**
20097          * The current drag and drop mode.  Default: POINT
20098          * @property mode
20099          * @type int
20100          * @static
20101          */
20102         mode: 0,
20103
20104         /**
20105          * Runs method on all drag and drop objects
20106          * @method _execOnAll
20107          * @private
20108          * @static
20109          */
20110         _execOnAll: function(sMethod, args) {
20111             for (var i in this.ids) {
20112                 for (var j in this.ids[i]) {
20113                     var oDD = this.ids[i][j];
20114                     if (! this.isTypeOfDD(oDD)) {
20115                         continue;
20116                     }
20117                     oDD[sMethod].apply(oDD, args);
20118                 }
20119             }
20120         },
20121
20122         /**
20123          * Drag and drop initialization.  Sets up the global event handlers
20124          * @method _onLoad
20125          * @private
20126          * @static
20127          */
20128         _onLoad: function() {
20129
20130             this.init();
20131
20132             if (!Roo.isTouch) {
20133                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20134                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20135             }
20136             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20137             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20138             
20139             Event.on(window,   "unload",    this._onUnload, this, true);
20140             Event.on(window,   "resize",    this._onResize, this, true);
20141             // Event.on(window,   "mouseout",    this._test);
20142
20143         },
20144
20145         /**
20146          * Reset constraints on all drag and drop objs
20147          * @method _onResize
20148          * @private
20149          * @static
20150          */
20151         _onResize: function(e) {
20152             this._execOnAll("resetConstraints", []);
20153         },
20154
20155         /**
20156          * Lock all drag and drop functionality
20157          * @method lock
20158          * @static
20159          */
20160         lock: function() { this.locked = true; },
20161
20162         /**
20163          * Unlock all drag and drop functionality
20164          * @method unlock
20165          * @static
20166          */
20167         unlock: function() { this.locked = false; },
20168
20169         /**
20170          * Is drag and drop locked?
20171          * @method isLocked
20172          * @return {boolean} True if drag and drop is locked, false otherwise.
20173          * @static
20174          */
20175         isLocked: function() { return this.locked; },
20176
20177         /**
20178          * Location cache that is set for all drag drop objects when a drag is
20179          * initiated, cleared when the drag is finished.
20180          * @property locationCache
20181          * @private
20182          * @static
20183          */
20184         locationCache: {},
20185
20186         /**
20187          * Set useCache to false if you want to force object the lookup of each
20188          * drag and drop linked element constantly during a drag.
20189          * @property useCache
20190          * @type boolean
20191          * @static
20192          */
20193         useCache: true,
20194
20195         /**
20196          * The number of pixels that the mouse needs to move after the
20197          * mousedown before the drag is initiated.  Default=3;
20198          * @property clickPixelThresh
20199          * @type int
20200          * @static
20201          */
20202         clickPixelThresh: 3,
20203
20204         /**
20205          * The number of milliseconds after the mousedown event to initiate the
20206          * drag if we don't get a mouseup event. Default=1000
20207          * @property clickTimeThresh
20208          * @type int
20209          * @static
20210          */
20211         clickTimeThresh: 350,
20212
20213         /**
20214          * Flag that indicates that either the drag pixel threshold or the
20215          * mousdown time threshold has been met
20216          * @property dragThreshMet
20217          * @type boolean
20218          * @private
20219          * @static
20220          */
20221         dragThreshMet: false,
20222
20223         /**
20224          * Timeout used for the click time threshold
20225          * @property clickTimeout
20226          * @type Object
20227          * @private
20228          * @static
20229          */
20230         clickTimeout: null,
20231
20232         /**
20233          * The X position of the mousedown event stored for later use when a
20234          * drag threshold is met.
20235          * @property startX
20236          * @type int
20237          * @private
20238          * @static
20239          */
20240         startX: 0,
20241
20242         /**
20243          * The Y position of the mousedown event stored for later use when a
20244          * drag threshold is met.
20245          * @property startY
20246          * @type int
20247          * @private
20248          * @static
20249          */
20250         startY: 0,
20251
20252         /**
20253          * Each DragDrop instance must be registered with the DragDropMgr.
20254          * This is executed in DragDrop.init()
20255          * @method regDragDrop
20256          * @param {DragDrop} oDD the DragDrop object to register
20257          * @param {String} sGroup the name of the group this element belongs to
20258          * @static
20259          */
20260         regDragDrop: function(oDD, sGroup) {
20261             if (!this.initialized) { this.init(); }
20262
20263             if (!this.ids[sGroup]) {
20264                 this.ids[sGroup] = {};
20265             }
20266             this.ids[sGroup][oDD.id] = oDD;
20267         },
20268
20269         /**
20270          * Removes the supplied dd instance from the supplied group. Executed
20271          * by DragDrop.removeFromGroup, so don't call this function directly.
20272          * @method removeDDFromGroup
20273          * @private
20274          * @static
20275          */
20276         removeDDFromGroup: function(oDD, sGroup) {
20277             if (!this.ids[sGroup]) {
20278                 this.ids[sGroup] = {};
20279             }
20280
20281             var obj = this.ids[sGroup];
20282             if (obj && obj[oDD.id]) {
20283                 delete obj[oDD.id];
20284             }
20285         },
20286
20287         /**
20288          * Unregisters a drag and drop item.  This is executed in
20289          * DragDrop.unreg, use that method instead of calling this directly.
20290          * @method _remove
20291          * @private
20292          * @static
20293          */
20294         _remove: function(oDD) {
20295             for (var g in oDD.groups) {
20296                 if (g && this.ids[g][oDD.id]) {
20297                     delete this.ids[g][oDD.id];
20298                 }
20299             }
20300             delete this.handleIds[oDD.id];
20301         },
20302
20303         /**
20304          * Each DragDrop handle element must be registered.  This is done
20305          * automatically when executing DragDrop.setHandleElId()
20306          * @method regHandle
20307          * @param {String} sDDId the DragDrop id this element is a handle for
20308          * @param {String} sHandleId the id of the element that is the drag
20309          * handle
20310          * @static
20311          */
20312         regHandle: function(sDDId, sHandleId) {
20313             if (!this.handleIds[sDDId]) {
20314                 this.handleIds[sDDId] = {};
20315             }
20316             this.handleIds[sDDId][sHandleId] = sHandleId;
20317         },
20318
20319         /**
20320          * Utility function to determine if a given element has been
20321          * registered as a drag drop item.
20322          * @method isDragDrop
20323          * @param {String} id the element id to check
20324          * @return {boolean} true if this element is a DragDrop item,
20325          * false otherwise
20326          * @static
20327          */
20328         isDragDrop: function(id) {
20329             return ( this.getDDById(id) ) ? true : false;
20330         },
20331
20332         /**
20333          * Returns the drag and drop instances that are in all groups the
20334          * passed in instance belongs to.
20335          * @method getRelated
20336          * @param {DragDrop} p_oDD the obj to get related data for
20337          * @param {boolean} bTargetsOnly if true, only return targetable objs
20338          * @return {DragDrop[]} the related instances
20339          * @static
20340          */
20341         getRelated: function(p_oDD, bTargetsOnly) {
20342             var oDDs = [];
20343             for (var i in p_oDD.groups) {
20344                 for (j in this.ids[i]) {
20345                     var dd = this.ids[i][j];
20346                     if (! this.isTypeOfDD(dd)) {
20347                         continue;
20348                     }
20349                     if (!bTargetsOnly || dd.isTarget) {
20350                         oDDs[oDDs.length] = dd;
20351                     }
20352                 }
20353             }
20354
20355             return oDDs;
20356         },
20357
20358         /**
20359          * Returns true if the specified dd target is a legal target for
20360          * the specifice drag obj
20361          * @method isLegalTarget
20362          * @param {DragDrop} the drag obj
20363          * @param {DragDrop} the target
20364          * @return {boolean} true if the target is a legal target for the
20365          * dd obj
20366          * @static
20367          */
20368         isLegalTarget: function (oDD, oTargetDD) {
20369             var targets = this.getRelated(oDD, true);
20370             for (var i=0, len=targets.length;i<len;++i) {
20371                 if (targets[i].id == oTargetDD.id) {
20372                     return true;
20373                 }
20374             }
20375
20376             return false;
20377         },
20378
20379         /**
20380          * My goal is to be able to transparently determine if an object is
20381          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20382          * returns "object", oDD.constructor.toString() always returns
20383          * "DragDrop" and not the name of the subclass.  So for now it just
20384          * evaluates a well-known variable in DragDrop.
20385          * @method isTypeOfDD
20386          * @param {Object} the object to evaluate
20387          * @return {boolean} true if typeof oDD = DragDrop
20388          * @static
20389          */
20390         isTypeOfDD: function (oDD) {
20391             return (oDD && oDD.__ygDragDrop);
20392         },
20393
20394         /**
20395          * Utility function to determine if a given element has been
20396          * registered as a drag drop handle for the given Drag Drop object.
20397          * @method isHandle
20398          * @param {String} id the element id to check
20399          * @return {boolean} true if this element is a DragDrop handle, false
20400          * otherwise
20401          * @static
20402          */
20403         isHandle: function(sDDId, sHandleId) {
20404             return ( this.handleIds[sDDId] &&
20405                             this.handleIds[sDDId][sHandleId] );
20406         },
20407
20408         /**
20409          * Returns the DragDrop instance for a given id
20410          * @method getDDById
20411          * @param {String} id the id of the DragDrop object
20412          * @return {DragDrop} the drag drop object, null if it is not found
20413          * @static
20414          */
20415         getDDById: function(id) {
20416             for (var i in this.ids) {
20417                 if (this.ids[i][id]) {
20418                     return this.ids[i][id];
20419                 }
20420             }
20421             return null;
20422         },
20423
20424         /**
20425          * Fired after a registered DragDrop object gets the mousedown event.
20426          * Sets up the events required to track the object being dragged
20427          * @method handleMouseDown
20428          * @param {Event} e the event
20429          * @param oDD the DragDrop object being dragged
20430          * @private
20431          * @static
20432          */
20433         handleMouseDown: function(e, oDD) {
20434             if(Roo.QuickTips){
20435                 Roo.QuickTips.disable();
20436             }
20437             this.currentTarget = e.getTarget();
20438
20439             this.dragCurrent = oDD;
20440
20441             var el = oDD.getEl();
20442
20443             // track start position
20444             this.startX = e.getPageX();
20445             this.startY = e.getPageY();
20446
20447             this.deltaX = this.startX - el.offsetLeft;
20448             this.deltaY = this.startY - el.offsetTop;
20449
20450             this.dragThreshMet = false;
20451
20452             this.clickTimeout = setTimeout(
20453                     function() {
20454                         var DDM = Roo.dd.DDM;
20455                         DDM.startDrag(DDM.startX, DDM.startY);
20456                     },
20457                     this.clickTimeThresh );
20458         },
20459
20460         /**
20461          * Fired when either the drag pixel threshol or the mousedown hold
20462          * time threshold has been met.
20463          * @method startDrag
20464          * @param x {int} the X position of the original mousedown
20465          * @param y {int} the Y position of the original mousedown
20466          * @static
20467          */
20468         startDrag: function(x, y) {
20469             clearTimeout(this.clickTimeout);
20470             if (this.dragCurrent) {
20471                 this.dragCurrent.b4StartDrag(x, y);
20472                 this.dragCurrent.startDrag(x, y);
20473             }
20474             this.dragThreshMet = true;
20475         },
20476
20477         /**
20478          * Internal function to handle the mouseup event.  Will be invoked
20479          * from the context of the document.
20480          * @method handleMouseUp
20481          * @param {Event} e the event
20482          * @private
20483          * @static
20484          */
20485         handleMouseUp: function(e) {
20486
20487             if(Roo.QuickTips){
20488                 Roo.QuickTips.enable();
20489             }
20490             if (! this.dragCurrent) {
20491                 return;
20492             }
20493
20494             clearTimeout(this.clickTimeout);
20495
20496             if (this.dragThreshMet) {
20497                 this.fireEvents(e, true);
20498             } else {
20499             }
20500
20501             this.stopDrag(e);
20502
20503             this.stopEvent(e);
20504         },
20505
20506         /**
20507          * Utility to stop event propagation and event default, if these
20508          * features are turned on.
20509          * @method stopEvent
20510          * @param {Event} e the event as returned by this.getEvent()
20511          * @static
20512          */
20513         stopEvent: function(e){
20514             if(this.stopPropagation) {
20515                 e.stopPropagation();
20516             }
20517
20518             if (this.preventDefault) {
20519                 e.preventDefault();
20520             }
20521         },
20522
20523         /**
20524          * Internal function to clean up event handlers after the drag
20525          * operation is complete
20526          * @method stopDrag
20527          * @param {Event} e the event
20528          * @private
20529          * @static
20530          */
20531         stopDrag: function(e) {
20532             // Fire the drag end event for the item that was dragged
20533             if (this.dragCurrent) {
20534                 if (this.dragThreshMet) {
20535                     this.dragCurrent.b4EndDrag(e);
20536                     this.dragCurrent.endDrag(e);
20537                 }
20538
20539                 this.dragCurrent.onMouseUp(e);
20540             }
20541
20542             this.dragCurrent = null;
20543             this.dragOvers = {};
20544         },
20545
20546         /**
20547          * Internal function to handle the mousemove event.  Will be invoked
20548          * from the context of the html element.
20549          *
20550          * @TODO figure out what we can do about mouse events lost when the
20551          * user drags objects beyond the window boundary.  Currently we can
20552          * detect this in internet explorer by verifying that the mouse is
20553          * down during the mousemove event.  Firefox doesn't give us the
20554          * button state on the mousemove event.
20555          * @method handleMouseMove
20556          * @param {Event} e the event
20557          * @private
20558          * @static
20559          */
20560         handleMouseMove: function(e) {
20561             if (! this.dragCurrent) {
20562                 return true;
20563             }
20564
20565             // var button = e.which || e.button;
20566
20567             // check for IE mouseup outside of page boundary
20568             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20569                 this.stopEvent(e);
20570                 return this.handleMouseUp(e);
20571             }
20572
20573             if (!this.dragThreshMet) {
20574                 var diffX = Math.abs(this.startX - e.getPageX());
20575                 var diffY = Math.abs(this.startY - e.getPageY());
20576                 if (diffX > this.clickPixelThresh ||
20577                             diffY > this.clickPixelThresh) {
20578                     this.startDrag(this.startX, this.startY);
20579                 }
20580             }
20581
20582             if (this.dragThreshMet) {
20583                 this.dragCurrent.b4Drag(e);
20584                 this.dragCurrent.onDrag(e);
20585                 if(!this.dragCurrent.moveOnly){
20586                     this.fireEvents(e, false);
20587                 }
20588             }
20589
20590             this.stopEvent(e);
20591
20592             return true;
20593         },
20594
20595         /**
20596          * Iterates over all of the DragDrop elements to find ones we are
20597          * hovering over or dropping on
20598          * @method fireEvents
20599          * @param {Event} e the event
20600          * @param {boolean} isDrop is this a drop op or a mouseover op?
20601          * @private
20602          * @static
20603          */
20604         fireEvents: function(e, isDrop) {
20605             var dc = this.dragCurrent;
20606
20607             // If the user did the mouse up outside of the window, we could
20608             // get here even though we have ended the drag.
20609             if (!dc || dc.isLocked()) {
20610                 return;
20611             }
20612
20613             var pt = e.getPoint();
20614
20615             // cache the previous dragOver array
20616             var oldOvers = [];
20617
20618             var outEvts   = [];
20619             var overEvts  = [];
20620             var dropEvts  = [];
20621             var enterEvts = [];
20622
20623             // Check to see if the object(s) we were hovering over is no longer
20624             // being hovered over so we can fire the onDragOut event
20625             for (var i in this.dragOvers) {
20626
20627                 var ddo = this.dragOvers[i];
20628
20629                 if (! this.isTypeOfDD(ddo)) {
20630                     continue;
20631                 }
20632
20633                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20634                     outEvts.push( ddo );
20635                 }
20636
20637                 oldOvers[i] = true;
20638                 delete this.dragOvers[i];
20639             }
20640
20641             for (var sGroup in dc.groups) {
20642
20643                 if ("string" != typeof sGroup) {
20644                     continue;
20645                 }
20646
20647                 for (i in this.ids[sGroup]) {
20648                     var oDD = this.ids[sGroup][i];
20649                     if (! this.isTypeOfDD(oDD)) {
20650                         continue;
20651                     }
20652
20653                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20654                         if (this.isOverTarget(pt, oDD, this.mode)) {
20655                             // look for drop interactions
20656                             if (isDrop) {
20657                                 dropEvts.push( oDD );
20658                             // look for drag enter and drag over interactions
20659                             } else {
20660
20661                                 // initial drag over: dragEnter fires
20662                                 if (!oldOvers[oDD.id]) {
20663                                     enterEvts.push( oDD );
20664                                 // subsequent drag overs: dragOver fires
20665                                 } else {
20666                                     overEvts.push( oDD );
20667                                 }
20668
20669                                 this.dragOvers[oDD.id] = oDD;
20670                             }
20671                         }
20672                     }
20673                 }
20674             }
20675
20676             if (this.mode) {
20677                 if (outEvts.length) {
20678                     dc.b4DragOut(e, outEvts);
20679                     dc.onDragOut(e, outEvts);
20680                 }
20681
20682                 if (enterEvts.length) {
20683                     dc.onDragEnter(e, enterEvts);
20684                 }
20685
20686                 if (overEvts.length) {
20687                     dc.b4DragOver(e, overEvts);
20688                     dc.onDragOver(e, overEvts);
20689                 }
20690
20691                 if (dropEvts.length) {
20692                     dc.b4DragDrop(e, dropEvts);
20693                     dc.onDragDrop(e, dropEvts);
20694                 }
20695
20696             } else {
20697                 // fire dragout events
20698                 var len = 0;
20699                 for (i=0, len=outEvts.length; i<len; ++i) {
20700                     dc.b4DragOut(e, outEvts[i].id);
20701                     dc.onDragOut(e, outEvts[i].id);
20702                 }
20703
20704                 // fire enter events
20705                 for (i=0,len=enterEvts.length; i<len; ++i) {
20706                     // dc.b4DragEnter(e, oDD.id);
20707                     dc.onDragEnter(e, enterEvts[i].id);
20708                 }
20709
20710                 // fire over events
20711                 for (i=0,len=overEvts.length; i<len; ++i) {
20712                     dc.b4DragOver(e, overEvts[i].id);
20713                     dc.onDragOver(e, overEvts[i].id);
20714                 }
20715
20716                 // fire drop events
20717                 for (i=0, len=dropEvts.length; i<len; ++i) {
20718                     dc.b4DragDrop(e, dropEvts[i].id);
20719                     dc.onDragDrop(e, dropEvts[i].id);
20720                 }
20721
20722             }
20723
20724             // notify about a drop that did not find a target
20725             if (isDrop && !dropEvts.length) {
20726                 dc.onInvalidDrop(e);
20727             }
20728
20729         },
20730
20731         /**
20732          * Helper function for getting the best match from the list of drag
20733          * and drop objects returned by the drag and drop events when we are
20734          * in INTERSECT mode.  It returns either the first object that the
20735          * cursor is over, or the object that has the greatest overlap with
20736          * the dragged element.
20737          * @method getBestMatch
20738          * @param  {DragDrop[]} dds The array of drag and drop objects
20739          * targeted
20740          * @return {DragDrop}       The best single match
20741          * @static
20742          */
20743         getBestMatch: function(dds) {
20744             var winner = null;
20745             // Return null if the input is not what we expect
20746             //if (!dds || !dds.length || dds.length == 0) {
20747                // winner = null;
20748             // If there is only one item, it wins
20749             //} else if (dds.length == 1) {
20750
20751             var len = dds.length;
20752
20753             if (len == 1) {
20754                 winner = dds[0];
20755             } else {
20756                 // Loop through the targeted items
20757                 for (var i=0; i<len; ++i) {
20758                     var dd = dds[i];
20759                     // If the cursor is over the object, it wins.  If the
20760                     // cursor is over multiple matches, the first one we come
20761                     // to wins.
20762                     if (dd.cursorIsOver) {
20763                         winner = dd;
20764                         break;
20765                     // Otherwise the object with the most overlap wins
20766                     } else {
20767                         if (!winner ||
20768                             winner.overlap.getArea() < dd.overlap.getArea()) {
20769                             winner = dd;
20770                         }
20771                     }
20772                 }
20773             }
20774
20775             return winner;
20776         },
20777
20778         /**
20779          * Refreshes the cache of the top-left and bottom-right points of the
20780          * drag and drop objects in the specified group(s).  This is in the
20781          * format that is stored in the drag and drop instance, so typical
20782          * usage is:
20783          * <code>
20784          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20785          * </code>
20786          * Alternatively:
20787          * <code>
20788          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20789          * </code>
20790          * @TODO this really should be an indexed array.  Alternatively this
20791          * method could accept both.
20792          * @method refreshCache
20793          * @param {Object} groups an associative array of groups to refresh
20794          * @static
20795          */
20796         refreshCache: function(groups) {
20797             for (var sGroup in groups) {
20798                 if ("string" != typeof sGroup) {
20799                     continue;
20800                 }
20801                 for (var i in this.ids[sGroup]) {
20802                     var oDD = this.ids[sGroup][i];
20803
20804                     if (this.isTypeOfDD(oDD)) {
20805                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20806                         var loc = this.getLocation(oDD);
20807                         if (loc) {
20808                             this.locationCache[oDD.id] = loc;
20809                         } else {
20810                             delete this.locationCache[oDD.id];
20811                             // this will unregister the drag and drop object if
20812                             // the element is not in a usable state
20813                             // oDD.unreg();
20814                         }
20815                     }
20816                 }
20817             }
20818         },
20819
20820         /**
20821          * This checks to make sure an element exists and is in the DOM.  The
20822          * main purpose is to handle cases where innerHTML is used to remove
20823          * drag and drop objects from the DOM.  IE provides an 'unspecified
20824          * error' when trying to access the offsetParent of such an element
20825          * @method verifyEl
20826          * @param {HTMLElement} el the element to check
20827          * @return {boolean} true if the element looks usable
20828          * @static
20829          */
20830         verifyEl: function(el) {
20831             if (el) {
20832                 var parent;
20833                 if(Roo.isIE){
20834                     try{
20835                         parent = el.offsetParent;
20836                     }catch(e){}
20837                 }else{
20838                     parent = el.offsetParent;
20839                 }
20840                 if (parent) {
20841                     return true;
20842                 }
20843             }
20844
20845             return false;
20846         },
20847
20848         /**
20849          * Returns a Region object containing the drag and drop element's position
20850          * and size, including the padding configured for it
20851          * @method getLocation
20852          * @param {DragDrop} oDD the drag and drop object to get the
20853          *                       location for
20854          * @return {Roo.lib.Region} a Region object representing the total area
20855          *                             the element occupies, including any padding
20856          *                             the instance is configured for.
20857          * @static
20858          */
20859         getLocation: function(oDD) {
20860             if (! this.isTypeOfDD(oDD)) {
20861                 return null;
20862             }
20863
20864             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20865
20866             try {
20867                 pos= Roo.lib.Dom.getXY(el);
20868             } catch (e) { }
20869
20870             if (!pos) {
20871                 return null;
20872             }
20873
20874             x1 = pos[0];
20875             x2 = x1 + el.offsetWidth;
20876             y1 = pos[1];
20877             y2 = y1 + el.offsetHeight;
20878
20879             t = y1 - oDD.padding[0];
20880             r = x2 + oDD.padding[1];
20881             b = y2 + oDD.padding[2];
20882             l = x1 - oDD.padding[3];
20883
20884             return new Roo.lib.Region( t, r, b, l );
20885         },
20886
20887         /**
20888          * Checks the cursor location to see if it over the target
20889          * @method isOverTarget
20890          * @param {Roo.lib.Point} pt The point to evaluate
20891          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20892          * @return {boolean} true if the mouse is over the target
20893          * @private
20894          * @static
20895          */
20896         isOverTarget: function(pt, oTarget, intersect) {
20897             // use cache if available
20898             var loc = this.locationCache[oTarget.id];
20899             if (!loc || !this.useCache) {
20900                 loc = this.getLocation(oTarget);
20901                 this.locationCache[oTarget.id] = loc;
20902
20903             }
20904
20905             if (!loc) {
20906                 return false;
20907             }
20908
20909             oTarget.cursorIsOver = loc.contains( pt );
20910
20911             // DragDrop is using this as a sanity check for the initial mousedown
20912             // in this case we are done.  In POINT mode, if the drag obj has no
20913             // contraints, we are also done. Otherwise we need to evaluate the
20914             // location of the target as related to the actual location of the
20915             // dragged element.
20916             var dc = this.dragCurrent;
20917             if (!dc || !dc.getTargetCoord ||
20918                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20919                 return oTarget.cursorIsOver;
20920             }
20921
20922             oTarget.overlap = null;
20923
20924             // Get the current location of the drag element, this is the
20925             // location of the mouse event less the delta that represents
20926             // where the original mousedown happened on the element.  We
20927             // need to consider constraints and ticks as well.
20928             var pos = dc.getTargetCoord(pt.x, pt.y);
20929
20930             var el = dc.getDragEl();
20931             var curRegion = new Roo.lib.Region( pos.y,
20932                                                    pos.x + el.offsetWidth,
20933                                                    pos.y + el.offsetHeight,
20934                                                    pos.x );
20935
20936             var overlap = curRegion.intersect(loc);
20937
20938             if (overlap) {
20939                 oTarget.overlap = overlap;
20940                 return (intersect) ? true : oTarget.cursorIsOver;
20941             } else {
20942                 return false;
20943             }
20944         },
20945
20946         /**
20947          * unload event handler
20948          * @method _onUnload
20949          * @private
20950          * @static
20951          */
20952         _onUnload: function(e, me) {
20953             Roo.dd.DragDropMgr.unregAll();
20954         },
20955
20956         /**
20957          * Cleans up the drag and drop events and objects.
20958          * @method unregAll
20959          * @private
20960          * @static
20961          */
20962         unregAll: function() {
20963
20964             if (this.dragCurrent) {
20965                 this.stopDrag();
20966                 this.dragCurrent = null;
20967             }
20968
20969             this._execOnAll("unreg", []);
20970
20971             for (i in this.elementCache) {
20972                 delete this.elementCache[i];
20973             }
20974
20975             this.elementCache = {};
20976             this.ids = {};
20977         },
20978
20979         /**
20980          * A cache of DOM elements
20981          * @property elementCache
20982          * @private
20983          * @static
20984          */
20985         elementCache: {},
20986
20987         /**
20988          * Get the wrapper for the DOM element specified
20989          * @method getElWrapper
20990          * @param {String} id the id of the element to get
20991          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20992          * @private
20993          * @deprecated This wrapper isn't that useful
20994          * @static
20995          */
20996         getElWrapper: function(id) {
20997             var oWrapper = this.elementCache[id];
20998             if (!oWrapper || !oWrapper.el) {
20999                 oWrapper = this.elementCache[id] =
21000                     new this.ElementWrapper(Roo.getDom(id));
21001             }
21002             return oWrapper;
21003         },
21004
21005         /**
21006          * Returns the actual DOM element
21007          * @method getElement
21008          * @param {String} id the id of the elment to get
21009          * @return {Object} The element
21010          * @deprecated use Roo.getDom instead
21011          * @static
21012          */
21013         getElement: function(id) {
21014             return Roo.getDom(id);
21015         },
21016
21017         /**
21018          * Returns the style property for the DOM element (i.e.,
21019          * document.getElById(id).style)
21020          * @method getCss
21021          * @param {String} id the id of the elment to get
21022          * @return {Object} The style property of the element
21023          * @deprecated use Roo.getDom instead
21024          * @static
21025          */
21026         getCss: function(id) {
21027             var el = Roo.getDom(id);
21028             return (el) ? el.style : null;
21029         },
21030
21031         /**
21032          * Inner class for cached elements
21033          * @class DragDropMgr.ElementWrapper
21034          * @for DragDropMgr
21035          * @private
21036          * @deprecated
21037          */
21038         ElementWrapper: function(el) {
21039                 /**
21040                  * The element
21041                  * @property el
21042                  */
21043                 this.el = el || null;
21044                 /**
21045                  * The element id
21046                  * @property id
21047                  */
21048                 this.id = this.el && el.id;
21049                 /**
21050                  * A reference to the style property
21051                  * @property css
21052                  */
21053                 this.css = this.el && el.style;
21054             },
21055
21056         /**
21057          * Returns the X position of an html element
21058          * @method getPosX
21059          * @param el the element for which to get the position
21060          * @return {int} the X coordinate
21061          * @for DragDropMgr
21062          * @deprecated use Roo.lib.Dom.getX instead
21063          * @static
21064          */
21065         getPosX: function(el) {
21066             return Roo.lib.Dom.getX(el);
21067         },
21068
21069         /**
21070          * Returns the Y position of an html element
21071          * @method getPosY
21072          * @param el the element for which to get the position
21073          * @return {int} the Y coordinate
21074          * @deprecated use Roo.lib.Dom.getY instead
21075          * @static
21076          */
21077         getPosY: function(el) {
21078             return Roo.lib.Dom.getY(el);
21079         },
21080
21081         /**
21082          * Swap two nodes.  In IE, we use the native method, for others we
21083          * emulate the IE behavior
21084          * @method swapNode
21085          * @param n1 the first node to swap
21086          * @param n2 the other node to swap
21087          * @static
21088          */
21089         swapNode: function(n1, n2) {
21090             if (n1.swapNode) {
21091                 n1.swapNode(n2);
21092             } else {
21093                 var p = n2.parentNode;
21094                 var s = n2.nextSibling;
21095
21096                 if (s == n1) {
21097                     p.insertBefore(n1, n2);
21098                 } else if (n2 == n1.nextSibling) {
21099                     p.insertBefore(n2, n1);
21100                 } else {
21101                     n1.parentNode.replaceChild(n2, n1);
21102                     p.insertBefore(n1, s);
21103                 }
21104             }
21105         },
21106
21107         /**
21108          * Returns the current scroll position
21109          * @method getScroll
21110          * @private
21111          * @static
21112          */
21113         getScroll: function () {
21114             var t, l, dde=document.documentElement, db=document.body;
21115             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21116                 t = dde.scrollTop;
21117                 l = dde.scrollLeft;
21118             } else if (db) {
21119                 t = db.scrollTop;
21120                 l = db.scrollLeft;
21121             } else {
21122
21123             }
21124             return { top: t, left: l };
21125         },
21126
21127         /**
21128          * Returns the specified element style property
21129          * @method getStyle
21130          * @param {HTMLElement} el          the element
21131          * @param {string}      styleProp   the style property
21132          * @return {string} The value of the style property
21133          * @deprecated use Roo.lib.Dom.getStyle
21134          * @static
21135          */
21136         getStyle: function(el, styleProp) {
21137             return Roo.fly(el).getStyle(styleProp);
21138         },
21139
21140         /**
21141          * Gets the scrollTop
21142          * @method getScrollTop
21143          * @return {int} the document's scrollTop
21144          * @static
21145          */
21146         getScrollTop: function () { return this.getScroll().top; },
21147
21148         /**
21149          * Gets the scrollLeft
21150          * @method getScrollLeft
21151          * @return {int} the document's scrollTop
21152          * @static
21153          */
21154         getScrollLeft: function () { return this.getScroll().left; },
21155
21156         /**
21157          * Sets the x/y position of an element to the location of the
21158          * target element.
21159          * @method moveToEl
21160          * @param {HTMLElement} moveEl      The element to move
21161          * @param {HTMLElement} targetEl    The position reference element
21162          * @static
21163          */
21164         moveToEl: function (moveEl, targetEl) {
21165             var aCoord = Roo.lib.Dom.getXY(targetEl);
21166             Roo.lib.Dom.setXY(moveEl, aCoord);
21167         },
21168
21169         /**
21170          * Numeric array sort function
21171          * @method numericSort
21172          * @static
21173          */
21174         numericSort: function(a, b) { return (a - b); },
21175
21176         /**
21177          * Internal counter
21178          * @property _timeoutCount
21179          * @private
21180          * @static
21181          */
21182         _timeoutCount: 0,
21183
21184         /**
21185          * Trying to make the load order less important.  Without this we get
21186          * an error if this file is loaded before the Event Utility.
21187          * @method _addListeners
21188          * @private
21189          * @static
21190          */
21191         _addListeners: function() {
21192             var DDM = Roo.dd.DDM;
21193             if ( Roo.lib.Event && document ) {
21194                 DDM._onLoad();
21195             } else {
21196                 if (DDM._timeoutCount > 2000) {
21197                 } else {
21198                     setTimeout(DDM._addListeners, 10);
21199                     if (document && document.body) {
21200                         DDM._timeoutCount += 1;
21201                     }
21202                 }
21203             }
21204         },
21205
21206         /**
21207          * Recursively searches the immediate parent and all child nodes for
21208          * the handle element in order to determine wheter or not it was
21209          * clicked.
21210          * @method handleWasClicked
21211          * @param node the html element to inspect
21212          * @static
21213          */
21214         handleWasClicked: function(node, id) {
21215             if (this.isHandle(id, node.id)) {
21216                 return true;
21217             } else {
21218                 // check to see if this is a text node child of the one we want
21219                 var p = node.parentNode;
21220
21221                 while (p) {
21222                     if (this.isHandle(id, p.id)) {
21223                         return true;
21224                     } else {
21225                         p = p.parentNode;
21226                     }
21227                 }
21228             }
21229
21230             return false;
21231         }
21232
21233     };
21234
21235 }();
21236
21237 // shorter alias, save a few bytes
21238 Roo.dd.DDM = Roo.dd.DragDropMgr;
21239 Roo.dd.DDM._addListeners();
21240
21241 }/*
21242  * Based on:
21243  * Ext JS Library 1.1.1
21244  * Copyright(c) 2006-2007, Ext JS, LLC.
21245  *
21246  * Originally Released Under LGPL - original licence link has changed is not relivant.
21247  *
21248  * Fork - LGPL
21249  * <script type="text/javascript">
21250  */
21251
21252 /**
21253  * @class Roo.dd.DD
21254  * A DragDrop implementation where the linked element follows the
21255  * mouse cursor during a drag.
21256  * @extends Roo.dd.DragDrop
21257  * @constructor
21258  * @param {String} id the id of the linked element
21259  * @param {String} sGroup the group of related DragDrop items
21260  * @param {object} config an object containing configurable attributes
21261  *                Valid properties for DD:
21262  *                    scroll
21263  */
21264 Roo.dd.DD = function(id, sGroup, config) {
21265     if (id) {
21266         this.init(id, sGroup, config);
21267     }
21268 };
21269
21270 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21271
21272     /**
21273      * When set to true, the utility automatically tries to scroll the browser
21274      * window wehn a drag and drop element is dragged near the viewport boundary.
21275      * Defaults to true.
21276      * @property scroll
21277      * @type boolean
21278      */
21279     scroll: true,
21280
21281     /**
21282      * Sets the pointer offset to the distance between the linked element's top
21283      * left corner and the location the element was clicked
21284      * @method autoOffset
21285      * @param {int} iPageX the X coordinate of the click
21286      * @param {int} iPageY the Y coordinate of the click
21287      */
21288     autoOffset: function(iPageX, iPageY) {
21289         var x = iPageX - this.startPageX;
21290         var y = iPageY - this.startPageY;
21291         this.setDelta(x, y);
21292     },
21293
21294     /**
21295      * Sets the pointer offset.  You can call this directly to force the
21296      * offset to be in a particular location (e.g., pass in 0,0 to set it
21297      * to the center of the object)
21298      * @method setDelta
21299      * @param {int} iDeltaX the distance from the left
21300      * @param {int} iDeltaY the distance from the top
21301      */
21302     setDelta: function(iDeltaX, iDeltaY) {
21303         this.deltaX = iDeltaX;
21304         this.deltaY = iDeltaY;
21305     },
21306
21307     /**
21308      * Sets the drag element to the location of the mousedown or click event,
21309      * maintaining the cursor location relative to the location on the element
21310      * that was clicked.  Override this if you want to place the element in a
21311      * location other than where the cursor is.
21312      * @method setDragElPos
21313      * @param {int} iPageX the X coordinate of the mousedown or drag event
21314      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21315      */
21316     setDragElPos: function(iPageX, iPageY) {
21317         // the first time we do this, we are going to check to make sure
21318         // the element has css positioning
21319
21320         var el = this.getDragEl();
21321         this.alignElWithMouse(el, iPageX, iPageY);
21322     },
21323
21324     /**
21325      * Sets the element to the location of the mousedown or click event,
21326      * maintaining the cursor location relative to the location on the element
21327      * that was clicked.  Override this if you want to place the element in a
21328      * location other than where the cursor is.
21329      * @method alignElWithMouse
21330      * @param {HTMLElement} el the element to move
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     alignElWithMouse: function(el, iPageX, iPageY) {
21335         var oCoord = this.getTargetCoord(iPageX, iPageY);
21336         var fly = el.dom ? el : Roo.fly(el);
21337         if (!this.deltaSetXY) {
21338             var aCoord = [oCoord.x, oCoord.y];
21339             fly.setXY(aCoord);
21340             var newLeft = fly.getLeft(true);
21341             var newTop  = fly.getTop(true);
21342             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21343         } else {
21344             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21345         }
21346
21347         this.cachePosition(oCoord.x, oCoord.y);
21348         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21349         return oCoord;
21350     },
21351
21352     /**
21353      * Saves the most recent position so that we can reset the constraints and
21354      * tick marks on-demand.  We need to know this so that we can calculate the
21355      * number of pixels the element is offset from its original position.
21356      * @method cachePosition
21357      * @param iPageX the current x position (optional, this just makes it so we
21358      * don't have to look it up again)
21359      * @param iPageY the current y position (optional, this just makes it so we
21360      * don't have to look it up again)
21361      */
21362     cachePosition: function(iPageX, iPageY) {
21363         if (iPageX) {
21364             this.lastPageX = iPageX;
21365             this.lastPageY = iPageY;
21366         } else {
21367             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21368             this.lastPageX = aCoord[0];
21369             this.lastPageY = aCoord[1];
21370         }
21371     },
21372
21373     /**
21374      * Auto-scroll the window if the dragged object has been moved beyond the
21375      * visible window boundary.
21376      * @method autoScroll
21377      * @param {int} x the drag element's x position
21378      * @param {int} y the drag element's y position
21379      * @param {int} h the height of the drag element
21380      * @param {int} w the width of the drag element
21381      * @private
21382      */
21383     autoScroll: function(x, y, h, w) {
21384
21385         if (this.scroll) {
21386             // The client height
21387             var clientH = Roo.lib.Dom.getViewWidth();
21388
21389             // The client width
21390             var clientW = Roo.lib.Dom.getViewHeight();
21391
21392             // The amt scrolled down
21393             var st = this.DDM.getScrollTop();
21394
21395             // The amt scrolled right
21396             var sl = this.DDM.getScrollLeft();
21397
21398             // Location of the bottom of the element
21399             var bot = h + y;
21400
21401             // Location of the right of the element
21402             var right = w + x;
21403
21404             // The distance from the cursor to the bottom of the visible area,
21405             // adjusted so that we don't scroll if the cursor is beyond the
21406             // element drag constraints
21407             var toBot = (clientH + st - y - this.deltaY);
21408
21409             // The distance from the cursor to the right of the visible area
21410             var toRight = (clientW + sl - x - this.deltaX);
21411
21412
21413             // How close to the edge the cursor must be before we scroll
21414             // var thresh = (document.all) ? 100 : 40;
21415             var thresh = 40;
21416
21417             // How many pixels to scroll per autoscroll op.  This helps to reduce
21418             // clunky scrolling. IE is more sensitive about this ... it needs this
21419             // value to be higher.
21420             var scrAmt = (document.all) ? 80 : 30;
21421
21422             // Scroll down if we are near the bottom of the visible page and the
21423             // obj extends below the crease
21424             if ( bot > clientH && toBot < thresh ) {
21425                 window.scrollTo(sl, st + scrAmt);
21426             }
21427
21428             // Scroll up if the window is scrolled down and the top of the object
21429             // goes above the top border
21430             if ( y < st && st > 0 && y - st < thresh ) {
21431                 window.scrollTo(sl, st - scrAmt);
21432             }
21433
21434             // Scroll right if the obj is beyond the right border and the cursor is
21435             // near the border.
21436             if ( right > clientW && toRight < thresh ) {
21437                 window.scrollTo(sl + scrAmt, st);
21438             }
21439
21440             // Scroll left if the window has been scrolled to the right and the obj
21441             // extends past the left border
21442             if ( x < sl && sl > 0 && x - sl < thresh ) {
21443                 window.scrollTo(sl - scrAmt, st);
21444             }
21445         }
21446     },
21447
21448     /**
21449      * Finds the location the element should be placed if we want to move
21450      * it to where the mouse location less the click offset would place us.
21451      * @method getTargetCoord
21452      * @param {int} iPageX the X coordinate of the click
21453      * @param {int} iPageY the Y coordinate of the click
21454      * @return an object that contains the coordinates (Object.x and Object.y)
21455      * @private
21456      */
21457     getTargetCoord: function(iPageX, iPageY) {
21458
21459
21460         var x = iPageX - this.deltaX;
21461         var y = iPageY - this.deltaY;
21462
21463         if (this.constrainX) {
21464             if (x < this.minX) { x = this.minX; }
21465             if (x > this.maxX) { x = this.maxX; }
21466         }
21467
21468         if (this.constrainY) {
21469             if (y < this.minY) { y = this.minY; }
21470             if (y > this.maxY) { y = this.maxY; }
21471         }
21472
21473         x = this.getTick(x, this.xTicks);
21474         y = this.getTick(y, this.yTicks);
21475
21476
21477         return {x:x, y:y};
21478     },
21479
21480     /*
21481      * Sets up config options specific to this class. Overrides
21482      * Roo.dd.DragDrop, but all versions of this method through the
21483      * inheritance chain are called
21484      */
21485     applyConfig: function() {
21486         Roo.dd.DD.superclass.applyConfig.call(this);
21487         this.scroll = (this.config.scroll !== false);
21488     },
21489
21490     /*
21491      * Event that fires prior to the onMouseDown event.  Overrides
21492      * Roo.dd.DragDrop.
21493      */
21494     b4MouseDown: function(e) {
21495         // this.resetConstraints();
21496         this.autoOffset(e.getPageX(),
21497                             e.getPageY());
21498     },
21499
21500     /*
21501      * Event that fires prior to the onDrag event.  Overrides
21502      * Roo.dd.DragDrop.
21503      */
21504     b4Drag: function(e) {
21505         this.setDragElPos(e.getPageX(),
21506                             e.getPageY());
21507     },
21508
21509     toString: function() {
21510         return ("DD " + this.id);
21511     }
21512
21513     //////////////////////////////////////////////////////////////////////////
21514     // Debugging ygDragDrop events that can be overridden
21515     //////////////////////////////////////////////////////////////////////////
21516     /*
21517     startDrag: function(x, y) {
21518     },
21519
21520     onDrag: function(e) {
21521     },
21522
21523     onDragEnter: function(e, id) {
21524     },
21525
21526     onDragOver: function(e, id) {
21527     },
21528
21529     onDragOut: function(e, id) {
21530     },
21531
21532     onDragDrop: function(e, id) {
21533     },
21534
21535     endDrag: function(e) {
21536     }
21537
21538     */
21539
21540 });/*
21541  * Based on:
21542  * Ext JS Library 1.1.1
21543  * Copyright(c) 2006-2007, Ext JS, LLC.
21544  *
21545  * Originally Released Under LGPL - original licence link has changed is not relivant.
21546  *
21547  * Fork - LGPL
21548  * <script type="text/javascript">
21549  */
21550
21551 /**
21552  * @class Roo.dd.DDProxy
21553  * A DragDrop implementation that inserts an empty, bordered div into
21554  * the document that follows the cursor during drag operations.  At the time of
21555  * the click, the frame div is resized to the dimensions of the linked html
21556  * element, and moved to the exact location of the linked element.
21557  *
21558  * References to the "frame" element refer to the single proxy element that
21559  * was created to be dragged in place of all DDProxy elements on the
21560  * page.
21561  *
21562  * @extends Roo.dd.DD
21563  * @constructor
21564  * @param {String} id the id of the linked html element
21565  * @param {String} sGroup the group of related DragDrop objects
21566  * @param {object} config an object containing configurable attributes
21567  *                Valid properties for DDProxy in addition to those in DragDrop:
21568  *                   resizeFrame, centerFrame, dragElId
21569  */
21570 Roo.dd.DDProxy = function(id, sGroup, config) {
21571     if (id) {
21572         this.init(id, sGroup, config);
21573         this.initFrame();
21574     }
21575 };
21576
21577 /**
21578  * The default drag frame div id
21579  * @property Roo.dd.DDProxy.dragElId
21580  * @type String
21581  * @static
21582  */
21583 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21584
21585 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21586
21587     /**
21588      * By default we resize the drag frame to be the same size as the element
21589      * we want to drag (this is to get the frame effect).  We can turn it off
21590      * if we want a different behavior.
21591      * @property resizeFrame
21592      * @type boolean
21593      */
21594     resizeFrame: true,
21595
21596     /**
21597      * By default the frame is positioned exactly where the drag element is, so
21598      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21599      * you do not have constraints on the obj is to have the drag frame centered
21600      * around the cursor.  Set centerFrame to true for this effect.
21601      * @property centerFrame
21602      * @type boolean
21603      */
21604     centerFrame: false,
21605
21606     /**
21607      * Creates the proxy element if it does not yet exist
21608      * @method createFrame
21609      */
21610     createFrame: function() {
21611         var self = this;
21612         var body = document.body;
21613
21614         if (!body || !body.firstChild) {
21615             setTimeout( function() { self.createFrame(); }, 50 );
21616             return;
21617         }
21618
21619         var div = this.getDragEl();
21620
21621         if (!div) {
21622             div    = document.createElement("div");
21623             div.id = this.dragElId;
21624             var s  = div.style;
21625
21626             s.position   = "absolute";
21627             s.visibility = "hidden";
21628             s.cursor     = "move";
21629             s.border     = "2px solid #aaa";
21630             s.zIndex     = 999;
21631
21632             // appendChild can blow up IE if invoked prior to the window load event
21633             // while rendering a table.  It is possible there are other scenarios
21634             // that would cause this to happen as well.
21635             body.insertBefore(div, body.firstChild);
21636         }
21637     },
21638
21639     /**
21640      * Initialization for the drag frame element.  Must be called in the
21641      * constructor of all subclasses
21642      * @method initFrame
21643      */
21644     initFrame: function() {
21645         this.createFrame();
21646     },
21647
21648     applyConfig: function() {
21649         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21650
21651         this.resizeFrame = (this.config.resizeFrame !== false);
21652         this.centerFrame = (this.config.centerFrame);
21653         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21654     },
21655
21656     /**
21657      * Resizes the drag frame to the dimensions of the clicked object, positions
21658      * it over the object, and finally displays it
21659      * @method showFrame
21660      * @param {int} iPageX X click position
21661      * @param {int} iPageY Y click position
21662      * @private
21663      */
21664     showFrame: function(iPageX, iPageY) {
21665         var el = this.getEl();
21666         var dragEl = this.getDragEl();
21667         var s = dragEl.style;
21668
21669         this._resizeProxy();
21670
21671         if (this.centerFrame) {
21672             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21673                            Math.round(parseInt(s.height, 10)/2) );
21674         }
21675
21676         this.setDragElPos(iPageX, iPageY);
21677
21678         Roo.fly(dragEl).show();
21679     },
21680
21681     /**
21682      * The proxy is automatically resized to the dimensions of the linked
21683      * element when a drag is initiated, unless resizeFrame is set to false
21684      * @method _resizeProxy
21685      * @private
21686      */
21687     _resizeProxy: function() {
21688         if (this.resizeFrame) {
21689             var el = this.getEl();
21690             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21691         }
21692     },
21693
21694     // overrides Roo.dd.DragDrop
21695     b4MouseDown: function(e) {
21696         var x = e.getPageX();
21697         var y = e.getPageY();
21698         this.autoOffset(x, y);
21699         this.setDragElPos(x, y);
21700     },
21701
21702     // overrides Roo.dd.DragDrop
21703     b4StartDrag: function(x, y) {
21704         // show the drag frame
21705         this.showFrame(x, y);
21706     },
21707
21708     // overrides Roo.dd.DragDrop
21709     b4EndDrag: function(e) {
21710         Roo.fly(this.getDragEl()).hide();
21711     },
21712
21713     // overrides Roo.dd.DragDrop
21714     // By default we try to move the element to the last location of the frame.
21715     // This is so that the default behavior mirrors that of Roo.dd.DD.
21716     endDrag: function(e) {
21717
21718         var lel = this.getEl();
21719         var del = this.getDragEl();
21720
21721         // Show the drag frame briefly so we can get its position
21722         del.style.visibility = "";
21723
21724         this.beforeMove();
21725         // Hide the linked element before the move to get around a Safari
21726         // rendering bug.
21727         lel.style.visibility = "hidden";
21728         Roo.dd.DDM.moveToEl(lel, del);
21729         del.style.visibility = "hidden";
21730         lel.style.visibility = "";
21731
21732         this.afterDrag();
21733     },
21734
21735     beforeMove : function(){
21736
21737     },
21738
21739     afterDrag : function(){
21740
21741     },
21742
21743     toString: function() {
21744         return ("DDProxy " + this.id);
21745     }
21746
21747 });
21748 /*
21749  * Based on:
21750  * Ext JS Library 1.1.1
21751  * Copyright(c) 2006-2007, Ext JS, LLC.
21752  *
21753  * Originally Released Under LGPL - original licence link has changed is not relivant.
21754  *
21755  * Fork - LGPL
21756  * <script type="text/javascript">
21757  */
21758
21759  /**
21760  * @class Roo.dd.DDTarget
21761  * A DragDrop implementation that does not move, but can be a drop
21762  * target.  You would get the same result by simply omitting implementation
21763  * for the event callbacks, but this way we reduce the processing cost of the
21764  * event listener and the callbacks.
21765  * @extends Roo.dd.DragDrop
21766  * @constructor
21767  * @param {String} id the id of the element that is a drop target
21768  * @param {String} sGroup the group of related DragDrop objects
21769  * @param {object} config an object containing configurable attributes
21770  *                 Valid properties for DDTarget in addition to those in
21771  *                 DragDrop:
21772  *                    none
21773  */
21774 Roo.dd.DDTarget = function(id, sGroup, config) {
21775     if (id) {
21776         this.initTarget(id, sGroup, config);
21777     }
21778     if (config && (config.listeners || config.events)) { 
21779         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21780             listeners : config.listeners || {}, 
21781             events : config.events || {} 
21782         });    
21783     }
21784 };
21785
21786 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21787 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21788     toString: function() {
21789         return ("DDTarget " + this.id);
21790     }
21791 });
21792 /*
21793  * Based on:
21794  * Ext JS Library 1.1.1
21795  * Copyright(c) 2006-2007, Ext JS, LLC.
21796  *
21797  * Originally Released Under LGPL - original licence link has changed is not relivant.
21798  *
21799  * Fork - LGPL
21800  * <script type="text/javascript">
21801  */
21802  
21803
21804 /**
21805  * @class Roo.dd.ScrollManager
21806  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21807  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21808  * @singleton
21809  */
21810 Roo.dd.ScrollManager = function(){
21811     var ddm = Roo.dd.DragDropMgr;
21812     var els = {};
21813     var dragEl = null;
21814     var proc = {};
21815     
21816     
21817     
21818     var onStop = function(e){
21819         dragEl = null;
21820         clearProc();
21821     };
21822     
21823     var triggerRefresh = function(){
21824         if(ddm.dragCurrent){
21825              ddm.refreshCache(ddm.dragCurrent.groups);
21826         }
21827     };
21828     
21829     var doScroll = function(){
21830         if(ddm.dragCurrent){
21831             var dds = Roo.dd.ScrollManager;
21832             if(!dds.animate){
21833                 if(proc.el.scroll(proc.dir, dds.increment)){
21834                     triggerRefresh();
21835                 }
21836             }else{
21837                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21838             }
21839         }
21840     };
21841     
21842     var clearProc = function(){
21843         if(proc.id){
21844             clearInterval(proc.id);
21845         }
21846         proc.id = 0;
21847         proc.el = null;
21848         proc.dir = "";
21849     };
21850     
21851     var startProc = function(el, dir){
21852          Roo.log('scroll startproc');
21853         clearProc();
21854         proc.el = el;
21855         proc.dir = dir;
21856         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21857     };
21858     
21859     var onFire = function(e, isDrop){
21860        
21861         if(isDrop || !ddm.dragCurrent){ return; }
21862         var dds = Roo.dd.ScrollManager;
21863         if(!dragEl || dragEl != ddm.dragCurrent){
21864             dragEl = ddm.dragCurrent;
21865             // refresh regions on drag start
21866             dds.refreshCache();
21867         }
21868         
21869         var xy = Roo.lib.Event.getXY(e);
21870         var pt = new Roo.lib.Point(xy[0], xy[1]);
21871         for(var id in els){
21872             var el = els[id], r = el._region;
21873             if(r && r.contains(pt) && el.isScrollable()){
21874                 if(r.bottom - pt.y <= dds.thresh){
21875                     if(proc.el != el){
21876                         startProc(el, "down");
21877                     }
21878                     return;
21879                 }else if(r.right - pt.x <= dds.thresh){
21880                     if(proc.el != el){
21881                         startProc(el, "left");
21882                     }
21883                     return;
21884                 }else if(pt.y - r.top <= dds.thresh){
21885                     if(proc.el != el){
21886                         startProc(el, "up");
21887                     }
21888                     return;
21889                 }else if(pt.x - r.left <= dds.thresh){
21890                     if(proc.el != el){
21891                         startProc(el, "right");
21892                     }
21893                     return;
21894                 }
21895             }
21896         }
21897         clearProc();
21898     };
21899     
21900     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21901     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21902     
21903     return {
21904         /**
21905          * Registers new overflow element(s) to auto scroll
21906          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21907          */
21908         register : function(el){
21909             if(el instanceof Array){
21910                 for(var i = 0, len = el.length; i < len; i++) {
21911                         this.register(el[i]);
21912                 }
21913             }else{
21914                 el = Roo.get(el);
21915                 els[el.id] = el;
21916             }
21917             Roo.dd.ScrollManager.els = els;
21918         },
21919         
21920         /**
21921          * Unregisters overflow element(s) so they are no longer scrolled
21922          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21923          */
21924         unregister : function(el){
21925             if(el instanceof Array){
21926                 for(var i = 0, len = el.length; i < len; i++) {
21927                         this.unregister(el[i]);
21928                 }
21929             }else{
21930                 el = Roo.get(el);
21931                 delete els[el.id];
21932             }
21933         },
21934         
21935         /**
21936          * The number of pixels from the edge of a container the pointer needs to be to 
21937          * trigger scrolling (defaults to 25)
21938          * @type Number
21939          */
21940         thresh : 25,
21941         
21942         /**
21943          * The number of pixels to scroll in each scroll increment (defaults to 50)
21944          * @type Number
21945          */
21946         increment : 100,
21947         
21948         /**
21949          * The frequency of scrolls in milliseconds (defaults to 500)
21950          * @type Number
21951          */
21952         frequency : 500,
21953         
21954         /**
21955          * True to animate the scroll (defaults to true)
21956          * @type Boolean
21957          */
21958         animate: true,
21959         
21960         /**
21961          * The animation duration in seconds - 
21962          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21963          * @type Number
21964          */
21965         animDuration: .4,
21966         
21967         /**
21968          * Manually trigger a cache refresh.
21969          */
21970         refreshCache : function(){
21971             for(var id in els){
21972                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21973                     els[id]._region = els[id].getRegion();
21974                 }
21975             }
21976         }
21977     };
21978 }();/*
21979  * Based on:
21980  * Ext JS Library 1.1.1
21981  * Copyright(c) 2006-2007, Ext JS, LLC.
21982  *
21983  * Originally Released Under LGPL - original licence link has changed is not relivant.
21984  *
21985  * Fork - LGPL
21986  * <script type="text/javascript">
21987  */
21988  
21989
21990 /**
21991  * @class Roo.dd.Registry
21992  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21993  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21994  * @singleton
21995  */
21996 Roo.dd.Registry = function(){
21997     var elements = {}; 
21998     var handles = {}; 
21999     var autoIdSeed = 0;
22000
22001     var getId = function(el, autogen){
22002         if(typeof el == "string"){
22003             return el;
22004         }
22005         var id = el.id;
22006         if(!id && autogen !== false){
22007             id = "roodd-" + (++autoIdSeed);
22008             el.id = id;
22009         }
22010         return id;
22011     };
22012     
22013     return {
22014     /**
22015      * Register a drag drop element
22016      * @param {String|HTMLElement} element The id or DOM node to register
22017      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22018      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22019      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22020      * populated in the data object (if applicable):
22021      * <pre>
22022 Value      Description<br />
22023 ---------  ------------------------------------------<br />
22024 handles    Array of DOM nodes that trigger dragging<br />
22025            for the element being registered<br />
22026 isHandle   True if the element passed in triggers<br />
22027            dragging itself, else false
22028 </pre>
22029      */
22030         register : function(el, data){
22031             data = data || {};
22032             if(typeof el == "string"){
22033                 el = document.getElementById(el);
22034             }
22035             data.ddel = el;
22036             elements[getId(el)] = data;
22037             if(data.isHandle !== false){
22038                 handles[data.ddel.id] = data;
22039             }
22040             if(data.handles){
22041                 var hs = data.handles;
22042                 for(var i = 0, len = hs.length; i < len; i++){
22043                         handles[getId(hs[i])] = data;
22044                 }
22045             }
22046         },
22047
22048     /**
22049      * Unregister a drag drop element
22050      * @param {String|HTMLElement}  element The id or DOM node to unregister
22051      */
22052         unregister : function(el){
22053             var id = getId(el, false);
22054             var data = elements[id];
22055             if(data){
22056                 delete elements[id];
22057                 if(data.handles){
22058                     var hs = data.handles;
22059                     for(var i = 0, len = hs.length; i < len; i++){
22060                         delete handles[getId(hs[i], false)];
22061                     }
22062                 }
22063             }
22064         },
22065
22066     /**
22067      * Returns the handle registered for a DOM Node by id
22068      * @param {String|HTMLElement} id The DOM node or id to look up
22069      * @return {Object} handle The custom handle data
22070      */
22071         getHandle : function(id){
22072             if(typeof id != "string"){ // must be element?
22073                 id = id.id;
22074             }
22075             return handles[id];
22076         },
22077
22078     /**
22079      * Returns the handle that is registered for the DOM node that is the target of the event
22080      * @param {Event} e The event
22081      * @return {Object} handle The custom handle data
22082      */
22083         getHandleFromEvent : function(e){
22084             var t = Roo.lib.Event.getTarget(e);
22085             return t ? handles[t.id] : null;
22086         },
22087
22088     /**
22089      * Returns a custom data object that is registered for a DOM node by id
22090      * @param {String|HTMLElement} id The DOM node or id to look up
22091      * @return {Object} data The custom data
22092      */
22093         getTarget : function(id){
22094             if(typeof id != "string"){ // must be element?
22095                 id = id.id;
22096             }
22097             return elements[id];
22098         },
22099
22100     /**
22101      * Returns a custom data object that is registered for the DOM node that is the target of the event
22102      * @param {Event} e The event
22103      * @return {Object} data The custom data
22104      */
22105         getTargetFromEvent : function(e){
22106             var t = Roo.lib.Event.getTarget(e);
22107             return t ? elements[t.id] || handles[t.id] : null;
22108         }
22109     };
22110 }();/*
22111  * Based on:
22112  * Ext JS Library 1.1.1
22113  * Copyright(c) 2006-2007, Ext JS, LLC.
22114  *
22115  * Originally Released Under LGPL - original licence link has changed is not relivant.
22116  *
22117  * Fork - LGPL
22118  * <script type="text/javascript">
22119  */
22120  
22121
22122 /**
22123  * @class Roo.dd.StatusProxy
22124  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22125  * default drag proxy used by all Roo.dd components.
22126  * @constructor
22127  * @param {Object} config
22128  */
22129 Roo.dd.StatusProxy = function(config){
22130     Roo.apply(this, config);
22131     this.id = this.id || Roo.id();
22132     this.el = new Roo.Layer({
22133         dh: {
22134             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22135                 {tag: "div", cls: "x-dd-drop-icon"},
22136                 {tag: "div", cls: "x-dd-drag-ghost"}
22137             ]
22138         }, 
22139         shadow: !config || config.shadow !== false
22140     });
22141     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22142     this.dropStatus = this.dropNotAllowed;
22143 };
22144
22145 Roo.dd.StatusProxy.prototype = {
22146     /**
22147      * @cfg {String} dropAllowed
22148      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22149      */
22150     dropAllowed : "x-dd-drop-ok",
22151     /**
22152      * @cfg {String} dropNotAllowed
22153      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22154      */
22155     dropNotAllowed : "x-dd-drop-nodrop",
22156
22157     /**
22158      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22159      * over the current target element.
22160      * @param {String} cssClass The css class for the new drop status indicator image
22161      */
22162     setStatus : function(cssClass){
22163         cssClass = cssClass || this.dropNotAllowed;
22164         if(this.dropStatus != cssClass){
22165             this.el.replaceClass(this.dropStatus, cssClass);
22166             this.dropStatus = cssClass;
22167         }
22168     },
22169
22170     /**
22171      * Resets the status indicator to the default dropNotAllowed value
22172      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22173      */
22174     reset : function(clearGhost){
22175         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22176         this.dropStatus = this.dropNotAllowed;
22177         if(clearGhost){
22178             this.ghost.update("");
22179         }
22180     },
22181
22182     /**
22183      * Updates the contents of the ghost element
22184      * @param {String} html The html that will replace the current innerHTML of the ghost element
22185      */
22186     update : function(html){
22187         if(typeof html == "string"){
22188             this.ghost.update(html);
22189         }else{
22190             this.ghost.update("");
22191             html.style.margin = "0";
22192             this.ghost.dom.appendChild(html);
22193         }
22194         // ensure float = none set?? cant remember why though.
22195         var el = this.ghost.dom.firstChild;
22196                 if(el){
22197                         Roo.fly(el).setStyle('float', 'none');
22198                 }
22199     },
22200     
22201     /**
22202      * Returns the underlying proxy {@link Roo.Layer}
22203      * @return {Roo.Layer} el
22204     */
22205     getEl : function(){
22206         return this.el;
22207     },
22208
22209     /**
22210      * Returns the ghost element
22211      * @return {Roo.Element} el
22212      */
22213     getGhost : function(){
22214         return this.ghost;
22215     },
22216
22217     /**
22218      * Hides the proxy
22219      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22220      */
22221     hide : function(clear){
22222         this.el.hide();
22223         if(clear){
22224             this.reset(true);
22225         }
22226     },
22227
22228     /**
22229      * Stops the repair animation if it's currently running
22230      */
22231     stop : function(){
22232         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22233             this.anim.stop();
22234         }
22235     },
22236
22237     /**
22238      * Displays this proxy
22239      */
22240     show : function(){
22241         this.el.show();
22242     },
22243
22244     /**
22245      * Force the Layer to sync its shadow and shim positions to the element
22246      */
22247     sync : function(){
22248         this.el.sync();
22249     },
22250
22251     /**
22252      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22253      * invalid drop operation by the item being dragged.
22254      * @param {Array} xy The XY position of the element ([x, y])
22255      * @param {Function} callback The function to call after the repair is complete
22256      * @param {Object} scope The scope in which to execute the callback
22257      */
22258     repair : function(xy, callback, scope){
22259         this.callback = callback;
22260         this.scope = scope;
22261         if(xy && this.animRepair !== false){
22262             this.el.addClass("x-dd-drag-repair");
22263             this.el.hideUnders(true);
22264             this.anim = this.el.shift({
22265                 duration: this.repairDuration || .5,
22266                 easing: 'easeOut',
22267                 xy: xy,
22268                 stopFx: true,
22269                 callback: this.afterRepair,
22270                 scope: this
22271             });
22272         }else{
22273             this.afterRepair();
22274         }
22275     },
22276
22277     // private
22278     afterRepair : function(){
22279         this.hide(true);
22280         if(typeof this.callback == "function"){
22281             this.callback.call(this.scope || this);
22282         }
22283         this.callback = null;
22284         this.scope = null;
22285     }
22286 };/*
22287  * Based on:
22288  * Ext JS Library 1.1.1
22289  * Copyright(c) 2006-2007, Ext JS, LLC.
22290  *
22291  * Originally Released Under LGPL - original licence link has changed is not relivant.
22292  *
22293  * Fork - LGPL
22294  * <script type="text/javascript">
22295  */
22296
22297 /**
22298  * @class Roo.dd.DragSource
22299  * @extends Roo.dd.DDProxy
22300  * A simple class that provides the basic implementation needed to make any element draggable.
22301  * @constructor
22302  * @param {String/HTMLElement/Element} el The container element
22303  * @param {Object} config
22304  */
22305 Roo.dd.DragSource = function(el, config){
22306     this.el = Roo.get(el);
22307     this.dragData = {};
22308     
22309     Roo.apply(this, config);
22310     
22311     if(!this.proxy){
22312         this.proxy = new Roo.dd.StatusProxy();
22313     }
22314
22315     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22316           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22317     
22318     this.dragging = false;
22319 };
22320
22321 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22322     /**
22323      * @cfg {String} dropAllowed
22324      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22325      */
22326     dropAllowed : "x-dd-drop-ok",
22327     /**
22328      * @cfg {String} dropNotAllowed
22329      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22330      */
22331     dropNotAllowed : "x-dd-drop-nodrop",
22332
22333     /**
22334      * Returns the data object associated with this drag source
22335      * @return {Object} data An object containing arbitrary data
22336      */
22337     getDragData : function(e){
22338         return this.dragData;
22339     },
22340
22341     // private
22342     onDragEnter : function(e, id){
22343         var target = Roo.dd.DragDropMgr.getDDById(id);
22344         this.cachedTarget = target;
22345         if(this.beforeDragEnter(target, e, id) !== false){
22346             if(target.isNotifyTarget){
22347                 var status = target.notifyEnter(this, e, this.dragData);
22348                 this.proxy.setStatus(status);
22349             }else{
22350                 this.proxy.setStatus(this.dropAllowed);
22351             }
22352             
22353             if(this.afterDragEnter){
22354                 /**
22355                  * An empty function by default, but provided so that you can perform a custom action
22356                  * when the dragged item enters the drop target by providing an implementation.
22357                  * @param {Roo.dd.DragDrop} target The drop target
22358                  * @param {Event} e The event object
22359                  * @param {String} id The id of the dragged element
22360                  * @method afterDragEnter
22361                  */
22362                 this.afterDragEnter(target, e, id);
22363             }
22364         }
22365     },
22366
22367     /**
22368      * An empty function by default, but provided so that you can perform a custom action
22369      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22370      * @param {Roo.dd.DragDrop} target The drop target
22371      * @param {Event} e The event object
22372      * @param {String} id The id of the dragged element
22373      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22374      */
22375     beforeDragEnter : function(target, e, id){
22376         return true;
22377     },
22378
22379     // private
22380     alignElWithMouse: function() {
22381         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22382         this.proxy.sync();
22383     },
22384
22385     // private
22386     onDragOver : function(e, id){
22387         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22388         if(this.beforeDragOver(target, e, id) !== false){
22389             if(target.isNotifyTarget){
22390                 var status = target.notifyOver(this, e, this.dragData);
22391                 this.proxy.setStatus(status);
22392             }
22393
22394             if(this.afterDragOver){
22395                 /**
22396                  * An empty function by default, but provided so that you can perform a custom action
22397                  * while the dragged item is over the drop target by providing an implementation.
22398                  * @param {Roo.dd.DragDrop} target The drop target
22399                  * @param {Event} e The event object
22400                  * @param {String} id The id of the dragged element
22401                  * @method afterDragOver
22402                  */
22403                 this.afterDragOver(target, e, id);
22404             }
22405         }
22406     },
22407
22408     /**
22409      * An empty function by default, but provided so that you can perform a custom action
22410      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22411      * @param {Roo.dd.DragDrop} target The drop target
22412      * @param {Event} e The event object
22413      * @param {String} id The id of the dragged element
22414      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22415      */
22416     beforeDragOver : function(target, e, id){
22417         return true;
22418     },
22419
22420     // private
22421     onDragOut : function(e, id){
22422         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22423         if(this.beforeDragOut(target, e, id) !== false){
22424             if(target.isNotifyTarget){
22425                 target.notifyOut(this, e, this.dragData);
22426             }
22427             this.proxy.reset();
22428             if(this.afterDragOut){
22429                 /**
22430                  * An empty function by default, but provided so that you can perform a custom action
22431                  * after the dragged item is dragged out of the target without dropping.
22432                  * @param {Roo.dd.DragDrop} target The drop target
22433                  * @param {Event} e The event object
22434                  * @param {String} id The id of the dragged element
22435                  * @method afterDragOut
22436                  */
22437                 this.afterDragOut(target, e, id);
22438             }
22439         }
22440         this.cachedTarget = null;
22441     },
22442
22443     /**
22444      * An empty function by default, but provided so that you can perform a custom action before the dragged
22445      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22446      * @param {Roo.dd.DragDrop} target The drop target
22447      * @param {Event} e The event object
22448      * @param {String} id The id of the dragged element
22449      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22450      */
22451     beforeDragOut : function(target, e, id){
22452         return true;
22453     },
22454     
22455     // private
22456     onDragDrop : function(e, id){
22457         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22458         if(this.beforeDragDrop(target, e, id) !== false){
22459             if(target.isNotifyTarget){
22460                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22461                     this.onValidDrop(target, e, id);
22462                 }else{
22463                     this.onInvalidDrop(target, e, id);
22464                 }
22465             }else{
22466                 this.onValidDrop(target, e, id);
22467             }
22468             
22469             if(this.afterDragDrop){
22470                 /**
22471                  * An empty function by default, but provided so that you can perform a custom action
22472                  * after a valid drag drop has occurred by providing an implementation.
22473                  * @param {Roo.dd.DragDrop} target The drop target
22474                  * @param {Event} e The event object
22475                  * @param {String} id The id of the dropped element
22476                  * @method afterDragDrop
22477                  */
22478                 this.afterDragDrop(target, e, id);
22479             }
22480         }
22481         delete this.cachedTarget;
22482     },
22483
22484     /**
22485      * An empty function by default, but provided so that you can perform a custom action before the dragged
22486      * item is dropped onto the target and optionally cancel the onDragDrop.
22487      * @param {Roo.dd.DragDrop} target The drop target
22488      * @param {Event} e The event object
22489      * @param {String} id The id of the dragged element
22490      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22491      */
22492     beforeDragDrop : function(target, e, id){
22493         return true;
22494     },
22495
22496     // private
22497     onValidDrop : function(target, e, id){
22498         this.hideProxy();
22499         if(this.afterValidDrop){
22500             /**
22501              * An empty function by default, but provided so that you can perform a custom action
22502              * after a valid drop has occurred by providing an implementation.
22503              * @param {Object} target The target DD 
22504              * @param {Event} e The event object
22505              * @param {String} id The id of the dropped element
22506              * @method afterInvalidDrop
22507              */
22508             this.afterValidDrop(target, e, id);
22509         }
22510     },
22511
22512     // private
22513     getRepairXY : function(e, data){
22514         return this.el.getXY();  
22515     },
22516
22517     // private
22518     onInvalidDrop : function(target, e, id){
22519         this.beforeInvalidDrop(target, e, id);
22520         if(this.cachedTarget){
22521             if(this.cachedTarget.isNotifyTarget){
22522                 this.cachedTarget.notifyOut(this, e, this.dragData);
22523             }
22524             this.cacheTarget = null;
22525         }
22526         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22527
22528         if(this.afterInvalidDrop){
22529             /**
22530              * An empty function by default, but provided so that you can perform a custom action
22531              * after an invalid drop has occurred by providing an implementation.
22532              * @param {Event} e The event object
22533              * @param {String} id The id of the dropped element
22534              * @method afterInvalidDrop
22535              */
22536             this.afterInvalidDrop(e, id);
22537         }
22538     },
22539
22540     // private
22541     afterRepair : function(){
22542         if(Roo.enableFx){
22543             this.el.highlight(this.hlColor || "c3daf9");
22544         }
22545         this.dragging = false;
22546     },
22547
22548     /**
22549      * An empty function by default, but provided so that you can perform a custom action after an invalid
22550      * drop has occurred.
22551      * @param {Roo.dd.DragDrop} target The drop target
22552      * @param {Event} e The event object
22553      * @param {String} id The id of the dragged element
22554      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22555      */
22556     beforeInvalidDrop : function(target, e, id){
22557         return true;
22558     },
22559
22560     // private
22561     handleMouseDown : function(e){
22562         if(this.dragging) {
22563             return;
22564         }
22565         var data = this.getDragData(e);
22566         if(data && this.onBeforeDrag(data, e) !== false){
22567             this.dragData = data;
22568             this.proxy.stop();
22569             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22570         } 
22571     },
22572
22573     /**
22574      * An empty function by default, but provided so that you can perform a custom action before the initial
22575      * drag event begins and optionally cancel it.
22576      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22577      * @param {Event} e The event object
22578      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22579      */
22580     onBeforeDrag : function(data, e){
22581         return true;
22582     },
22583
22584     /**
22585      * An empty function by default, but provided so that you can perform a custom action once the initial
22586      * drag event has begun.  The drag cannot be canceled from this function.
22587      * @param {Number} x The x position of the click on the dragged object
22588      * @param {Number} y The y position of the click on the dragged object
22589      */
22590     onStartDrag : Roo.emptyFn,
22591
22592     // private - YUI override
22593     startDrag : function(x, y){
22594         this.proxy.reset();
22595         this.dragging = true;
22596         this.proxy.update("");
22597         this.onInitDrag(x, y);
22598         this.proxy.show();
22599     },
22600
22601     // private
22602     onInitDrag : function(x, y){
22603         var clone = this.el.dom.cloneNode(true);
22604         clone.id = Roo.id(); // prevent duplicate ids
22605         this.proxy.update(clone);
22606         this.onStartDrag(x, y);
22607         return true;
22608     },
22609
22610     /**
22611      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22612      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22613      */
22614     getProxy : function(){
22615         return this.proxy;  
22616     },
22617
22618     /**
22619      * Hides the drag source's {@link Roo.dd.StatusProxy}
22620      */
22621     hideProxy : function(){
22622         this.proxy.hide();  
22623         this.proxy.reset(true);
22624         this.dragging = false;
22625     },
22626
22627     // private
22628     triggerCacheRefresh : function(){
22629         Roo.dd.DDM.refreshCache(this.groups);
22630     },
22631
22632     // private - override to prevent hiding
22633     b4EndDrag: function(e) {
22634     },
22635
22636     // private - override to prevent moving
22637     endDrag : function(e){
22638         this.onEndDrag(this.dragData, e);
22639     },
22640
22641     // private
22642     onEndDrag : function(data, e){
22643     },
22644     
22645     // private - pin to cursor
22646     autoOffset : function(x, y) {
22647         this.setDelta(-12, -20);
22648     }    
22649 });/*
22650  * Based on:
22651  * Ext JS Library 1.1.1
22652  * Copyright(c) 2006-2007, Ext JS, LLC.
22653  *
22654  * Originally Released Under LGPL - original licence link has changed is not relivant.
22655  *
22656  * Fork - LGPL
22657  * <script type="text/javascript">
22658  */
22659
22660
22661 /**
22662  * @class Roo.dd.DropTarget
22663  * @extends Roo.dd.DDTarget
22664  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22665  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22666  * @constructor
22667  * @param {String/HTMLElement/Element} el The container element
22668  * @param {Object} config
22669  */
22670 Roo.dd.DropTarget = function(el, config){
22671     this.el = Roo.get(el);
22672     
22673     var listeners = false; ;
22674     if (config && config.listeners) {
22675         listeners= config.listeners;
22676         delete config.listeners;
22677     }
22678     Roo.apply(this, config);
22679     
22680     if(this.containerScroll){
22681         Roo.dd.ScrollManager.register(this.el);
22682     }
22683     this.addEvents( {
22684          /**
22685          * @scope Roo.dd.DropTarget
22686          */
22687          
22688          /**
22689          * @event enter
22690          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22691          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22692          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22693          * 
22694          * IMPORTANT : it should set  this.valid to true|false
22695          * 
22696          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22697          * @param {Event} e The event
22698          * @param {Object} data An object containing arbitrary data supplied by the drag source
22699          */
22700         "enter" : true,
22701         
22702          /**
22703          * @event over
22704          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22705          * This method will be called on every mouse movement while the drag source is over the drop target.
22706          * This default implementation simply returns the dropAllowed config value.
22707          * 
22708          * IMPORTANT : it should set  this.valid to true|false
22709          * 
22710          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22711          * @param {Event} e The event
22712          * @param {Object} data An object containing arbitrary data supplied by the drag source
22713          
22714          */
22715         "over" : true,
22716         /**
22717          * @event out
22718          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22719          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22720          * overClass (if any) from the drop element.
22721          * 
22722          * 
22723          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22724          * @param {Event} e The event
22725          * @param {Object} data An object containing arbitrary data supplied by the drag source
22726          */
22727          "out" : true,
22728          
22729         /**
22730          * @event drop
22731          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22732          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22733          * implementation that does something to process the drop event and returns true so that the drag source's
22734          * repair action does not run.
22735          * 
22736          * IMPORTANT : it should set this.success
22737          * 
22738          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22739          * @param {Event} e The event
22740          * @param {Object} data An object containing arbitrary data supplied by the drag source
22741         */
22742          "drop" : true
22743     });
22744             
22745      
22746     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22747         this.el.dom, 
22748         this.ddGroup || this.group,
22749         {
22750             isTarget: true,
22751             listeners : listeners || {} 
22752            
22753         
22754         }
22755     );
22756
22757 };
22758
22759 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22760     /**
22761      * @cfg {String} overClass
22762      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22763      */
22764      /**
22765      * @cfg {String} ddGroup
22766      * The drag drop group to handle drop events for
22767      */
22768      
22769     /**
22770      * @cfg {String} dropAllowed
22771      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22772      */
22773     dropAllowed : "x-dd-drop-ok",
22774     /**
22775      * @cfg {String} dropNotAllowed
22776      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22777      */
22778     dropNotAllowed : "x-dd-drop-nodrop",
22779     /**
22780      * @cfg {boolean} success
22781      * set this after drop listener.. 
22782      */
22783     success : false,
22784     /**
22785      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22786      * if the drop point is valid for over/enter..
22787      */
22788     valid : false,
22789     // private
22790     isTarget : true,
22791
22792     // private
22793     isNotifyTarget : true,
22794     
22795     /**
22796      * @hide
22797      */
22798     notifyEnter : function(dd, e, data)
22799     {
22800         this.valid = true;
22801         this.fireEvent('enter', dd, e, data);
22802         if(this.overClass){
22803             this.el.addClass(this.overClass);
22804         }
22805         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22806             this.valid ? this.dropAllowed : this.dropNotAllowed
22807         );
22808     },
22809
22810     /**
22811      * @hide
22812      */
22813     notifyOver : function(dd, e, data)
22814     {
22815         this.valid = true;
22816         this.fireEvent('over', dd, e, data);
22817         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22818             this.valid ? this.dropAllowed : this.dropNotAllowed
22819         );
22820     },
22821
22822     /**
22823      * @hide
22824      */
22825     notifyOut : function(dd, e, data)
22826     {
22827         this.fireEvent('out', dd, e, data);
22828         if(this.overClass){
22829             this.el.removeClass(this.overClass);
22830         }
22831     },
22832
22833     /**
22834      * @hide
22835      */
22836     notifyDrop : function(dd, e, data)
22837     {
22838         this.success = false;
22839         this.fireEvent('drop', dd, e, data);
22840         return this.success;
22841     }
22842 });/*
22843  * Based on:
22844  * Ext JS Library 1.1.1
22845  * Copyright(c) 2006-2007, Ext JS, LLC.
22846  *
22847  * Originally Released Under LGPL - original licence link has changed is not relivant.
22848  *
22849  * Fork - LGPL
22850  * <script type="text/javascript">
22851  */
22852
22853
22854 /**
22855  * @class Roo.dd.DragZone
22856  * @extends Roo.dd.DragSource
22857  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22858  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22859  * @constructor
22860  * @param {String/HTMLElement/Element} el The container element
22861  * @param {Object} config
22862  */
22863 Roo.dd.DragZone = function(el, config){
22864     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22865     if(this.containerScroll){
22866         Roo.dd.ScrollManager.register(this.el);
22867     }
22868 };
22869
22870 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22871     /**
22872      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22873      * for auto scrolling during drag operations.
22874      */
22875     /**
22876      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22877      * method after a failed drop (defaults to "c3daf9" - light blue)
22878      */
22879
22880     /**
22881      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22882      * for a valid target to drag based on the mouse down. Override this method
22883      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22884      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22885      * @param {EventObject} e The mouse down event
22886      * @return {Object} The dragData
22887      */
22888     getDragData : function(e){
22889         return Roo.dd.Registry.getHandleFromEvent(e);
22890     },
22891     
22892     /**
22893      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22894      * this.dragData.ddel
22895      * @param {Number} x The x position of the click on the dragged object
22896      * @param {Number} y The y position of the click on the dragged object
22897      * @return {Boolean} true to continue the drag, false to cancel
22898      */
22899     onInitDrag : function(x, y){
22900         this.proxy.update(this.dragData.ddel.cloneNode(true));
22901         this.onStartDrag(x, y);
22902         return true;
22903     },
22904     
22905     /**
22906      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22907      */
22908     afterRepair : function(){
22909         if(Roo.enableFx){
22910             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22911         }
22912         this.dragging = false;
22913     },
22914
22915     /**
22916      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22917      * the XY of this.dragData.ddel
22918      * @param {EventObject} e The mouse up event
22919      * @return {Array} The xy location (e.g. [100, 200])
22920      */
22921     getRepairXY : function(e){
22922         return Roo.Element.fly(this.dragData.ddel).getXY();  
22923     }
22924 });/*
22925  * Based on:
22926  * Ext JS Library 1.1.1
22927  * Copyright(c) 2006-2007, Ext JS, LLC.
22928  *
22929  * Originally Released Under LGPL - original licence link has changed is not relivant.
22930  *
22931  * Fork - LGPL
22932  * <script type="text/javascript">
22933  */
22934 /**
22935  * @class Roo.dd.DropZone
22936  * @extends Roo.dd.DropTarget
22937  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22938  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22939  * @constructor
22940  * @param {String/HTMLElement/Element} el The container element
22941  * @param {Object} config
22942  */
22943 Roo.dd.DropZone = function(el, config){
22944     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22945 };
22946
22947 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22948     /**
22949      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22950      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22951      * provide your own custom lookup.
22952      * @param {Event} e The event
22953      * @return {Object} data The custom data
22954      */
22955     getTargetFromEvent : function(e){
22956         return Roo.dd.Registry.getTargetFromEvent(e);
22957     },
22958
22959     /**
22960      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22961      * that it has registered.  This method has no default implementation and should be overridden to provide
22962      * node-specific processing if necessary.
22963      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22964      * {@link #getTargetFromEvent} for this node)
22965      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22966      * @param {Event} e The event
22967      * @param {Object} data An object containing arbitrary data supplied by the drag source
22968      */
22969     onNodeEnter : function(n, dd, e, data){
22970         
22971     },
22972
22973     /**
22974      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22975      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22976      * overridden to provide the proper feedback.
22977      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22978      * {@link #getTargetFromEvent} for this node)
22979      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22980      * @param {Event} e The event
22981      * @param {Object} data An object containing arbitrary data supplied by the drag source
22982      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22983      * underlying {@link Roo.dd.StatusProxy} can be updated
22984      */
22985     onNodeOver : function(n, dd, e, data){
22986         return this.dropAllowed;
22987     },
22988
22989     /**
22990      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22991      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22992      * node-specific processing if necessary.
22993      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22994      * {@link #getTargetFromEvent} for this node)
22995      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22996      * @param {Event} e The event
22997      * @param {Object} data An object containing arbitrary data supplied by the drag source
22998      */
22999     onNodeOut : function(n, dd, e, data){
23000         
23001     },
23002
23003     /**
23004      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23005      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23006      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23007      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23008      * {@link #getTargetFromEvent} for this node)
23009      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23010      * @param {Event} e The event
23011      * @param {Object} data An object containing arbitrary data supplied by the drag source
23012      * @return {Boolean} True if the drop was valid, else false
23013      */
23014     onNodeDrop : function(n, dd, e, data){
23015         return false;
23016     },
23017
23018     /**
23019      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23020      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23021      * it should be overridden to provide the proper feedback if necessary.
23022      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23023      * @param {Event} e The event
23024      * @param {Object} data An object containing arbitrary data supplied by the drag source
23025      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23026      * underlying {@link Roo.dd.StatusProxy} can be updated
23027      */
23028     onContainerOver : function(dd, e, data){
23029         return this.dropNotAllowed;
23030     },
23031
23032     /**
23033      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23034      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23035      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23036      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23037      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23038      * @param {Event} e The event
23039      * @param {Object} data An object containing arbitrary data supplied by the drag source
23040      * @return {Boolean} True if the drop was valid, else false
23041      */
23042     onContainerDrop : function(dd, e, data){
23043         return false;
23044     },
23045
23046     /**
23047      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23048      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23049      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23050      * you should override this method and provide a custom implementation.
23051      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23052      * @param {Event} e The event
23053      * @param {Object} data An object containing arbitrary data supplied by the drag source
23054      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23055      * underlying {@link Roo.dd.StatusProxy} can be updated
23056      */
23057     notifyEnter : function(dd, e, data){
23058         return this.dropNotAllowed;
23059     },
23060
23061     /**
23062      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23063      * This method will be called on every mouse movement while the drag source is over the drop zone.
23064      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23065      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23066      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23067      * registered node, it will call {@link #onContainerOver}.
23068      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23069      * @param {Event} e The event
23070      * @param {Object} data An object containing arbitrary data supplied by the drag source
23071      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23072      * underlying {@link Roo.dd.StatusProxy} can be updated
23073      */
23074     notifyOver : function(dd, e, data){
23075         var n = this.getTargetFromEvent(e);
23076         if(!n){ // not over valid drop target
23077             if(this.lastOverNode){
23078                 this.onNodeOut(this.lastOverNode, dd, e, data);
23079                 this.lastOverNode = null;
23080             }
23081             return this.onContainerOver(dd, e, data);
23082         }
23083         if(this.lastOverNode != n){
23084             if(this.lastOverNode){
23085                 this.onNodeOut(this.lastOverNode, dd, e, data);
23086             }
23087             this.onNodeEnter(n, dd, e, data);
23088             this.lastOverNode = n;
23089         }
23090         return this.onNodeOver(n, dd, e, data);
23091     },
23092
23093     /**
23094      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23095      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23096      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23097      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23098      * @param {Event} e The event
23099      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23100      */
23101     notifyOut : function(dd, e, data){
23102         if(this.lastOverNode){
23103             this.onNodeOut(this.lastOverNode, dd, e, data);
23104             this.lastOverNode = null;
23105         }
23106     },
23107
23108     /**
23109      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23110      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23111      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23112      * otherwise it will call {@link #onContainerDrop}.
23113      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23114      * @param {Event} e The event
23115      * @param {Object} data An object containing arbitrary data supplied by the drag source
23116      * @return {Boolean} True if the drop was valid, else false
23117      */
23118     notifyDrop : function(dd, e, data){
23119         if(this.lastOverNode){
23120             this.onNodeOut(this.lastOverNode, dd, e, data);
23121             this.lastOverNode = null;
23122         }
23123         var n = this.getTargetFromEvent(e);
23124         return n ?
23125             this.onNodeDrop(n, dd, e, data) :
23126             this.onContainerDrop(dd, e, data);
23127     },
23128
23129     // private
23130     triggerCacheRefresh : function(){
23131         Roo.dd.DDM.refreshCache(this.groups);
23132     }  
23133 });/*
23134  * Based on:
23135  * Ext JS Library 1.1.1
23136  * Copyright(c) 2006-2007, Ext JS, LLC.
23137  *
23138  * Originally Released Under LGPL - original licence link has changed is not relivant.
23139  *
23140  * Fork - LGPL
23141  * <script type="text/javascript">
23142  */
23143
23144
23145 /**
23146  * @class Roo.data.SortTypes
23147  * @singleton
23148  * Defines the default sorting (casting?) comparison functions used when sorting data.
23149  */
23150 Roo.data.SortTypes = {
23151     /**
23152      * Default sort that does nothing
23153      * @param {Mixed} s The value being converted
23154      * @return {Mixed} The comparison value
23155      */
23156     none : function(s){
23157         return s;
23158     },
23159     
23160     /**
23161      * The regular expression used to strip tags
23162      * @type {RegExp}
23163      * @property
23164      */
23165     stripTagsRE : /<\/?[^>]+>/gi,
23166     
23167     /**
23168      * Strips all HTML tags to sort on text only
23169      * @param {Mixed} s The value being converted
23170      * @return {String} The comparison value
23171      */
23172     asText : function(s){
23173         return String(s).replace(this.stripTagsRE, "");
23174     },
23175     
23176     /**
23177      * Strips all HTML tags to sort on text only - Case insensitive
23178      * @param {Mixed} s The value being converted
23179      * @return {String} The comparison value
23180      */
23181     asUCText : function(s){
23182         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23183     },
23184     
23185     /**
23186      * Case insensitive string
23187      * @param {Mixed} s The value being converted
23188      * @return {String} The comparison value
23189      */
23190     asUCString : function(s) {
23191         return String(s).toUpperCase();
23192     },
23193     
23194     /**
23195      * Date sorting
23196      * @param {Mixed} s The value being converted
23197      * @return {Number} The comparison value
23198      */
23199     asDate : function(s) {
23200         if(!s){
23201             return 0;
23202         }
23203         if(s instanceof Date){
23204             return s.getTime();
23205         }
23206         return Date.parse(String(s));
23207     },
23208     
23209     /**
23210      * Float sorting
23211      * @param {Mixed} s The value being converted
23212      * @return {Float} The comparison value
23213      */
23214     asFloat : function(s) {
23215         var val = parseFloat(String(s).replace(/,/g, ""));
23216         if(isNaN(val)) {
23217             val = 0;
23218         }
23219         return val;
23220     },
23221     
23222     /**
23223      * Integer sorting
23224      * @param {Mixed} s The value being converted
23225      * @return {Number} The comparison value
23226      */
23227     asInt : function(s) {
23228         var val = parseInt(String(s).replace(/,/g, ""));
23229         if(isNaN(val)) {
23230             val = 0;
23231         }
23232         return val;
23233     }
23234 };/*
23235  * Based on:
23236  * Ext JS Library 1.1.1
23237  * Copyright(c) 2006-2007, Ext JS, LLC.
23238  *
23239  * Originally Released Under LGPL - original licence link has changed is not relivant.
23240  *
23241  * Fork - LGPL
23242  * <script type="text/javascript">
23243  */
23244
23245 /**
23246 * @class Roo.data.Record
23247  * Instances of this class encapsulate both record <em>definition</em> information, and record
23248  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23249  * to access Records cached in an {@link Roo.data.Store} object.<br>
23250  * <p>
23251  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23252  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23253  * objects.<br>
23254  * <p>
23255  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23256  * @constructor
23257  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23258  * {@link #create}. The parameters are the same.
23259  * @param {Array} data An associative Array of data values keyed by the field name.
23260  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23261  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23262  * not specified an integer id is generated.
23263  */
23264 Roo.data.Record = function(data, id){
23265     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23266     this.data = data;
23267 };
23268
23269 /**
23270  * Generate a constructor for a specific record layout.
23271  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23272  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23273  * Each field definition object may contain the following properties: <ul>
23274  * <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,
23275  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23276  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23277  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23278  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23279  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23280  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23281  * this may be omitted.</p></li>
23282  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23283  * <ul><li>auto (Default, implies no conversion)</li>
23284  * <li>string</li>
23285  * <li>int</li>
23286  * <li>float</li>
23287  * <li>boolean</li>
23288  * <li>date</li></ul></p></li>
23289  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23290  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23291  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23292  * by the Reader into an object that will be stored in the Record. It is passed the
23293  * following parameters:<ul>
23294  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23295  * </ul></p></li>
23296  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23297  * </ul>
23298  * <br>usage:<br><pre><code>
23299 var TopicRecord = Roo.data.Record.create(
23300     {name: 'title', mapping: 'topic_title'},
23301     {name: 'author', mapping: 'username'},
23302     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23303     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23304     {name: 'lastPoster', mapping: 'user2'},
23305     {name: 'excerpt', mapping: 'post_text'}
23306 );
23307
23308 var myNewRecord = new TopicRecord({
23309     title: 'Do my job please',
23310     author: 'noobie',
23311     totalPosts: 1,
23312     lastPost: new Date(),
23313     lastPoster: 'Animal',
23314     excerpt: 'No way dude!'
23315 });
23316 myStore.add(myNewRecord);
23317 </code></pre>
23318  * @method create
23319  * @static
23320  */
23321 Roo.data.Record.create = function(o){
23322     var f = function(){
23323         f.superclass.constructor.apply(this, arguments);
23324     };
23325     Roo.extend(f, Roo.data.Record);
23326     var p = f.prototype;
23327     p.fields = new Roo.util.MixedCollection(false, function(field){
23328         return field.name;
23329     });
23330     for(var i = 0, len = o.length; i < len; i++){
23331         p.fields.add(new Roo.data.Field(o[i]));
23332     }
23333     f.getField = function(name){
23334         return p.fields.get(name);  
23335     };
23336     return f;
23337 };
23338
23339 Roo.data.Record.AUTO_ID = 1000;
23340 Roo.data.Record.EDIT = 'edit';
23341 Roo.data.Record.REJECT = 'reject';
23342 Roo.data.Record.COMMIT = 'commit';
23343
23344 Roo.data.Record.prototype = {
23345     /**
23346      * Readonly flag - true if this record has been modified.
23347      * @type Boolean
23348      */
23349     dirty : false,
23350     editing : false,
23351     error: null,
23352     modified: null,
23353
23354     // private
23355     join : function(store){
23356         this.store = store;
23357     },
23358
23359     /**
23360      * Set the named field to the specified value.
23361      * @param {String} name The name of the field to set.
23362      * @param {Object} value The value to set the field to.
23363      */
23364     set : function(name, value){
23365         if(this.data[name] == value){
23366             return;
23367         }
23368         this.dirty = true;
23369         if(!this.modified){
23370             this.modified = {};
23371         }
23372         if(typeof this.modified[name] == 'undefined'){
23373             this.modified[name] = this.data[name];
23374         }
23375         this.data[name] = value;
23376         if(!this.editing && this.store){
23377             this.store.afterEdit(this);
23378         }       
23379     },
23380
23381     /**
23382      * Get the value of the named field.
23383      * @param {String} name The name of the field to get the value of.
23384      * @return {Object} The value of the field.
23385      */
23386     get : function(name){
23387         return this.data[name]; 
23388     },
23389
23390     // private
23391     beginEdit : function(){
23392         this.editing = true;
23393         this.modified = {}; 
23394     },
23395
23396     // private
23397     cancelEdit : function(){
23398         this.editing = false;
23399         delete this.modified;
23400     },
23401
23402     // private
23403     endEdit : function(){
23404         this.editing = false;
23405         if(this.dirty && this.store){
23406             this.store.afterEdit(this);
23407         }
23408     },
23409
23410     /**
23411      * Usually called by the {@link Roo.data.Store} which owns the Record.
23412      * Rejects all changes made to the Record since either creation, or the last commit operation.
23413      * Modified fields are reverted to their original values.
23414      * <p>
23415      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23416      * of reject operations.
23417      */
23418     reject : function(){
23419         var m = this.modified;
23420         for(var n in m){
23421             if(typeof m[n] != "function"){
23422                 this.data[n] = m[n];
23423             }
23424         }
23425         this.dirty = false;
23426         delete this.modified;
23427         this.editing = false;
23428         if(this.store){
23429             this.store.afterReject(this);
23430         }
23431     },
23432
23433     /**
23434      * Usually called by the {@link Roo.data.Store} which owns the Record.
23435      * Commits all changes made to the Record since either creation, or the last commit operation.
23436      * <p>
23437      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23438      * of commit operations.
23439      */
23440     commit : function(){
23441         this.dirty = false;
23442         delete this.modified;
23443         this.editing = false;
23444         if(this.store){
23445             this.store.afterCommit(this);
23446         }
23447     },
23448
23449     // private
23450     hasError : function(){
23451         return this.error != null;
23452     },
23453
23454     // private
23455     clearError : function(){
23456         this.error = null;
23457     },
23458
23459     /**
23460      * Creates a copy of this record.
23461      * @param {String} id (optional) A new record id if you don't want to use this record's id
23462      * @return {Record}
23463      */
23464     copy : function(newId) {
23465         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23466     }
23467 };/*
23468  * Based on:
23469  * Ext JS Library 1.1.1
23470  * Copyright(c) 2006-2007, Ext JS, LLC.
23471  *
23472  * Originally Released Under LGPL - original licence link has changed is not relivant.
23473  *
23474  * Fork - LGPL
23475  * <script type="text/javascript">
23476  */
23477
23478
23479
23480 /**
23481  * @class Roo.data.Store
23482  * @extends Roo.util.Observable
23483  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23484  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23485  * <p>
23486  * 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
23487  * has no knowledge of the format of the data returned by the Proxy.<br>
23488  * <p>
23489  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23490  * instances from the data object. These records are cached and made available through accessor functions.
23491  * @constructor
23492  * Creates a new Store.
23493  * @param {Object} config A config object containing the objects needed for the Store to access data,
23494  * and read the data into Records.
23495  */
23496 Roo.data.Store = function(config){
23497     this.data = new Roo.util.MixedCollection(false);
23498     this.data.getKey = function(o){
23499         return o.id;
23500     };
23501     this.baseParams = {};
23502     // private
23503     this.paramNames = {
23504         "start" : "start",
23505         "limit" : "limit",
23506         "sort" : "sort",
23507         "dir" : "dir",
23508         "multisort" : "_multisort"
23509     };
23510
23511     if(config && config.data){
23512         this.inlineData = config.data;
23513         delete config.data;
23514     }
23515
23516     Roo.apply(this, config);
23517     
23518     if(this.reader){ // reader passed
23519         this.reader = Roo.factory(this.reader, Roo.data);
23520         this.reader.xmodule = this.xmodule || false;
23521         if(!this.recordType){
23522             this.recordType = this.reader.recordType;
23523         }
23524         if(this.reader.onMetaChange){
23525             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23526         }
23527     }
23528
23529     if(this.recordType){
23530         this.fields = this.recordType.prototype.fields;
23531     }
23532     this.modified = [];
23533
23534     this.addEvents({
23535         /**
23536          * @event datachanged
23537          * Fires when the data cache has changed, and a widget which is using this Store
23538          * as a Record cache should refresh its view.
23539          * @param {Store} this
23540          */
23541         datachanged : true,
23542         /**
23543          * @event metachange
23544          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23545          * @param {Store} this
23546          * @param {Object} meta The JSON metadata
23547          */
23548         metachange : true,
23549         /**
23550          * @event add
23551          * Fires when Records have been added to the Store
23552          * @param {Store} this
23553          * @param {Roo.data.Record[]} records The array of Records added
23554          * @param {Number} index The index at which the record(s) were added
23555          */
23556         add : true,
23557         /**
23558          * @event remove
23559          * Fires when a Record has been removed from the Store
23560          * @param {Store} this
23561          * @param {Roo.data.Record} record The Record that was removed
23562          * @param {Number} index The index at which the record was removed
23563          */
23564         remove : true,
23565         /**
23566          * @event update
23567          * Fires when a Record has been updated
23568          * @param {Store} this
23569          * @param {Roo.data.Record} record The Record that was updated
23570          * @param {String} operation The update operation being performed.  Value may be one of:
23571          * <pre><code>
23572  Roo.data.Record.EDIT
23573  Roo.data.Record.REJECT
23574  Roo.data.Record.COMMIT
23575          * </code></pre>
23576          */
23577         update : true,
23578         /**
23579          * @event clear
23580          * Fires when the data cache has been cleared.
23581          * @param {Store} this
23582          */
23583         clear : true,
23584         /**
23585          * @event beforeload
23586          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23587          * the load action will be canceled.
23588          * @param {Store} this
23589          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23590          */
23591         beforeload : true,
23592         /**
23593          * @event beforeloadadd
23594          * Fires after a new set of Records has been loaded.
23595          * @param {Store} this
23596          * @param {Roo.data.Record[]} records The Records that were loaded
23597          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23598          */
23599         beforeloadadd : true,
23600         /**
23601          * @event load
23602          * Fires after a new set of Records has been loaded, before they are added to the store.
23603          * @param {Store} this
23604          * @param {Roo.data.Record[]} records The Records that were loaded
23605          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23606          * @params {Object} return from reader
23607          */
23608         load : true,
23609         /**
23610          * @event loadexception
23611          * Fires if an exception occurs in the Proxy during loading.
23612          * Called with the signature of the Proxy's "loadexception" event.
23613          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23614          * 
23615          * @param {Proxy} 
23616          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23617          * @param {Object} load options 
23618          * @param {Object} jsonData from your request (normally this contains the Exception)
23619          */
23620         loadexception : true
23621     });
23622     
23623     if(this.proxy){
23624         this.proxy = Roo.factory(this.proxy, Roo.data);
23625         this.proxy.xmodule = this.xmodule || false;
23626         this.relayEvents(this.proxy,  ["loadexception"]);
23627     }
23628     this.sortToggle = {};
23629     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23630
23631     Roo.data.Store.superclass.constructor.call(this);
23632
23633     if(this.inlineData){
23634         this.loadData(this.inlineData);
23635         delete this.inlineData;
23636     }
23637 };
23638
23639 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23640      /**
23641     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23642     * without a remote query - used by combo/forms at present.
23643     */
23644     
23645     /**
23646     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23647     */
23648     /**
23649     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23650     */
23651     /**
23652     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23653     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23654     */
23655     /**
23656     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23657     * on any HTTP request
23658     */
23659     /**
23660     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23661     */
23662     /**
23663     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23664     */
23665     multiSort: false,
23666     /**
23667     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23668     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23669     */
23670     remoteSort : false,
23671
23672     /**
23673     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23674      * loaded or when a record is removed. (defaults to false).
23675     */
23676     pruneModifiedRecords : false,
23677
23678     // private
23679     lastOptions : null,
23680
23681     /**
23682      * Add Records to the Store and fires the add event.
23683      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23684      */
23685     add : function(records){
23686         records = [].concat(records);
23687         for(var i = 0, len = records.length; i < len; i++){
23688             records[i].join(this);
23689         }
23690         var index = this.data.length;
23691         this.data.addAll(records);
23692         this.fireEvent("add", this, records, index);
23693     },
23694
23695     /**
23696      * Remove a Record from the Store and fires the remove event.
23697      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23698      */
23699     remove : function(record){
23700         var index = this.data.indexOf(record);
23701         this.data.removeAt(index);
23702  
23703         if(this.pruneModifiedRecords){
23704             this.modified.remove(record);
23705         }
23706         this.fireEvent("remove", this, record, index);
23707     },
23708
23709     /**
23710      * Remove all Records from the Store and fires the clear event.
23711      */
23712     removeAll : function(){
23713         this.data.clear();
23714         if(this.pruneModifiedRecords){
23715             this.modified = [];
23716         }
23717         this.fireEvent("clear", this);
23718     },
23719
23720     /**
23721      * Inserts Records to the Store at the given index and fires the add event.
23722      * @param {Number} index The start index at which to insert the passed Records.
23723      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23724      */
23725     insert : function(index, records){
23726         records = [].concat(records);
23727         for(var i = 0, len = records.length; i < len; i++){
23728             this.data.insert(index, records[i]);
23729             records[i].join(this);
23730         }
23731         this.fireEvent("add", this, records, index);
23732     },
23733
23734     /**
23735      * Get the index within the cache of the passed Record.
23736      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23737      * @return {Number} The index of the passed Record. Returns -1 if not found.
23738      */
23739     indexOf : function(record){
23740         return this.data.indexOf(record);
23741     },
23742
23743     /**
23744      * Get the index within the cache of the Record with the passed id.
23745      * @param {String} id The id of the Record to find.
23746      * @return {Number} The index of the Record. Returns -1 if not found.
23747      */
23748     indexOfId : function(id){
23749         return this.data.indexOfKey(id);
23750     },
23751
23752     /**
23753      * Get the Record with the specified id.
23754      * @param {String} id The id of the Record to find.
23755      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23756      */
23757     getById : function(id){
23758         return this.data.key(id);
23759     },
23760
23761     /**
23762      * Get the Record at the specified index.
23763      * @param {Number} index The index of the Record to find.
23764      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23765      */
23766     getAt : function(index){
23767         return this.data.itemAt(index);
23768     },
23769
23770     /**
23771      * Returns a range of Records between specified indices.
23772      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23773      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23774      * @return {Roo.data.Record[]} An array of Records
23775      */
23776     getRange : function(start, end){
23777         return this.data.getRange(start, end);
23778     },
23779
23780     // private
23781     storeOptions : function(o){
23782         o = Roo.apply({}, o);
23783         delete o.callback;
23784         delete o.scope;
23785         this.lastOptions = o;
23786     },
23787
23788     /**
23789      * Loads the Record cache from the configured Proxy using the configured Reader.
23790      * <p>
23791      * If using remote paging, then the first load call must specify the <em>start</em>
23792      * and <em>limit</em> properties in the options.params property to establish the initial
23793      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23794      * <p>
23795      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23796      * and this call will return before the new data has been loaded. Perform any post-processing
23797      * in a callback function, or in a "load" event handler.</strong>
23798      * <p>
23799      * @param {Object} options An object containing properties which control loading options:<ul>
23800      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23801      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23802      * passed the following arguments:<ul>
23803      * <li>r : Roo.data.Record[]</li>
23804      * <li>options: Options object from the load call</li>
23805      * <li>success: Boolean success indicator</li></ul></li>
23806      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23807      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23808      * </ul>
23809      */
23810     load : function(options){
23811         options = options || {};
23812         if(this.fireEvent("beforeload", this, options) !== false){
23813             this.storeOptions(options);
23814             var p = Roo.apply(options.params || {}, this.baseParams);
23815             // if meta was not loaded from remote source.. try requesting it.
23816             if (!this.reader.metaFromRemote) {
23817                 p._requestMeta = 1;
23818             }
23819             if(this.sortInfo && this.remoteSort){
23820                 var pn = this.paramNames;
23821                 p[pn["sort"]] = this.sortInfo.field;
23822                 p[pn["dir"]] = this.sortInfo.direction;
23823             }
23824             if (this.multiSort) {
23825                 var pn = this.paramNames;
23826                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23827             }
23828             
23829             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23830         }
23831     },
23832
23833     /**
23834      * Reloads the Record cache from the configured Proxy using the configured Reader and
23835      * the options from the last load operation performed.
23836      * @param {Object} options (optional) An object containing properties which may override the options
23837      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23838      * the most recently used options are reused).
23839      */
23840     reload : function(options){
23841         this.load(Roo.applyIf(options||{}, this.lastOptions));
23842     },
23843
23844     // private
23845     // Called as a callback by the Reader during a load operation.
23846     loadRecords : function(o, options, success){
23847         if(!o || success === false){
23848             if(success !== false){
23849                 this.fireEvent("load", this, [], options, o);
23850             }
23851             if(options.callback){
23852                 options.callback.call(options.scope || this, [], options, false);
23853             }
23854             return;
23855         }
23856         // if data returned failure - throw an exception.
23857         if (o.success === false) {
23858             // show a message if no listener is registered.
23859             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23860                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23861             }
23862             // loadmask wil be hooked into this..
23863             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23864             return;
23865         }
23866         var r = o.records, t = o.totalRecords || r.length;
23867         
23868         this.fireEvent("beforeloadadd", this, r, options, o);
23869         
23870         if(!options || options.add !== true){
23871             if(this.pruneModifiedRecords){
23872                 this.modified = [];
23873             }
23874             for(var i = 0, len = r.length; i < len; i++){
23875                 r[i].join(this);
23876             }
23877             if(this.snapshot){
23878                 this.data = this.snapshot;
23879                 delete this.snapshot;
23880             }
23881             this.data.clear();
23882             this.data.addAll(r);
23883             this.totalLength = t;
23884             this.applySort();
23885             this.fireEvent("datachanged", this);
23886         }else{
23887             this.totalLength = Math.max(t, this.data.length+r.length);
23888             this.add(r);
23889         }
23890         
23891         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23892                 
23893             var e = new Roo.data.Record({});
23894
23895             e.set(this.parent.displayField, this.parent.emptyTitle);
23896             e.set(this.parent.valueField, '');
23897
23898             this.insert(0, e);
23899         }
23900             
23901         this.fireEvent("load", this, r, options, o);
23902         if(options.callback){
23903             options.callback.call(options.scope || this, r, options, true);
23904         }
23905     },
23906
23907
23908     /**
23909      * Loads data from a passed data block. A Reader which understands the format of the data
23910      * must have been configured in the constructor.
23911      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23912      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23913      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23914      */
23915     loadData : function(o, append){
23916         var r = this.reader.readRecords(o);
23917         this.loadRecords(r, {add: append}, true);
23918     },
23919     
23920      /**
23921      * using 'cn' the nested child reader read the child array into it's child stores.
23922      * @param {Object} rec The record with a 'children array
23923      */
23924     loadDataFromChildren : function(rec)
23925     {
23926         this.loadData(this.reader.toLoadData(rec));
23927     },
23928     
23929
23930     /**
23931      * Gets the number of cached records.
23932      * <p>
23933      * <em>If using paging, this may not be the total size of the dataset. If the data object
23934      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23935      * the data set size</em>
23936      */
23937     getCount : function(){
23938         return this.data.length || 0;
23939     },
23940
23941     /**
23942      * Gets the total number of records in the dataset as returned by the server.
23943      * <p>
23944      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23945      * the dataset size</em>
23946      */
23947     getTotalCount : function(){
23948         return this.totalLength || 0;
23949     },
23950
23951     /**
23952      * Returns the sort state of the Store as an object with two properties:
23953      * <pre><code>
23954  field {String} The name of the field by which the Records are sorted
23955  direction {String} The sort order, "ASC" or "DESC"
23956      * </code></pre>
23957      */
23958     getSortState : function(){
23959         return this.sortInfo;
23960     },
23961
23962     // private
23963     applySort : function(){
23964         if(this.sortInfo && !this.remoteSort){
23965             var s = this.sortInfo, f = s.field;
23966             var st = this.fields.get(f).sortType;
23967             var fn = function(r1, r2){
23968                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23969                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23970             };
23971             this.data.sort(s.direction, fn);
23972             if(this.snapshot && this.snapshot != this.data){
23973                 this.snapshot.sort(s.direction, fn);
23974             }
23975         }
23976     },
23977
23978     /**
23979      * Sets the default sort column and order to be used by the next load operation.
23980      * @param {String} fieldName The name of the field to sort by.
23981      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23982      */
23983     setDefaultSort : function(field, dir){
23984         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23985     },
23986
23987     /**
23988      * Sort the Records.
23989      * If remote sorting is used, the sort is performed on the server, and the cache is
23990      * reloaded. If local sorting is used, the cache is sorted internally.
23991      * @param {String} fieldName The name of the field to sort by.
23992      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23993      */
23994     sort : function(fieldName, dir){
23995         var f = this.fields.get(fieldName);
23996         if(!dir){
23997             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23998             
23999             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24000                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24001             }else{
24002                 dir = f.sortDir;
24003             }
24004         }
24005         this.sortToggle[f.name] = dir;
24006         this.sortInfo = {field: f.name, direction: dir};
24007         if(!this.remoteSort){
24008             this.applySort();
24009             this.fireEvent("datachanged", this);
24010         }else{
24011             this.load(this.lastOptions);
24012         }
24013     },
24014
24015     /**
24016      * Calls the specified function for each of the Records in the cache.
24017      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24018      * Returning <em>false</em> aborts and exits the iteration.
24019      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24020      */
24021     each : function(fn, scope){
24022         this.data.each(fn, scope);
24023     },
24024
24025     /**
24026      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24027      * (e.g., during paging).
24028      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24029      */
24030     getModifiedRecords : function(){
24031         return this.modified;
24032     },
24033
24034     // private
24035     createFilterFn : function(property, value, anyMatch){
24036         if(!value.exec){ // not a regex
24037             value = String(value);
24038             if(value.length == 0){
24039                 return false;
24040             }
24041             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24042         }
24043         return function(r){
24044             return value.test(r.data[property]);
24045         };
24046     },
24047
24048     /**
24049      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24050      * @param {String} property A field on your records
24051      * @param {Number} start The record index to start at (defaults to 0)
24052      * @param {Number} end The last record index to include (defaults to length - 1)
24053      * @return {Number} The sum
24054      */
24055     sum : function(property, start, end){
24056         var rs = this.data.items, v = 0;
24057         start = start || 0;
24058         end = (end || end === 0) ? end : rs.length-1;
24059
24060         for(var i = start; i <= end; i++){
24061             v += (rs[i].data[property] || 0);
24062         }
24063         return v;
24064     },
24065
24066     /**
24067      * Filter the records by a specified property.
24068      * @param {String} field A field on your records
24069      * @param {String/RegExp} value Either a string that the field
24070      * should start with or a RegExp to test against the field
24071      * @param {Boolean} anyMatch True to match any part not just the beginning
24072      */
24073     filter : function(property, value, anyMatch){
24074         var fn = this.createFilterFn(property, value, anyMatch);
24075         return fn ? this.filterBy(fn) : this.clearFilter();
24076     },
24077
24078     /**
24079      * Filter by a function. The specified function will be called with each
24080      * record in this data source. If the function returns true the record is included,
24081      * otherwise it is filtered.
24082      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24083      * @param {Object} scope (optional) The scope of the function (defaults to this)
24084      */
24085     filterBy : function(fn, scope){
24086         this.snapshot = this.snapshot || this.data;
24087         this.data = this.queryBy(fn, scope||this);
24088         this.fireEvent("datachanged", this);
24089     },
24090
24091     /**
24092      * Query the records by a specified property.
24093      * @param {String} field A field on your records
24094      * @param {String/RegExp} value Either a string that the field
24095      * should start with or a RegExp to test against the field
24096      * @param {Boolean} anyMatch True to match any part not just the beginning
24097      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24098      */
24099     query : function(property, value, anyMatch){
24100         var fn = this.createFilterFn(property, value, anyMatch);
24101         return fn ? this.queryBy(fn) : this.data.clone();
24102     },
24103
24104     /**
24105      * Query by a function. The specified function will be called with each
24106      * record in this data source. If the function returns true the record is included
24107      * in the results.
24108      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24109      * @param {Object} scope (optional) The scope of the function (defaults to this)
24110       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24111      **/
24112     queryBy : function(fn, scope){
24113         var data = this.snapshot || this.data;
24114         return data.filterBy(fn, scope||this);
24115     },
24116
24117     /**
24118      * Collects unique values for a particular dataIndex from this store.
24119      * @param {String} dataIndex The property to collect
24120      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24121      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24122      * @return {Array} An array of the unique values
24123      **/
24124     collect : function(dataIndex, allowNull, bypassFilter){
24125         var d = (bypassFilter === true && this.snapshot) ?
24126                 this.snapshot.items : this.data.items;
24127         var v, sv, r = [], l = {};
24128         for(var i = 0, len = d.length; i < len; i++){
24129             v = d[i].data[dataIndex];
24130             sv = String(v);
24131             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24132                 l[sv] = true;
24133                 r[r.length] = v;
24134             }
24135         }
24136         return r;
24137     },
24138
24139     /**
24140      * Revert to a view of the Record cache with no filtering applied.
24141      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24142      */
24143     clearFilter : function(suppressEvent){
24144         if(this.snapshot && this.snapshot != this.data){
24145             this.data = this.snapshot;
24146             delete this.snapshot;
24147             if(suppressEvent !== true){
24148                 this.fireEvent("datachanged", this);
24149             }
24150         }
24151     },
24152
24153     // private
24154     afterEdit : function(record){
24155         if(this.modified.indexOf(record) == -1){
24156             this.modified.push(record);
24157         }
24158         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24159     },
24160     
24161     // private
24162     afterReject : function(record){
24163         this.modified.remove(record);
24164         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24165     },
24166
24167     // private
24168     afterCommit : function(record){
24169         this.modified.remove(record);
24170         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24171     },
24172
24173     /**
24174      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24175      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24176      */
24177     commitChanges : function(){
24178         var m = this.modified.slice(0);
24179         this.modified = [];
24180         for(var i = 0, len = m.length; i < len; i++){
24181             m[i].commit();
24182         }
24183     },
24184
24185     /**
24186      * Cancel outstanding changes on all changed records.
24187      */
24188     rejectChanges : function(){
24189         var m = this.modified.slice(0);
24190         this.modified = [];
24191         for(var i = 0, len = m.length; i < len; i++){
24192             m[i].reject();
24193         }
24194     },
24195
24196     onMetaChange : function(meta, rtype, o){
24197         this.recordType = rtype;
24198         this.fields = rtype.prototype.fields;
24199         delete this.snapshot;
24200         this.sortInfo = meta.sortInfo || this.sortInfo;
24201         this.modified = [];
24202         this.fireEvent('metachange', this, this.reader.meta);
24203     },
24204     
24205     moveIndex : function(data, type)
24206     {
24207         var index = this.indexOf(data);
24208         
24209         var newIndex = index + type;
24210         
24211         this.remove(data);
24212         
24213         this.insert(newIndex, data);
24214         
24215     }
24216 });/*
24217  * Based on:
24218  * Ext JS Library 1.1.1
24219  * Copyright(c) 2006-2007, Ext JS, LLC.
24220  *
24221  * Originally Released Under LGPL - original licence link has changed is not relivant.
24222  *
24223  * Fork - LGPL
24224  * <script type="text/javascript">
24225  */
24226
24227 /**
24228  * @class Roo.data.SimpleStore
24229  * @extends Roo.data.Store
24230  * Small helper class to make creating Stores from Array data easier.
24231  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24232  * @cfg {Array} fields An array of field definition objects, or field name strings.
24233  * @cfg {Object} an existing reader (eg. copied from another store)
24234  * @cfg {Array} data The multi-dimensional array of data
24235  * @constructor
24236  * @param {Object} config
24237  */
24238 Roo.data.SimpleStore = function(config)
24239 {
24240     Roo.data.SimpleStore.superclass.constructor.call(this, {
24241         isLocal : true,
24242         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24243                 id: config.id
24244             },
24245             Roo.data.Record.create(config.fields)
24246         ),
24247         proxy : new Roo.data.MemoryProxy(config.data)
24248     });
24249     this.load();
24250 };
24251 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24252  * Based on:
24253  * Ext JS Library 1.1.1
24254  * Copyright(c) 2006-2007, Ext JS, LLC.
24255  *
24256  * Originally Released Under LGPL - original licence link has changed is not relivant.
24257  *
24258  * Fork - LGPL
24259  * <script type="text/javascript">
24260  */
24261
24262 /**
24263 /**
24264  * @extends Roo.data.Store
24265  * @class Roo.data.JsonStore
24266  * Small helper class to make creating Stores for JSON data easier. <br/>
24267 <pre><code>
24268 var store = new Roo.data.JsonStore({
24269     url: 'get-images.php',
24270     root: 'images',
24271     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24272 });
24273 </code></pre>
24274  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24275  * JsonReader and HttpProxy (unless inline data is provided).</b>
24276  * @cfg {Array} fields An array of field definition objects, or field name strings.
24277  * @constructor
24278  * @param {Object} config
24279  */
24280 Roo.data.JsonStore = function(c){
24281     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24282         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24283         reader: new Roo.data.JsonReader(c, c.fields)
24284     }));
24285 };
24286 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24287  * Based on:
24288  * Ext JS Library 1.1.1
24289  * Copyright(c) 2006-2007, Ext JS, LLC.
24290  *
24291  * Originally Released Under LGPL - original licence link has changed is not relivant.
24292  *
24293  * Fork - LGPL
24294  * <script type="text/javascript">
24295  */
24296
24297  
24298 Roo.data.Field = function(config){
24299     if(typeof config == "string"){
24300         config = {name: config};
24301     }
24302     Roo.apply(this, config);
24303     
24304     if(!this.type){
24305         this.type = "auto";
24306     }
24307     
24308     var st = Roo.data.SortTypes;
24309     // named sortTypes are supported, here we look them up
24310     if(typeof this.sortType == "string"){
24311         this.sortType = st[this.sortType];
24312     }
24313     
24314     // set default sortType for strings and dates
24315     if(!this.sortType){
24316         switch(this.type){
24317             case "string":
24318                 this.sortType = st.asUCString;
24319                 break;
24320             case "date":
24321                 this.sortType = st.asDate;
24322                 break;
24323             default:
24324                 this.sortType = st.none;
24325         }
24326     }
24327
24328     // define once
24329     var stripRe = /[\$,%]/g;
24330
24331     // prebuilt conversion function for this field, instead of
24332     // switching every time we're reading a value
24333     if(!this.convert){
24334         var cv, dateFormat = this.dateFormat;
24335         switch(this.type){
24336             case "":
24337             case "auto":
24338             case undefined:
24339                 cv = function(v){ return v; };
24340                 break;
24341             case "string":
24342                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24343                 break;
24344             case "int":
24345                 cv = function(v){
24346                     return v !== undefined && v !== null && v !== '' ?
24347                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24348                     };
24349                 break;
24350             case "float":
24351                 cv = function(v){
24352                     return v !== undefined && v !== null && v !== '' ?
24353                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24354                     };
24355                 break;
24356             case "bool":
24357             case "boolean":
24358                 cv = function(v){ return v === true || v === "true" || v == 1; };
24359                 break;
24360             case "date":
24361                 cv = function(v){
24362                     if(!v){
24363                         return '';
24364                     }
24365                     if(v instanceof Date){
24366                         return v;
24367                     }
24368                     if(dateFormat){
24369                         if(dateFormat == "timestamp"){
24370                             return new Date(v*1000);
24371                         }
24372                         return Date.parseDate(v, dateFormat);
24373                     }
24374                     var parsed = Date.parse(v);
24375                     return parsed ? new Date(parsed) : null;
24376                 };
24377              break;
24378             
24379         }
24380         this.convert = cv;
24381     }
24382 };
24383
24384 Roo.data.Field.prototype = {
24385     dateFormat: null,
24386     defaultValue: "",
24387     mapping: null,
24388     sortType : null,
24389     sortDir : "ASC"
24390 };/*
24391  * Based on:
24392  * Ext JS Library 1.1.1
24393  * Copyright(c) 2006-2007, Ext JS, LLC.
24394  *
24395  * Originally Released Under LGPL - original licence link has changed is not relivant.
24396  *
24397  * Fork - LGPL
24398  * <script type="text/javascript">
24399  */
24400  
24401 // Base class for reading structured data from a data source.  This class is intended to be
24402 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24403
24404 /**
24405  * @class Roo.data.DataReader
24406  * Base class for reading structured data from a data source.  This class is intended to be
24407  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24408  */
24409
24410 Roo.data.DataReader = function(meta, recordType){
24411     
24412     this.meta = meta;
24413     
24414     this.recordType = recordType instanceof Array ? 
24415         Roo.data.Record.create(recordType) : recordType;
24416 };
24417
24418 Roo.data.DataReader.prototype = {
24419     
24420     
24421     readerType : 'Data',
24422      /**
24423      * Create an empty record
24424      * @param {Object} data (optional) - overlay some values
24425      * @return {Roo.data.Record} record created.
24426      */
24427     newRow :  function(d) {
24428         var da =  {};
24429         this.recordType.prototype.fields.each(function(c) {
24430             switch( c.type) {
24431                 case 'int' : da[c.name] = 0; break;
24432                 case 'date' : da[c.name] = new Date(); break;
24433                 case 'float' : da[c.name] = 0.0; break;
24434                 case 'boolean' : da[c.name] = false; break;
24435                 default : da[c.name] = ""; break;
24436             }
24437             
24438         });
24439         return new this.recordType(Roo.apply(da, d));
24440     }
24441     
24442     
24443 };/*
24444  * Based on:
24445  * Ext JS Library 1.1.1
24446  * Copyright(c) 2006-2007, Ext JS, LLC.
24447  *
24448  * Originally Released Under LGPL - original licence link has changed is not relivant.
24449  *
24450  * Fork - LGPL
24451  * <script type="text/javascript">
24452  */
24453
24454 /**
24455  * @class Roo.data.DataProxy
24456  * @extends Roo.data.Observable
24457  * This class is an abstract base class for implementations which provide retrieval of
24458  * unformatted data objects.<br>
24459  * <p>
24460  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24461  * (of the appropriate type which knows how to parse the data object) to provide a block of
24462  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24463  * <p>
24464  * Custom implementations must implement the load method as described in
24465  * {@link Roo.data.HttpProxy#load}.
24466  */
24467 Roo.data.DataProxy = function(){
24468     this.addEvents({
24469         /**
24470          * @event beforeload
24471          * Fires before a network request is made to retrieve a data object.
24472          * @param {Object} This DataProxy object.
24473          * @param {Object} params The params parameter to the load function.
24474          */
24475         beforeload : true,
24476         /**
24477          * @event load
24478          * Fires before the load method's callback is called.
24479          * @param {Object} This DataProxy object.
24480          * @param {Object} o The data object.
24481          * @param {Object} arg The callback argument object passed to the load function.
24482          */
24483         load : true,
24484         /**
24485          * @event loadexception
24486          * Fires if an Exception occurs during data retrieval.
24487          * @param {Object} This DataProxy object.
24488          * @param {Object} o The data object.
24489          * @param {Object} arg The callback argument object passed to the load function.
24490          * @param {Object} e The Exception.
24491          */
24492         loadexception : true
24493     });
24494     Roo.data.DataProxy.superclass.constructor.call(this);
24495 };
24496
24497 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24498
24499     /**
24500      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24501      */
24502 /*
24503  * Based on:
24504  * Ext JS Library 1.1.1
24505  * Copyright(c) 2006-2007, Ext JS, LLC.
24506  *
24507  * Originally Released Under LGPL - original licence link has changed is not relivant.
24508  *
24509  * Fork - LGPL
24510  * <script type="text/javascript">
24511  */
24512 /**
24513  * @class Roo.data.MemoryProxy
24514  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24515  * to the Reader when its load method is called.
24516  * @constructor
24517  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24518  */
24519 Roo.data.MemoryProxy = function(data){
24520     if (data.data) {
24521         data = data.data;
24522     }
24523     Roo.data.MemoryProxy.superclass.constructor.call(this);
24524     this.data = data;
24525 };
24526
24527 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24528     
24529     /**
24530      * Load data from the requested source (in this case an in-memory
24531      * data object passed to the constructor), read the data object into
24532      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24533      * process that block using the passed callback.
24534      * @param {Object} params This parameter is not used by the MemoryProxy class.
24535      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24536      * object into a block of Roo.data.Records.
24537      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24538      * The function must be passed <ul>
24539      * <li>The Record block object</li>
24540      * <li>The "arg" argument from the load function</li>
24541      * <li>A boolean success indicator</li>
24542      * </ul>
24543      * @param {Object} scope The scope in which to call the callback
24544      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24545      */
24546     load : function(params, reader, callback, scope, arg){
24547         params = params || {};
24548         var result;
24549         try {
24550             result = reader.readRecords(params.data ? params.data :this.data);
24551         }catch(e){
24552             this.fireEvent("loadexception", this, arg, null, e);
24553             callback.call(scope, null, arg, false);
24554             return;
24555         }
24556         callback.call(scope, result, arg, true);
24557     },
24558     
24559     // private
24560     update : function(params, records){
24561         
24562     }
24563 });/*
24564  * Based on:
24565  * Ext JS Library 1.1.1
24566  * Copyright(c) 2006-2007, Ext JS, LLC.
24567  *
24568  * Originally Released Under LGPL - original licence link has changed is not relivant.
24569  *
24570  * Fork - LGPL
24571  * <script type="text/javascript">
24572  */
24573 /**
24574  * @class Roo.data.HttpProxy
24575  * @extends Roo.data.DataProxy
24576  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24577  * configured to reference a certain URL.<br><br>
24578  * <p>
24579  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24580  * from which the running page was served.<br><br>
24581  * <p>
24582  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24583  * <p>
24584  * Be aware that to enable the browser to parse an XML document, the server must set
24585  * the Content-Type header in the HTTP response to "text/xml".
24586  * @constructor
24587  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24588  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24589  * will be used to make the request.
24590  */
24591 Roo.data.HttpProxy = function(conn){
24592     Roo.data.HttpProxy.superclass.constructor.call(this);
24593     // is conn a conn config or a real conn?
24594     this.conn = conn;
24595     this.useAjax = !conn || !conn.events;
24596   
24597 };
24598
24599 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24600     // thse are take from connection...
24601     
24602     /**
24603      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24604      */
24605     /**
24606      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24607      * extra parameters to each request made by this object. (defaults to undefined)
24608      */
24609     /**
24610      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24611      *  to each request made by this object. (defaults to undefined)
24612      */
24613     /**
24614      * @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)
24615      */
24616     /**
24617      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24618      */
24619      /**
24620      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24621      * @type Boolean
24622      */
24623   
24624
24625     /**
24626      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24627      * @type Boolean
24628      */
24629     /**
24630      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24631      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24632      * a finer-grained basis than the DataProxy events.
24633      */
24634     getConnection : function(){
24635         return this.useAjax ? Roo.Ajax : this.conn;
24636     },
24637
24638     /**
24639      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24640      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24641      * process that block using the passed callback.
24642      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24643      * for the request to the remote server.
24644      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24645      * object into a block of Roo.data.Records.
24646      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24647      * The function must be passed <ul>
24648      * <li>The Record block object</li>
24649      * <li>The "arg" argument from the load function</li>
24650      * <li>A boolean success indicator</li>
24651      * </ul>
24652      * @param {Object} scope The scope in which to call the callback
24653      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24654      */
24655     load : function(params, reader, callback, scope, arg){
24656         if(this.fireEvent("beforeload", this, params) !== false){
24657             var  o = {
24658                 params : params || {},
24659                 request: {
24660                     callback : callback,
24661                     scope : scope,
24662                     arg : arg
24663                 },
24664                 reader: reader,
24665                 callback : this.loadResponse,
24666                 scope: this
24667             };
24668             if(this.useAjax){
24669                 Roo.applyIf(o, this.conn);
24670                 if(this.activeRequest){
24671                     Roo.Ajax.abort(this.activeRequest);
24672                 }
24673                 this.activeRequest = Roo.Ajax.request(o);
24674             }else{
24675                 this.conn.request(o);
24676             }
24677         }else{
24678             callback.call(scope||this, null, arg, false);
24679         }
24680     },
24681
24682     // private
24683     loadResponse : function(o, success, response){
24684         delete this.activeRequest;
24685         if(!success){
24686             this.fireEvent("loadexception", this, o, response);
24687             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24688             return;
24689         }
24690         var result;
24691         try {
24692             result = o.reader.read(response);
24693         }catch(e){
24694             this.fireEvent("loadexception", this, o, response, e);
24695             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24696             return;
24697         }
24698         
24699         this.fireEvent("load", this, o, o.request.arg);
24700         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24701     },
24702
24703     // private
24704     update : function(dataSet){
24705
24706     },
24707
24708     // private
24709     updateResponse : function(dataSet){
24710
24711     }
24712 });/*
24713  * Based on:
24714  * Ext JS Library 1.1.1
24715  * Copyright(c) 2006-2007, Ext JS, LLC.
24716  *
24717  * Originally Released Under LGPL - original licence link has changed is not relivant.
24718  *
24719  * Fork - LGPL
24720  * <script type="text/javascript">
24721  */
24722
24723 /**
24724  * @class Roo.data.ScriptTagProxy
24725  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24726  * other than the originating domain of the running page.<br><br>
24727  * <p>
24728  * <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
24729  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24730  * <p>
24731  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24732  * source code that is used as the source inside a &lt;script> tag.<br><br>
24733  * <p>
24734  * In order for the browser to process the returned data, the server must wrap the data object
24735  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24736  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24737  * depending on whether the callback name was passed:
24738  * <p>
24739  * <pre><code>
24740 boolean scriptTag = false;
24741 String cb = request.getParameter("callback");
24742 if (cb != null) {
24743     scriptTag = true;
24744     response.setContentType("text/javascript");
24745 } else {
24746     response.setContentType("application/x-json");
24747 }
24748 Writer out = response.getWriter();
24749 if (scriptTag) {
24750     out.write(cb + "(");
24751 }
24752 out.print(dataBlock.toJsonString());
24753 if (scriptTag) {
24754     out.write(");");
24755 }
24756 </pre></code>
24757  *
24758  * @constructor
24759  * @param {Object} config A configuration object.
24760  */
24761 Roo.data.ScriptTagProxy = function(config){
24762     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24763     Roo.apply(this, config);
24764     this.head = document.getElementsByTagName("head")[0];
24765 };
24766
24767 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24768
24769 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24770     /**
24771      * @cfg {String} url The URL from which to request the data object.
24772      */
24773     /**
24774      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24775      */
24776     timeout : 30000,
24777     /**
24778      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24779      * the server the name of the callback function set up by the load call to process the returned data object.
24780      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24781      * javascript output which calls this named function passing the data object as its only parameter.
24782      */
24783     callbackParam : "callback",
24784     /**
24785      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24786      * name to the request.
24787      */
24788     nocache : true,
24789
24790     /**
24791      * Load data from the configured URL, read the data object into
24792      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24793      * process that block using the passed callback.
24794      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24795      * for the request to the remote server.
24796      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24797      * object into a block of Roo.data.Records.
24798      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24799      * The function must be passed <ul>
24800      * <li>The Record block object</li>
24801      * <li>The "arg" argument from the load function</li>
24802      * <li>A boolean success indicator</li>
24803      * </ul>
24804      * @param {Object} scope The scope in which to call the callback
24805      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24806      */
24807     load : function(params, reader, callback, scope, arg){
24808         if(this.fireEvent("beforeload", this, params) !== false){
24809
24810             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24811
24812             var url = this.url;
24813             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24814             if(this.nocache){
24815                 url += "&_dc=" + (new Date().getTime());
24816             }
24817             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24818             var trans = {
24819                 id : transId,
24820                 cb : "stcCallback"+transId,
24821                 scriptId : "stcScript"+transId,
24822                 params : params,
24823                 arg : arg,
24824                 url : url,
24825                 callback : callback,
24826                 scope : scope,
24827                 reader : reader
24828             };
24829             var conn = this;
24830
24831             window[trans.cb] = function(o){
24832                 conn.handleResponse(o, trans);
24833             };
24834
24835             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24836
24837             if(this.autoAbort !== false){
24838                 this.abort();
24839             }
24840
24841             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24842
24843             var script = document.createElement("script");
24844             script.setAttribute("src", url);
24845             script.setAttribute("type", "text/javascript");
24846             script.setAttribute("id", trans.scriptId);
24847             this.head.appendChild(script);
24848
24849             this.trans = trans;
24850         }else{
24851             callback.call(scope||this, null, arg, false);
24852         }
24853     },
24854
24855     // private
24856     isLoading : function(){
24857         return this.trans ? true : false;
24858     },
24859
24860     /**
24861      * Abort the current server request.
24862      */
24863     abort : function(){
24864         if(this.isLoading()){
24865             this.destroyTrans(this.trans);
24866         }
24867     },
24868
24869     // private
24870     destroyTrans : function(trans, isLoaded){
24871         this.head.removeChild(document.getElementById(trans.scriptId));
24872         clearTimeout(trans.timeoutId);
24873         if(isLoaded){
24874             window[trans.cb] = undefined;
24875             try{
24876                 delete window[trans.cb];
24877             }catch(e){}
24878         }else{
24879             // if hasn't been loaded, wait for load to remove it to prevent script error
24880             window[trans.cb] = function(){
24881                 window[trans.cb] = undefined;
24882                 try{
24883                     delete window[trans.cb];
24884                 }catch(e){}
24885             };
24886         }
24887     },
24888
24889     // private
24890     handleResponse : function(o, trans){
24891         this.trans = false;
24892         this.destroyTrans(trans, true);
24893         var result;
24894         try {
24895             result = trans.reader.readRecords(o);
24896         }catch(e){
24897             this.fireEvent("loadexception", this, o, trans.arg, e);
24898             trans.callback.call(trans.scope||window, null, trans.arg, false);
24899             return;
24900         }
24901         this.fireEvent("load", this, o, trans.arg);
24902         trans.callback.call(trans.scope||window, result, trans.arg, true);
24903     },
24904
24905     // private
24906     handleFailure : function(trans){
24907         this.trans = false;
24908         this.destroyTrans(trans, false);
24909         this.fireEvent("loadexception", this, null, trans.arg);
24910         trans.callback.call(trans.scope||window, null, trans.arg, false);
24911     }
24912 });/*
24913  * Based on:
24914  * Ext JS Library 1.1.1
24915  * Copyright(c) 2006-2007, Ext JS, LLC.
24916  *
24917  * Originally Released Under LGPL - original licence link has changed is not relivant.
24918  *
24919  * Fork - LGPL
24920  * <script type="text/javascript">
24921  */
24922
24923 /**
24924  * @class Roo.data.JsonReader
24925  * @extends Roo.data.DataReader
24926  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24927  * based on mappings in a provided Roo.data.Record constructor.
24928  * 
24929  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24930  * in the reply previously. 
24931  * 
24932  * <p>
24933  * Example code:
24934  * <pre><code>
24935 var RecordDef = Roo.data.Record.create([
24936     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24937     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24938 ]);
24939 var myReader = new Roo.data.JsonReader({
24940     totalProperty: "results",    // The property which contains the total dataset size (optional)
24941     root: "rows",                // The property which contains an Array of row objects
24942     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24943 }, RecordDef);
24944 </code></pre>
24945  * <p>
24946  * This would consume a JSON file like this:
24947  * <pre><code>
24948 { 'results': 2, 'rows': [
24949     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24950     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24951 }
24952 </code></pre>
24953  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24954  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24955  * paged from the remote server.
24956  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24957  * @cfg {String} root name of the property which contains the Array of row objects.
24958  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24959  * @cfg {Array} fields Array of field definition objects
24960  * @constructor
24961  * Create a new JsonReader
24962  * @param {Object} meta Metadata configuration options
24963  * @param {Object} recordType Either an Array of field definition objects,
24964  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24965  */
24966 Roo.data.JsonReader = function(meta, recordType){
24967     
24968     meta = meta || {};
24969     // set some defaults:
24970     Roo.applyIf(meta, {
24971         totalProperty: 'total',
24972         successProperty : 'success',
24973         root : 'data',
24974         id : 'id'
24975     });
24976     
24977     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24978 };
24979 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24980     
24981     readerType : 'Json',
24982     
24983     /**
24984      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24985      * Used by Store query builder to append _requestMeta to params.
24986      * 
24987      */
24988     metaFromRemote : false,
24989     /**
24990      * This method is only used by a DataProxy which has retrieved data from a remote server.
24991      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24992      * @return {Object} data A data block which is used by an Roo.data.Store object as
24993      * a cache of Roo.data.Records.
24994      */
24995     read : function(response){
24996         var json = response.responseText;
24997        
24998         var o = /* eval:var:o */ eval("("+json+")");
24999         if(!o) {
25000             throw {message: "JsonReader.read: Json object not found"};
25001         }
25002         
25003         if(o.metaData){
25004             
25005             delete this.ef;
25006             this.metaFromRemote = true;
25007             this.meta = o.metaData;
25008             this.recordType = Roo.data.Record.create(o.metaData.fields);
25009             this.onMetaChange(this.meta, this.recordType, o);
25010         }
25011         return this.readRecords(o);
25012     },
25013
25014     // private function a store will implement
25015     onMetaChange : function(meta, recordType, o){
25016
25017     },
25018
25019     /**
25020          * @ignore
25021          */
25022     simpleAccess: function(obj, subsc) {
25023         return obj[subsc];
25024     },
25025
25026         /**
25027          * @ignore
25028          */
25029     getJsonAccessor: function(){
25030         var re = /[\[\.]/;
25031         return function(expr) {
25032             try {
25033                 return(re.test(expr))
25034                     ? new Function("obj", "return obj." + expr)
25035                     : function(obj){
25036                         return obj[expr];
25037                     };
25038             } catch(e){}
25039             return Roo.emptyFn;
25040         };
25041     }(),
25042
25043     /**
25044      * Create a data block containing Roo.data.Records from an XML document.
25045      * @param {Object} o An object which contains an Array of row objects in the property specified
25046      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25047      * which contains the total size of the dataset.
25048      * @return {Object} data A data block which is used by an Roo.data.Store object as
25049      * a cache of Roo.data.Records.
25050      */
25051     readRecords : function(o){
25052         /**
25053          * After any data loads, the raw JSON data is available for further custom processing.
25054          * @type Object
25055          */
25056         this.o = o;
25057         var s = this.meta, Record = this.recordType,
25058             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25059
25060 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25061         if (!this.ef) {
25062             if(s.totalProperty) {
25063                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25064                 }
25065                 if(s.successProperty) {
25066                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25067                 }
25068                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25069                 if (s.id) {
25070                         var g = this.getJsonAccessor(s.id);
25071                         this.getId = function(rec) {
25072                                 var r = g(rec);  
25073                                 return (r === undefined || r === "") ? null : r;
25074                         };
25075                 } else {
25076                         this.getId = function(){return null;};
25077                 }
25078             this.ef = [];
25079             for(var jj = 0; jj < fl; jj++){
25080                 f = fi[jj];
25081                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25082                 this.ef[jj] = this.getJsonAccessor(map);
25083             }
25084         }
25085
25086         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25087         if(s.totalProperty){
25088             var vt = parseInt(this.getTotal(o), 10);
25089             if(!isNaN(vt)){
25090                 totalRecords = vt;
25091             }
25092         }
25093         if(s.successProperty){
25094             var vs = this.getSuccess(o);
25095             if(vs === false || vs === 'false'){
25096                 success = false;
25097             }
25098         }
25099         var records = [];
25100         for(var i = 0; i < c; i++){
25101                 var n = root[i];
25102             var values = {};
25103             var id = this.getId(n);
25104             for(var j = 0; j < fl; j++){
25105                 f = fi[j];
25106             var v = this.ef[j](n);
25107             if (!f.convert) {
25108                 Roo.log('missing convert for ' + f.name);
25109                 Roo.log(f);
25110                 continue;
25111             }
25112             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25113             }
25114             var record = new Record(values, id);
25115             record.json = n;
25116             records[i] = record;
25117         }
25118         return {
25119             raw : o,
25120             success : success,
25121             records : records,
25122             totalRecords : totalRecords
25123         };
25124     },
25125     // used when loading children.. @see loadDataFromChildren
25126     toLoadData: function(rec)
25127     {
25128         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25129         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25130         return { data : data, total : data.length };
25131         
25132     }
25133 });/*
25134  * Based on:
25135  * Ext JS Library 1.1.1
25136  * Copyright(c) 2006-2007, Ext JS, LLC.
25137  *
25138  * Originally Released Under LGPL - original licence link has changed is not relivant.
25139  *
25140  * Fork - LGPL
25141  * <script type="text/javascript">
25142  */
25143
25144 /**
25145  * @class Roo.data.XmlReader
25146  * @extends Roo.data.DataReader
25147  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25148  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25149  * <p>
25150  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25151  * header in the HTTP response must be set to "text/xml".</em>
25152  * <p>
25153  * Example code:
25154  * <pre><code>
25155 var RecordDef = Roo.data.Record.create([
25156    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25157    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25158 ]);
25159 var myReader = new Roo.data.XmlReader({
25160    totalRecords: "results", // The element which contains the total dataset size (optional)
25161    record: "row",           // The repeated element which contains row information
25162    id: "id"                 // The element within the row that provides an ID for the record (optional)
25163 }, RecordDef);
25164 </code></pre>
25165  * <p>
25166  * This would consume an XML file like this:
25167  * <pre><code>
25168 &lt;?xml?>
25169 &lt;dataset>
25170  &lt;results>2&lt;/results>
25171  &lt;row>
25172    &lt;id>1&lt;/id>
25173    &lt;name>Bill&lt;/name>
25174    &lt;occupation>Gardener&lt;/occupation>
25175  &lt;/row>
25176  &lt;row>
25177    &lt;id>2&lt;/id>
25178    &lt;name>Ben&lt;/name>
25179    &lt;occupation>Horticulturalist&lt;/occupation>
25180  &lt;/row>
25181 &lt;/dataset>
25182 </code></pre>
25183  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25184  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25185  * paged from the remote server.
25186  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25187  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25188  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25189  * a record identifier value.
25190  * @constructor
25191  * Create a new XmlReader
25192  * @param {Object} meta Metadata configuration options
25193  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25194  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25195  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25196  */
25197 Roo.data.XmlReader = function(meta, recordType){
25198     meta = meta || {};
25199     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25200 };
25201 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25202     
25203     readerType : 'Xml',
25204     
25205     /**
25206      * This method is only used by a DataProxy which has retrieved data from a remote server.
25207          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25208          * to contain a method called 'responseXML' that returns an XML document object.
25209      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25210      * a cache of Roo.data.Records.
25211      */
25212     read : function(response){
25213         var doc = response.responseXML;
25214         if(!doc) {
25215             throw {message: "XmlReader.read: XML Document not available"};
25216         }
25217         return this.readRecords(doc);
25218     },
25219
25220     /**
25221      * Create a data block containing Roo.data.Records from an XML document.
25222          * @param {Object} doc A parsed XML document.
25223      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25224      * a cache of Roo.data.Records.
25225      */
25226     readRecords : function(doc){
25227         /**
25228          * After any data loads/reads, the raw XML Document is available for further custom processing.
25229          * @type XMLDocument
25230          */
25231         this.xmlData = doc;
25232         var root = doc.documentElement || doc;
25233         var q = Roo.DomQuery;
25234         var recordType = this.recordType, fields = recordType.prototype.fields;
25235         var sid = this.meta.id;
25236         var totalRecords = 0, success = true;
25237         if(this.meta.totalRecords){
25238             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25239         }
25240         
25241         if(this.meta.success){
25242             var sv = q.selectValue(this.meta.success, root, true);
25243             success = sv !== false && sv !== 'false';
25244         }
25245         var records = [];
25246         var ns = q.select(this.meta.record, root);
25247         for(var i = 0, len = ns.length; i < len; i++) {
25248                 var n = ns[i];
25249                 var values = {};
25250                 var id = sid ? q.selectValue(sid, n) : undefined;
25251                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25252                     var f = fields.items[j];
25253                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25254                     v = f.convert(v);
25255                     values[f.name] = v;
25256                 }
25257                 var record = new recordType(values, id);
25258                 record.node = n;
25259                 records[records.length] = record;
25260             }
25261
25262             return {
25263                 success : success,
25264                 records : records,
25265                 totalRecords : totalRecords || records.length
25266             };
25267     }
25268 });/*
25269  * Based on:
25270  * Ext JS Library 1.1.1
25271  * Copyright(c) 2006-2007, Ext JS, LLC.
25272  *
25273  * Originally Released Under LGPL - original licence link has changed is not relivant.
25274  *
25275  * Fork - LGPL
25276  * <script type="text/javascript">
25277  */
25278
25279 /**
25280  * @class Roo.data.ArrayReader
25281  * @extends Roo.data.DataReader
25282  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25283  * Each element of that Array represents a row of data fields. The
25284  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25285  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25286  * <p>
25287  * Example code:.
25288  * <pre><code>
25289 var RecordDef = Roo.data.Record.create([
25290     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25291     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25292 ]);
25293 var myReader = new Roo.data.ArrayReader({
25294     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25295 }, RecordDef);
25296 </code></pre>
25297  * <p>
25298  * This would consume an Array like this:
25299  * <pre><code>
25300 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25301   </code></pre>
25302  
25303  * @constructor
25304  * Create a new JsonReader
25305  * @param {Object} meta Metadata configuration options.
25306  * @param {Object|Array} recordType Either an Array of field definition objects
25307  * 
25308  * @cfg {Array} fields Array of field definition objects
25309  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25310  * as specified to {@link Roo.data.Record#create},
25311  * or an {@link Roo.data.Record} object
25312  *
25313  * 
25314  * created using {@link Roo.data.Record#create}.
25315  */
25316 Roo.data.ArrayReader = function(meta, recordType)
25317 {    
25318     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25319 };
25320
25321 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25322     
25323       /**
25324      * Create a data block containing Roo.data.Records from an XML document.
25325      * @param {Object} o An Array of row objects which represents the dataset.
25326      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25327      * a cache of Roo.data.Records.
25328      */
25329     readRecords : function(o)
25330     {
25331         var sid = this.meta ? this.meta.id : null;
25332         var recordType = this.recordType, fields = recordType.prototype.fields;
25333         var records = [];
25334         var root = o;
25335         for(var i = 0; i < root.length; i++){
25336             var n = root[i];
25337             var values = {};
25338             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25339             for(var j = 0, jlen = fields.length; j < jlen; j++){
25340                 var f = fields.items[j];
25341                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25342                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25343                 v = f.convert(v);
25344                 values[f.name] = v;
25345             }
25346             var record = new recordType(values, id);
25347             record.json = n;
25348             records[records.length] = record;
25349         }
25350         return {
25351             records : records,
25352             totalRecords : records.length
25353         };
25354     },
25355     // used when loading children.. @see loadDataFromChildren
25356     toLoadData: function(rec)
25357     {
25358         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25359         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25360         
25361     }
25362     
25363     
25364 });/*
25365  * Based on:
25366  * Ext JS Library 1.1.1
25367  * Copyright(c) 2006-2007, Ext JS, LLC.
25368  *
25369  * Originally Released Under LGPL - original licence link has changed is not relivant.
25370  *
25371  * Fork - LGPL
25372  * <script type="text/javascript">
25373  */
25374
25375
25376 /**
25377  * @class Roo.data.Tree
25378  * @extends Roo.util.Observable
25379  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25380  * in the tree have most standard DOM functionality.
25381  * @constructor
25382  * @param {Node} root (optional) The root node
25383  */
25384 Roo.data.Tree = function(root){
25385    this.nodeHash = {};
25386    /**
25387     * The root node for this tree
25388     * @type Node
25389     */
25390    this.root = null;
25391    if(root){
25392        this.setRootNode(root);
25393    }
25394    this.addEvents({
25395        /**
25396         * @event append
25397         * Fires when a new child node is appended to a node in this tree.
25398         * @param {Tree} tree The owner tree
25399         * @param {Node} parent The parent node
25400         * @param {Node} node The newly appended node
25401         * @param {Number} index The index of the newly appended node
25402         */
25403        "append" : true,
25404        /**
25405         * @event remove
25406         * Fires when a child node is removed from a node in this tree.
25407         * @param {Tree} tree The owner tree
25408         * @param {Node} parent The parent node
25409         * @param {Node} node The child node removed
25410         */
25411        "remove" : true,
25412        /**
25413         * @event move
25414         * Fires when a node is moved to a new location in the tree
25415         * @param {Tree} tree The owner tree
25416         * @param {Node} node The node moved
25417         * @param {Node} oldParent The old parent of this node
25418         * @param {Node} newParent The new parent of this node
25419         * @param {Number} index The index it was moved to
25420         */
25421        "move" : true,
25422        /**
25423         * @event insert
25424         * Fires when a new child node is inserted in 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 inserted
25428         * @param {Node} refNode The child node the node was inserted before
25429         */
25430        "insert" : true,
25431        /**
25432         * @event beforeappend
25433         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25434         * @param {Tree} tree The owner tree
25435         * @param {Node} parent The parent node
25436         * @param {Node} node The child node to be appended
25437         */
25438        "beforeappend" : true,
25439        /**
25440         * @event beforeremove
25441         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25442         * @param {Tree} tree The owner tree
25443         * @param {Node} parent The parent node
25444         * @param {Node} node The child node to be removed
25445         */
25446        "beforeremove" : true,
25447        /**
25448         * @event beforemove
25449         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25450         * @param {Tree} tree The owner tree
25451         * @param {Node} node The node being moved
25452         * @param {Node} oldParent The parent of the node
25453         * @param {Node} newParent The new parent the node is moving to
25454         * @param {Number} index The index it is being moved to
25455         */
25456        "beforemove" : true,
25457        /**
25458         * @event beforeinsert
25459         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25460         * @param {Tree} tree The owner tree
25461         * @param {Node} parent The parent node
25462         * @param {Node} node The child node to be inserted
25463         * @param {Node} refNode The child node the node is being inserted before
25464         */
25465        "beforeinsert" : true
25466    });
25467
25468     Roo.data.Tree.superclass.constructor.call(this);
25469 };
25470
25471 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25472     pathSeparator: "/",
25473
25474     proxyNodeEvent : function(){
25475         return this.fireEvent.apply(this, arguments);
25476     },
25477
25478     /**
25479      * Returns the root node for this tree.
25480      * @return {Node}
25481      */
25482     getRootNode : function(){
25483         return this.root;
25484     },
25485
25486     /**
25487      * Sets the root node for this tree.
25488      * @param {Node} node
25489      * @return {Node}
25490      */
25491     setRootNode : function(node){
25492         this.root = node;
25493         node.ownerTree = this;
25494         node.isRoot = true;
25495         this.registerNode(node);
25496         return node;
25497     },
25498
25499     /**
25500      * Gets a node in this tree by its id.
25501      * @param {String} id
25502      * @return {Node}
25503      */
25504     getNodeById : function(id){
25505         return this.nodeHash[id];
25506     },
25507
25508     registerNode : function(node){
25509         this.nodeHash[node.id] = node;
25510     },
25511
25512     unregisterNode : function(node){
25513         delete this.nodeHash[node.id];
25514     },
25515
25516     toString : function(){
25517         return "[Tree"+(this.id?" "+this.id:"")+"]";
25518     }
25519 });
25520
25521 /**
25522  * @class Roo.data.Node
25523  * @extends Roo.util.Observable
25524  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25525  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25526  * @constructor
25527  * @param {Object} attributes The attributes/config for the node
25528  */
25529 Roo.data.Node = function(attributes){
25530     /**
25531      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25532      * @type {Object}
25533      */
25534     this.attributes = attributes || {};
25535     this.leaf = this.attributes.leaf;
25536     /**
25537      * The node id. @type String
25538      */
25539     this.id = this.attributes.id;
25540     if(!this.id){
25541         this.id = Roo.id(null, "ynode-");
25542         this.attributes.id = this.id;
25543     }
25544      
25545     
25546     /**
25547      * All child nodes of this node. @type Array
25548      */
25549     this.childNodes = [];
25550     if(!this.childNodes.indexOf){ // indexOf is a must
25551         this.childNodes.indexOf = function(o){
25552             for(var i = 0, len = this.length; i < len; i++){
25553                 if(this[i] == o) {
25554                     return i;
25555                 }
25556             }
25557             return -1;
25558         };
25559     }
25560     /**
25561      * The parent node for this node. @type Node
25562      */
25563     this.parentNode = null;
25564     /**
25565      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25566      */
25567     this.firstChild = null;
25568     /**
25569      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25570      */
25571     this.lastChild = null;
25572     /**
25573      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25574      */
25575     this.previousSibling = null;
25576     /**
25577      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25578      */
25579     this.nextSibling = null;
25580
25581     this.addEvents({
25582        /**
25583         * @event append
25584         * Fires when a new child node is appended
25585         * @param {Tree} tree The owner tree
25586         * @param {Node} this This node
25587         * @param {Node} node The newly appended node
25588         * @param {Number} index The index of the newly appended node
25589         */
25590        "append" : true,
25591        /**
25592         * @event remove
25593         * Fires when a child node is removed
25594         * @param {Tree} tree The owner tree
25595         * @param {Node} this This node
25596         * @param {Node} node The removed node
25597         */
25598        "remove" : true,
25599        /**
25600         * @event move
25601         * Fires when this node is moved to a new location in the tree
25602         * @param {Tree} tree The owner tree
25603         * @param {Node} this This node
25604         * @param {Node} oldParent The old parent of this node
25605         * @param {Node} newParent The new parent of this node
25606         * @param {Number} index The index it was moved to
25607         */
25608        "move" : true,
25609        /**
25610         * @event insert
25611         * Fires when a new child node is inserted.
25612         * @param {Tree} tree The owner tree
25613         * @param {Node} this This node
25614         * @param {Node} node The child node inserted
25615         * @param {Node} refNode The child node the node was inserted before
25616         */
25617        "insert" : true,
25618        /**
25619         * @event beforeappend
25620         * Fires before a new child is appended, return false to cancel the append.
25621         * @param {Tree} tree The owner tree
25622         * @param {Node} this This node
25623         * @param {Node} node The child node to be appended
25624         */
25625        "beforeappend" : true,
25626        /**
25627         * @event beforeremove
25628         * Fires before a child is removed, return false to cancel the remove.
25629         * @param {Tree} tree The owner tree
25630         * @param {Node} this This node
25631         * @param {Node} node The child node to be removed
25632         */
25633        "beforeremove" : true,
25634        /**
25635         * @event beforemove
25636         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25637         * @param {Tree} tree The owner tree
25638         * @param {Node} this This node
25639         * @param {Node} oldParent The parent of this node
25640         * @param {Node} newParent The new parent this node is moving to
25641         * @param {Number} index The index it is being moved to
25642         */
25643        "beforemove" : true,
25644        /**
25645         * @event beforeinsert
25646         * Fires before a new child is inserted, return false to cancel the insert.
25647         * @param {Tree} tree The owner tree
25648         * @param {Node} this This node
25649         * @param {Node} node The child node to be inserted
25650         * @param {Node} refNode The child node the node is being inserted before
25651         */
25652        "beforeinsert" : true
25653    });
25654     this.listeners = this.attributes.listeners;
25655     Roo.data.Node.superclass.constructor.call(this);
25656 };
25657
25658 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25659     fireEvent : function(evtName){
25660         // first do standard event for this node
25661         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25662             return false;
25663         }
25664         // then bubble it up to the tree if the event wasn't cancelled
25665         var ot = this.getOwnerTree();
25666         if(ot){
25667             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25668                 return false;
25669             }
25670         }
25671         return true;
25672     },
25673
25674     /**
25675      * Returns true if this node is a leaf
25676      * @return {Boolean}
25677      */
25678     isLeaf : function(){
25679         return this.leaf === true;
25680     },
25681
25682     // private
25683     setFirstChild : function(node){
25684         this.firstChild = node;
25685     },
25686
25687     //private
25688     setLastChild : function(node){
25689         this.lastChild = node;
25690     },
25691
25692
25693     /**
25694      * Returns true if this node is the last child of its parent
25695      * @return {Boolean}
25696      */
25697     isLast : function(){
25698        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25699     },
25700
25701     /**
25702      * Returns true if this node is the first child of its parent
25703      * @return {Boolean}
25704      */
25705     isFirst : function(){
25706        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25707     },
25708
25709     hasChildNodes : function(){
25710         return !this.isLeaf() && this.childNodes.length > 0;
25711     },
25712
25713     /**
25714      * Insert node(s) as the last child node of this node.
25715      * @param {Node/Array} node The node or Array of nodes to append
25716      * @return {Node} The appended node if single append, or null if an array was passed
25717      */
25718     appendChild : function(node){
25719         var multi = false;
25720         if(node instanceof Array){
25721             multi = node;
25722         }else if(arguments.length > 1){
25723             multi = arguments;
25724         }
25725         
25726         // if passed an array or multiple args do them one by one
25727         if(multi){
25728             for(var i = 0, len = multi.length; i < len; i++) {
25729                 this.appendChild(multi[i]);
25730             }
25731         }else{
25732             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25733                 return false;
25734             }
25735             var index = this.childNodes.length;
25736             var oldParent = node.parentNode;
25737             // it's a move, make sure we move it cleanly
25738             if(oldParent){
25739                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25740                     return false;
25741                 }
25742                 oldParent.removeChild(node);
25743             }
25744             
25745             index = this.childNodes.length;
25746             if(index == 0){
25747                 this.setFirstChild(node);
25748             }
25749             this.childNodes.push(node);
25750             node.parentNode = this;
25751             var ps = this.childNodes[index-1];
25752             if(ps){
25753                 node.previousSibling = ps;
25754                 ps.nextSibling = node;
25755             }else{
25756                 node.previousSibling = null;
25757             }
25758             node.nextSibling = null;
25759             this.setLastChild(node);
25760             node.setOwnerTree(this.getOwnerTree());
25761             this.fireEvent("append", this.ownerTree, this, node, index);
25762             if(this.ownerTree) {
25763                 this.ownerTree.fireEvent("appendnode", this, node, index);
25764             }
25765             if(oldParent){
25766                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25767             }
25768             return node;
25769         }
25770     },
25771
25772     /**
25773      * Removes a child node from this node.
25774      * @param {Node} node The node to remove
25775      * @return {Node} The removed node
25776      */
25777     removeChild : function(node){
25778         var index = this.childNodes.indexOf(node);
25779         if(index == -1){
25780             return false;
25781         }
25782         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25783             return false;
25784         }
25785
25786         // remove it from childNodes collection
25787         this.childNodes.splice(index, 1);
25788
25789         // update siblings
25790         if(node.previousSibling){
25791             node.previousSibling.nextSibling = node.nextSibling;
25792         }
25793         if(node.nextSibling){
25794             node.nextSibling.previousSibling = node.previousSibling;
25795         }
25796
25797         // update child refs
25798         if(this.firstChild == node){
25799             this.setFirstChild(node.nextSibling);
25800         }
25801         if(this.lastChild == node){
25802             this.setLastChild(node.previousSibling);
25803         }
25804
25805         node.setOwnerTree(null);
25806         // clear any references from the node
25807         node.parentNode = null;
25808         node.previousSibling = null;
25809         node.nextSibling = null;
25810         this.fireEvent("remove", this.ownerTree, this, node);
25811         return node;
25812     },
25813
25814     /**
25815      * Inserts the first node before the second node in this nodes childNodes collection.
25816      * @param {Node} node The node to insert
25817      * @param {Node} refNode The node to insert before (if null the node is appended)
25818      * @return {Node} The inserted node
25819      */
25820     insertBefore : function(node, refNode){
25821         if(!refNode){ // like standard Dom, refNode can be null for append
25822             return this.appendChild(node);
25823         }
25824         // nothing to do
25825         if(node == refNode){
25826             return false;
25827         }
25828
25829         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25830             return false;
25831         }
25832         var index = this.childNodes.indexOf(refNode);
25833         var oldParent = node.parentNode;
25834         var refIndex = index;
25835
25836         // when moving internally, indexes will change after remove
25837         if(oldParent == this && this.childNodes.indexOf(node) < index){
25838             refIndex--;
25839         }
25840
25841         // it's a move, make sure we move it cleanly
25842         if(oldParent){
25843             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25844                 return false;
25845             }
25846             oldParent.removeChild(node);
25847         }
25848         if(refIndex == 0){
25849             this.setFirstChild(node);
25850         }
25851         this.childNodes.splice(refIndex, 0, node);
25852         node.parentNode = this;
25853         var ps = this.childNodes[refIndex-1];
25854         if(ps){
25855             node.previousSibling = ps;
25856             ps.nextSibling = node;
25857         }else{
25858             node.previousSibling = null;
25859         }
25860         node.nextSibling = refNode;
25861         refNode.previousSibling = node;
25862         node.setOwnerTree(this.getOwnerTree());
25863         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25864         if(oldParent){
25865             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25866         }
25867         return node;
25868     },
25869
25870     /**
25871      * Returns the child node at the specified index.
25872      * @param {Number} index
25873      * @return {Node}
25874      */
25875     item : function(index){
25876         return this.childNodes[index];
25877     },
25878
25879     /**
25880      * Replaces one child node in this node with another.
25881      * @param {Node} newChild The replacement node
25882      * @param {Node} oldChild The node to replace
25883      * @return {Node} The replaced node
25884      */
25885     replaceChild : function(newChild, oldChild){
25886         this.insertBefore(newChild, oldChild);
25887         this.removeChild(oldChild);
25888         return oldChild;
25889     },
25890
25891     /**
25892      * Returns the index of a child node
25893      * @param {Node} node
25894      * @return {Number} The index of the node or -1 if it was not found
25895      */
25896     indexOf : function(child){
25897         return this.childNodes.indexOf(child);
25898     },
25899
25900     /**
25901      * Returns the tree this node is in.
25902      * @return {Tree}
25903      */
25904     getOwnerTree : function(){
25905         // if it doesn't have one, look for one
25906         if(!this.ownerTree){
25907             var p = this;
25908             while(p){
25909                 if(p.ownerTree){
25910                     this.ownerTree = p.ownerTree;
25911                     break;
25912                 }
25913                 p = p.parentNode;
25914             }
25915         }
25916         return this.ownerTree;
25917     },
25918
25919     /**
25920      * Returns depth of this node (the root node has a depth of 0)
25921      * @return {Number}
25922      */
25923     getDepth : function(){
25924         var depth = 0;
25925         var p = this;
25926         while(p.parentNode){
25927             ++depth;
25928             p = p.parentNode;
25929         }
25930         return depth;
25931     },
25932
25933     // private
25934     setOwnerTree : function(tree){
25935         // if it's move, we need to update everyone
25936         if(tree != this.ownerTree){
25937             if(this.ownerTree){
25938                 this.ownerTree.unregisterNode(this);
25939             }
25940             this.ownerTree = tree;
25941             var cs = this.childNodes;
25942             for(var i = 0, len = cs.length; i < len; i++) {
25943                 cs[i].setOwnerTree(tree);
25944             }
25945             if(tree){
25946                 tree.registerNode(this);
25947             }
25948         }
25949     },
25950
25951     /**
25952      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25953      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25954      * @return {String} The path
25955      */
25956     getPath : function(attr){
25957         attr = attr || "id";
25958         var p = this.parentNode;
25959         var b = [this.attributes[attr]];
25960         while(p){
25961             b.unshift(p.attributes[attr]);
25962             p = p.parentNode;
25963         }
25964         var sep = this.getOwnerTree().pathSeparator;
25965         return sep + b.join(sep);
25966     },
25967
25968     /**
25969      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25970      * function call will be the scope provided or the current node. The arguments to the function
25971      * will be the args provided or the current node. If the function returns false at any point,
25972      * the bubble is stopped.
25973      * @param {Function} fn The function to call
25974      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25975      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25976      */
25977     bubble : function(fn, scope, args){
25978         var p = this;
25979         while(p){
25980             if(fn.call(scope || p, args || p) === false){
25981                 break;
25982             }
25983             p = p.parentNode;
25984         }
25985     },
25986
25987     /**
25988      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25989      * function call will be the scope provided or the current node. The arguments to the function
25990      * will be the args provided or the current node. If the function returns false at any point,
25991      * the cascade is stopped on that branch.
25992      * @param {Function} fn The function to call
25993      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25994      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25995      */
25996     cascade : function(fn, scope, args){
25997         if(fn.call(scope || this, args || this) !== false){
25998             var cs = this.childNodes;
25999             for(var i = 0, len = cs.length; i < len; i++) {
26000                 cs[i].cascade(fn, scope, args);
26001             }
26002         }
26003     },
26004
26005     /**
26006      * Interates the child nodes of 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 iteration stops.
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     eachChild : function(fn, scope, args){
26015         var cs = this.childNodes;
26016         for(var i = 0, len = cs.length; i < len; i++) {
26017                 if(fn.call(scope || this, args || cs[i]) === false){
26018                     break;
26019                 }
26020         }
26021     },
26022
26023     /**
26024      * Finds the first child that has the attribute with the specified value.
26025      * @param {String} attribute The attribute name
26026      * @param {Mixed} value The value to search for
26027      * @return {Node} The found child or null if none was found
26028      */
26029     findChild : function(attribute, value){
26030         var cs = this.childNodes;
26031         for(var i = 0, len = cs.length; i < len; i++) {
26032                 if(cs[i].attributes[attribute] == value){
26033                     return cs[i];
26034                 }
26035         }
26036         return null;
26037     },
26038
26039     /**
26040      * Finds the first child by a custom function. The child matches if the function passed
26041      * returns true.
26042      * @param {Function} fn
26043      * @param {Object} scope (optional)
26044      * @return {Node} The found child or null if none was found
26045      */
26046     findChildBy : function(fn, scope){
26047         var cs = this.childNodes;
26048         for(var i = 0, len = cs.length; i < len; i++) {
26049                 if(fn.call(scope||cs[i], cs[i]) === true){
26050                     return cs[i];
26051                 }
26052         }
26053         return null;
26054     },
26055
26056     /**
26057      * Sorts this nodes children using the supplied sort function
26058      * @param {Function} fn
26059      * @param {Object} scope (optional)
26060      */
26061     sort : function(fn, scope){
26062         var cs = this.childNodes;
26063         var len = cs.length;
26064         if(len > 0){
26065             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26066             cs.sort(sortFn);
26067             for(var i = 0; i < len; i++){
26068                 var n = cs[i];
26069                 n.previousSibling = cs[i-1];
26070                 n.nextSibling = cs[i+1];
26071                 if(i == 0){
26072                     this.setFirstChild(n);
26073                 }
26074                 if(i == len-1){
26075                     this.setLastChild(n);
26076                 }
26077             }
26078         }
26079     },
26080
26081     /**
26082      * Returns true if this node is an ancestor (at any point) of the passed node.
26083      * @param {Node} node
26084      * @return {Boolean}
26085      */
26086     contains : function(node){
26087         return node.isAncestor(this);
26088     },
26089
26090     /**
26091      * Returns true if the passed node is an ancestor (at any point) of this node.
26092      * @param {Node} node
26093      * @return {Boolean}
26094      */
26095     isAncestor : function(node){
26096         var p = this.parentNode;
26097         while(p){
26098             if(p == node){
26099                 return true;
26100             }
26101             p = p.parentNode;
26102         }
26103         return false;
26104     },
26105
26106     toString : function(){
26107         return "[Node"+(this.id?" "+this.id:"")+"]";
26108     }
26109 });/*
26110  * Based on:
26111  * Ext JS Library 1.1.1
26112  * Copyright(c) 2006-2007, Ext JS, LLC.
26113  *
26114  * Originally Released Under LGPL - original licence link has changed is not relivant.
26115  *
26116  * Fork - LGPL
26117  * <script type="text/javascript">
26118  */
26119
26120
26121 /**
26122  * @class Roo.Shadow
26123  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26124  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26125  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26126  * @constructor
26127  * Create a new Shadow
26128  * @param {Object} config The config object
26129  */
26130 Roo.Shadow = function(config){
26131     Roo.apply(this, config);
26132     if(typeof this.mode != "string"){
26133         this.mode = this.defaultMode;
26134     }
26135     var o = this.offset, a = {h: 0};
26136     var rad = Math.floor(this.offset/2);
26137     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26138         case "drop":
26139             a.w = 0;
26140             a.l = a.t = o;
26141             a.t -= 1;
26142             if(Roo.isIE){
26143                 a.l -= this.offset + rad;
26144                 a.t -= this.offset + rad;
26145                 a.w -= rad;
26146                 a.h -= rad;
26147                 a.t += 1;
26148             }
26149         break;
26150         case "sides":
26151             a.w = (o*2);
26152             a.l = -o;
26153             a.t = o-1;
26154             if(Roo.isIE){
26155                 a.l -= (this.offset - rad);
26156                 a.t -= this.offset + rad;
26157                 a.l += 1;
26158                 a.w -= (this.offset - rad)*2;
26159                 a.w -= rad + 1;
26160                 a.h -= 1;
26161             }
26162         break;
26163         case "frame":
26164             a.w = a.h = (o*2);
26165             a.l = a.t = -o;
26166             a.t += 1;
26167             a.h -= 2;
26168             if(Roo.isIE){
26169                 a.l -= (this.offset - rad);
26170                 a.t -= (this.offset - rad);
26171                 a.l += 1;
26172                 a.w -= (this.offset + rad + 1);
26173                 a.h -= (this.offset + rad);
26174                 a.h += 1;
26175             }
26176         break;
26177     };
26178
26179     this.adjusts = a;
26180 };
26181
26182 Roo.Shadow.prototype = {
26183     /**
26184      * @cfg {String} mode
26185      * The shadow display mode.  Supports the following options:<br />
26186      * sides: Shadow displays on both sides and bottom only<br />
26187      * frame: Shadow displays equally on all four sides<br />
26188      * drop: Traditional bottom-right drop shadow (default)
26189      */
26190     /**
26191      * @cfg {String} offset
26192      * The number of pixels to offset the shadow from the element (defaults to 4)
26193      */
26194     offset: 4,
26195
26196     // private
26197     defaultMode: "drop",
26198
26199     /**
26200      * Displays the shadow under the target element
26201      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26202      */
26203     show : function(target){
26204         target = Roo.get(target);
26205         if(!this.el){
26206             this.el = Roo.Shadow.Pool.pull();
26207             if(this.el.dom.nextSibling != target.dom){
26208                 this.el.insertBefore(target);
26209             }
26210         }
26211         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26212         if(Roo.isIE){
26213             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26214         }
26215         this.realign(
26216             target.getLeft(true),
26217             target.getTop(true),
26218             target.getWidth(),
26219             target.getHeight()
26220         );
26221         this.el.dom.style.display = "block";
26222     },
26223
26224     /**
26225      * Returns true if the shadow is visible, else false
26226      */
26227     isVisible : function(){
26228         return this.el ? true : false;  
26229     },
26230
26231     /**
26232      * Direct alignment when values are already available. Show must be called at least once before
26233      * calling this method to ensure it is initialized.
26234      * @param {Number} left The target element left position
26235      * @param {Number} top The target element top position
26236      * @param {Number} width The target element width
26237      * @param {Number} height The target element height
26238      */
26239     realign : function(l, t, w, h){
26240         if(!this.el){
26241             return;
26242         }
26243         var a = this.adjusts, d = this.el.dom, s = d.style;
26244         var iea = 0;
26245         s.left = (l+a.l)+"px";
26246         s.top = (t+a.t)+"px";
26247         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26248  
26249         if(s.width != sws || s.height != shs){
26250             s.width = sws;
26251             s.height = shs;
26252             if(!Roo.isIE){
26253                 var cn = d.childNodes;
26254                 var sww = Math.max(0, (sw-12))+"px";
26255                 cn[0].childNodes[1].style.width = sww;
26256                 cn[1].childNodes[1].style.width = sww;
26257                 cn[2].childNodes[1].style.width = sww;
26258                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26259             }
26260         }
26261     },
26262
26263     /**
26264      * Hides this shadow
26265      */
26266     hide : function(){
26267         if(this.el){
26268             this.el.dom.style.display = "none";
26269             Roo.Shadow.Pool.push(this.el);
26270             delete this.el;
26271         }
26272     },
26273
26274     /**
26275      * Adjust the z-index of this shadow
26276      * @param {Number} zindex The new z-index
26277      */
26278     setZIndex : function(z){
26279         this.zIndex = z;
26280         if(this.el){
26281             this.el.setStyle("z-index", z);
26282         }
26283     }
26284 };
26285
26286 // Private utility class that manages the internal Shadow cache
26287 Roo.Shadow.Pool = function(){
26288     var p = [];
26289     var markup = Roo.isIE ?
26290                  '<div class="x-ie-shadow"></div>' :
26291                  '<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>';
26292     return {
26293         pull : function(){
26294             var sh = p.shift();
26295             if(!sh){
26296                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26297                 sh.autoBoxAdjust = false;
26298             }
26299             return sh;
26300         },
26301
26302         push : function(sh){
26303             p.push(sh);
26304         }
26305     };
26306 }();/*
26307  * Based on:
26308  * Ext JS Library 1.1.1
26309  * Copyright(c) 2006-2007, Ext JS, LLC.
26310  *
26311  * Originally Released Under LGPL - original licence link has changed is not relivant.
26312  *
26313  * Fork - LGPL
26314  * <script type="text/javascript">
26315  */
26316
26317
26318 /**
26319  * @class Roo.SplitBar
26320  * @extends Roo.util.Observable
26321  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26322  * <br><br>
26323  * Usage:
26324  * <pre><code>
26325 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26326                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26327 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26328 split.minSize = 100;
26329 split.maxSize = 600;
26330 split.animate = true;
26331 split.on('moved', splitterMoved);
26332 </code></pre>
26333  * @constructor
26334  * Create a new SplitBar
26335  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26336  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26337  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26338  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26339                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26340                         position of the SplitBar).
26341  */
26342 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26343     
26344     /** @private */
26345     this.el = Roo.get(dragElement, true);
26346     this.el.dom.unselectable = "on";
26347     /** @private */
26348     this.resizingEl = Roo.get(resizingElement, true);
26349
26350     /**
26351      * @private
26352      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26353      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26354      * @type Number
26355      */
26356     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26357     
26358     /**
26359      * The minimum size of the resizing element. (Defaults to 0)
26360      * @type Number
26361      */
26362     this.minSize = 0;
26363     
26364     /**
26365      * The maximum size of the resizing element. (Defaults to 2000)
26366      * @type Number
26367      */
26368     this.maxSize = 2000;
26369     
26370     /**
26371      * Whether to animate the transition to the new size
26372      * @type Boolean
26373      */
26374     this.animate = false;
26375     
26376     /**
26377      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26378      * @type Boolean
26379      */
26380     this.useShim = false;
26381     
26382     /** @private */
26383     this.shim = null;
26384     
26385     if(!existingProxy){
26386         /** @private */
26387         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26388     }else{
26389         this.proxy = Roo.get(existingProxy).dom;
26390     }
26391     /** @private */
26392     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26393     
26394     /** @private */
26395     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26396     
26397     /** @private */
26398     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26399     
26400     /** @private */
26401     this.dragSpecs = {};
26402     
26403     /**
26404      * @private The adapter to use to positon and resize elements
26405      */
26406     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26407     this.adapter.init(this);
26408     
26409     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26410         /** @private */
26411         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26412         this.el.addClass("x-splitbar-h");
26413     }else{
26414         /** @private */
26415         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26416         this.el.addClass("x-splitbar-v");
26417     }
26418     
26419     this.addEvents({
26420         /**
26421          * @event resize
26422          * Fires when the splitter is moved (alias for {@link #event-moved})
26423          * @param {Roo.SplitBar} this
26424          * @param {Number} newSize the new width or height
26425          */
26426         "resize" : true,
26427         /**
26428          * @event moved
26429          * Fires when the splitter is moved
26430          * @param {Roo.SplitBar} this
26431          * @param {Number} newSize the new width or height
26432          */
26433         "moved" : true,
26434         /**
26435          * @event beforeresize
26436          * Fires before the splitter is dragged
26437          * @param {Roo.SplitBar} this
26438          */
26439         "beforeresize" : true,
26440
26441         "beforeapply" : true
26442     });
26443
26444     Roo.util.Observable.call(this);
26445 };
26446
26447 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26448     onStartProxyDrag : function(x, y){
26449         this.fireEvent("beforeresize", this);
26450         if(!this.overlay){
26451             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26452             o.unselectable();
26453             o.enableDisplayMode("block");
26454             // all splitbars share the same overlay
26455             Roo.SplitBar.prototype.overlay = o;
26456         }
26457         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26458         this.overlay.show();
26459         Roo.get(this.proxy).setDisplayed("block");
26460         var size = this.adapter.getElementSize(this);
26461         this.activeMinSize = this.getMinimumSize();;
26462         this.activeMaxSize = this.getMaximumSize();;
26463         var c1 = size - this.activeMinSize;
26464         var c2 = Math.max(this.activeMaxSize - size, 0);
26465         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26466             this.dd.resetConstraints();
26467             this.dd.setXConstraint(
26468                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26469                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26470             );
26471             this.dd.setYConstraint(0, 0);
26472         }else{
26473             this.dd.resetConstraints();
26474             this.dd.setXConstraint(0, 0);
26475             this.dd.setYConstraint(
26476                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26477                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26478             );
26479          }
26480         this.dragSpecs.startSize = size;
26481         this.dragSpecs.startPoint = [x, y];
26482         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26483     },
26484     
26485     /** 
26486      * @private Called after the drag operation by the DDProxy
26487      */
26488     onEndProxyDrag : function(e){
26489         Roo.get(this.proxy).setDisplayed(false);
26490         var endPoint = Roo.lib.Event.getXY(e);
26491         if(this.overlay){
26492             this.overlay.hide();
26493         }
26494         var newSize;
26495         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26496             newSize = this.dragSpecs.startSize + 
26497                 (this.placement == Roo.SplitBar.LEFT ?
26498                     endPoint[0] - this.dragSpecs.startPoint[0] :
26499                     this.dragSpecs.startPoint[0] - endPoint[0]
26500                 );
26501         }else{
26502             newSize = this.dragSpecs.startSize + 
26503                 (this.placement == Roo.SplitBar.TOP ?
26504                     endPoint[1] - this.dragSpecs.startPoint[1] :
26505                     this.dragSpecs.startPoint[1] - endPoint[1]
26506                 );
26507         }
26508         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26509         if(newSize != this.dragSpecs.startSize){
26510             if(this.fireEvent('beforeapply', this, newSize) !== false){
26511                 this.adapter.setElementSize(this, newSize);
26512                 this.fireEvent("moved", this, newSize);
26513                 this.fireEvent("resize", this, newSize);
26514             }
26515         }
26516     },
26517     
26518     /**
26519      * Get the adapter this SplitBar uses
26520      * @return The adapter object
26521      */
26522     getAdapter : function(){
26523         return this.adapter;
26524     },
26525     
26526     /**
26527      * Set the adapter this SplitBar uses
26528      * @param {Object} adapter A SplitBar adapter object
26529      */
26530     setAdapter : function(adapter){
26531         this.adapter = adapter;
26532         this.adapter.init(this);
26533     },
26534     
26535     /**
26536      * Gets the minimum size for the resizing element
26537      * @return {Number} The minimum size
26538      */
26539     getMinimumSize : function(){
26540         return this.minSize;
26541     },
26542     
26543     /**
26544      * Sets the minimum size for the resizing element
26545      * @param {Number} minSize The minimum size
26546      */
26547     setMinimumSize : function(minSize){
26548         this.minSize = minSize;
26549     },
26550     
26551     /**
26552      * Gets the maximum size for the resizing element
26553      * @return {Number} The maximum size
26554      */
26555     getMaximumSize : function(){
26556         return this.maxSize;
26557     },
26558     
26559     /**
26560      * Sets the maximum size for the resizing element
26561      * @param {Number} maxSize The maximum size
26562      */
26563     setMaximumSize : function(maxSize){
26564         this.maxSize = maxSize;
26565     },
26566     
26567     /**
26568      * Sets the initialize size for the resizing element
26569      * @param {Number} size The initial size
26570      */
26571     setCurrentSize : function(size){
26572         var oldAnimate = this.animate;
26573         this.animate = false;
26574         this.adapter.setElementSize(this, size);
26575         this.animate = oldAnimate;
26576     },
26577     
26578     /**
26579      * Destroy this splitbar. 
26580      * @param {Boolean} removeEl True to remove the element
26581      */
26582     destroy : function(removeEl){
26583         if(this.shim){
26584             this.shim.remove();
26585         }
26586         this.dd.unreg();
26587         this.proxy.parentNode.removeChild(this.proxy);
26588         if(removeEl){
26589             this.el.remove();
26590         }
26591     }
26592 });
26593
26594 /**
26595  * @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.
26596  */
26597 Roo.SplitBar.createProxy = function(dir){
26598     var proxy = new Roo.Element(document.createElement("div"));
26599     proxy.unselectable();
26600     var cls = 'x-splitbar-proxy';
26601     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26602     document.body.appendChild(proxy.dom);
26603     return proxy.dom;
26604 };
26605
26606 /** 
26607  * @class Roo.SplitBar.BasicLayoutAdapter
26608  * Default Adapter. It assumes the splitter and resizing element are not positioned
26609  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26610  */
26611 Roo.SplitBar.BasicLayoutAdapter = function(){
26612 };
26613
26614 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26615     // do nothing for now
26616     init : function(s){
26617     
26618     },
26619     /**
26620      * Called before drag operations to get the current size of the resizing element. 
26621      * @param {Roo.SplitBar} s The SplitBar using this adapter
26622      */
26623      getElementSize : function(s){
26624         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26625             return s.resizingEl.getWidth();
26626         }else{
26627             return s.resizingEl.getHeight();
26628         }
26629     },
26630     
26631     /**
26632      * Called after drag operations to set the size of the resizing element.
26633      * @param {Roo.SplitBar} s The SplitBar using this adapter
26634      * @param {Number} newSize The new size to set
26635      * @param {Function} onComplete A function to be invoked when resizing is complete
26636      */
26637     setElementSize : function(s, newSize, onComplete){
26638         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26639             if(!s.animate){
26640                 s.resizingEl.setWidth(newSize);
26641                 if(onComplete){
26642                     onComplete(s, newSize);
26643                 }
26644             }else{
26645                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26646             }
26647         }else{
26648             
26649             if(!s.animate){
26650                 s.resizingEl.setHeight(newSize);
26651                 if(onComplete){
26652                     onComplete(s, newSize);
26653                 }
26654             }else{
26655                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26656             }
26657         }
26658     }
26659 };
26660
26661 /** 
26662  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26663  * @extends Roo.SplitBar.BasicLayoutAdapter
26664  * Adapter that  moves the splitter element to align with the resized sizing element. 
26665  * Used with an absolute positioned SplitBar.
26666  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26667  * document.body, make sure you assign an id to the body element.
26668  */
26669 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26670     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26671     this.container = Roo.get(container);
26672 };
26673
26674 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26675     init : function(s){
26676         this.basic.init(s);
26677     },
26678     
26679     getElementSize : function(s){
26680         return this.basic.getElementSize(s);
26681     },
26682     
26683     setElementSize : function(s, newSize, onComplete){
26684         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26685     },
26686     
26687     moveSplitter : function(s){
26688         var yes = Roo.SplitBar;
26689         switch(s.placement){
26690             case yes.LEFT:
26691                 s.el.setX(s.resizingEl.getRight());
26692                 break;
26693             case yes.RIGHT:
26694                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26695                 break;
26696             case yes.TOP:
26697                 s.el.setY(s.resizingEl.getBottom());
26698                 break;
26699             case yes.BOTTOM:
26700                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26701                 break;
26702         }
26703     }
26704 };
26705
26706 /**
26707  * Orientation constant - Create a vertical SplitBar
26708  * @static
26709  * @type Number
26710  */
26711 Roo.SplitBar.VERTICAL = 1;
26712
26713 /**
26714  * Orientation constant - Create a horizontal SplitBar
26715  * @static
26716  * @type Number
26717  */
26718 Roo.SplitBar.HORIZONTAL = 2;
26719
26720 /**
26721  * Placement constant - The resizing element is to the left of the splitter element
26722  * @static
26723  * @type Number
26724  */
26725 Roo.SplitBar.LEFT = 1;
26726
26727 /**
26728  * Placement constant - The resizing element is to the right of the splitter element
26729  * @static
26730  * @type Number
26731  */
26732 Roo.SplitBar.RIGHT = 2;
26733
26734 /**
26735  * Placement constant - The resizing element is positioned above the splitter element
26736  * @static
26737  * @type Number
26738  */
26739 Roo.SplitBar.TOP = 3;
26740
26741 /**
26742  * Placement constant - The resizing element is positioned under splitter element
26743  * @static
26744  * @type Number
26745  */
26746 Roo.SplitBar.BOTTOM = 4;
26747 /*
26748  * Based on:
26749  * Ext JS Library 1.1.1
26750  * Copyright(c) 2006-2007, Ext JS, LLC.
26751  *
26752  * Originally Released Under LGPL - original licence link has changed is not relivant.
26753  *
26754  * Fork - LGPL
26755  * <script type="text/javascript">
26756  */
26757
26758 /**
26759  * @class Roo.View
26760  * @extends Roo.util.Observable
26761  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26762  * This class also supports single and multi selection modes. <br>
26763  * Create a data model bound view:
26764  <pre><code>
26765  var store = new Roo.data.Store(...);
26766
26767  var view = new Roo.View({
26768     el : "my-element",
26769     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26770  
26771     singleSelect: true,
26772     selectedClass: "ydataview-selected",
26773     store: store
26774  });
26775
26776  // listen for node click?
26777  view.on("click", function(vw, index, node, e){
26778  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26779  });
26780
26781  // load XML data
26782  dataModel.load("foobar.xml");
26783  </code></pre>
26784  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26785  * <br><br>
26786  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26787  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26788  * 
26789  * Note: old style constructor is still suported (container, template, config)
26790  * 
26791  * @constructor
26792  * Create a new View
26793  * @param {Object} config The config object
26794  * 
26795  */
26796 Roo.View = function(config, depreciated_tpl, depreciated_config){
26797     
26798     this.parent = false;
26799     
26800     if (typeof(depreciated_tpl) == 'undefined') {
26801         // new way.. - universal constructor.
26802         Roo.apply(this, config);
26803         this.el  = Roo.get(this.el);
26804     } else {
26805         // old format..
26806         this.el  = Roo.get(config);
26807         this.tpl = depreciated_tpl;
26808         Roo.apply(this, depreciated_config);
26809     }
26810     this.wrapEl  = this.el.wrap().wrap();
26811     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26812     
26813     
26814     if(typeof(this.tpl) == "string"){
26815         this.tpl = new Roo.Template(this.tpl);
26816     } else {
26817         // support xtype ctors..
26818         this.tpl = new Roo.factory(this.tpl, Roo);
26819     }
26820     
26821     
26822     this.tpl.compile();
26823     
26824     /** @private */
26825     this.addEvents({
26826         /**
26827          * @event beforeclick
26828          * Fires before a click is processed. Returns false to cancel the default action.
26829          * @param {Roo.View} this
26830          * @param {Number} index The index of the target node
26831          * @param {HTMLElement} node The target node
26832          * @param {Roo.EventObject} e The raw event object
26833          */
26834             "beforeclick" : true,
26835         /**
26836          * @event click
26837          * Fires when a template node is clicked.
26838          * @param {Roo.View} this
26839          * @param {Number} index The index of the target node
26840          * @param {HTMLElement} node The target node
26841          * @param {Roo.EventObject} e The raw event object
26842          */
26843             "click" : true,
26844         /**
26845          * @event dblclick
26846          * Fires when a template node is double clicked.
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             "dblclick" : true,
26853         /**
26854          * @event contextmenu
26855          * Fires when a template node is right 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             "contextmenu" : true,
26862         /**
26863          * @event selectionchange
26864          * Fires when the selected nodes change.
26865          * @param {Roo.View} this
26866          * @param {Array} selections Array of the selected nodes
26867          */
26868             "selectionchange" : true,
26869     
26870         /**
26871          * @event beforeselect
26872          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26873          * @param {Roo.View} this
26874          * @param {HTMLElement} node The node to be selected
26875          * @param {Array} selections Array of currently selected nodes
26876          */
26877             "beforeselect" : true,
26878         /**
26879          * @event preparedata
26880          * Fires on every row to render, to allow you to change the data.
26881          * @param {Roo.View} this
26882          * @param {Object} data to be rendered (change this)
26883          */
26884           "preparedata" : true
26885           
26886           
26887         });
26888
26889
26890
26891     this.el.on({
26892         "click": this.onClick,
26893         "dblclick": this.onDblClick,
26894         "contextmenu": this.onContextMenu,
26895         scope:this
26896     });
26897
26898     this.selections = [];
26899     this.nodes = [];
26900     this.cmp = new Roo.CompositeElementLite([]);
26901     if(this.store){
26902         this.store = Roo.factory(this.store, Roo.data);
26903         this.setStore(this.store, true);
26904     }
26905     
26906     if ( this.footer && this.footer.xtype) {
26907            
26908          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26909         
26910         this.footer.dataSource = this.store;
26911         this.footer.container = fctr;
26912         this.footer = Roo.factory(this.footer, Roo);
26913         fctr.insertFirst(this.el);
26914         
26915         // this is a bit insane - as the paging toolbar seems to detach the el..
26916 //        dom.parentNode.parentNode.parentNode
26917          // they get detached?
26918     }
26919     
26920     
26921     Roo.View.superclass.constructor.call(this);
26922     
26923     
26924 };
26925
26926 Roo.extend(Roo.View, Roo.util.Observable, {
26927     
26928      /**
26929      * @cfg {Roo.data.Store} store Data store to load data from.
26930      */
26931     store : false,
26932     
26933     /**
26934      * @cfg {String|Roo.Element} el The container element.
26935      */
26936     el : '',
26937     
26938     /**
26939      * @cfg {String|Roo.Template} tpl The template used by this View 
26940      */
26941     tpl : false,
26942     /**
26943      * @cfg {String} dataName the named area of the template to use as the data area
26944      *                          Works with domtemplates roo-name="name"
26945      */
26946     dataName: false,
26947     /**
26948      * @cfg {String} selectedClass The css class to add to selected nodes
26949      */
26950     selectedClass : "x-view-selected",
26951      /**
26952      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26953      */
26954     emptyText : "",
26955     
26956     /**
26957      * @cfg {String} text to display on mask (default Loading)
26958      */
26959     mask : false,
26960     /**
26961      * @cfg {Boolean} multiSelect Allow multiple selection
26962      */
26963     multiSelect : false,
26964     /**
26965      * @cfg {Boolean} singleSelect Allow single selection
26966      */
26967     singleSelect:  false,
26968     
26969     /**
26970      * @cfg {Boolean} toggleSelect - selecting 
26971      */
26972     toggleSelect : false,
26973     
26974     /**
26975      * @cfg {Boolean} tickable - selecting 
26976      */
26977     tickable : false,
26978     
26979     /**
26980      * Returns the element this view is bound to.
26981      * @return {Roo.Element}
26982      */
26983     getEl : function(){
26984         return this.wrapEl;
26985     },
26986     
26987     
26988
26989     /**
26990      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26991      */
26992     refresh : function(){
26993         //Roo.log('refresh');
26994         var t = this.tpl;
26995         
26996         // if we are using something like 'domtemplate', then
26997         // the what gets used is:
26998         // t.applySubtemplate(NAME, data, wrapping data..)
26999         // the outer template then get' applied with
27000         //     the store 'extra data'
27001         // and the body get's added to the
27002         //      roo-name="data" node?
27003         //      <span class='roo-tpl-{name}'></span> ?????
27004         
27005         
27006         
27007         this.clearSelections();
27008         this.el.update("");
27009         var html = [];
27010         var records = this.store.getRange();
27011         if(records.length < 1) {
27012             
27013             // is this valid??  = should it render a template??
27014             
27015             this.el.update(this.emptyText);
27016             return;
27017         }
27018         var el = this.el;
27019         if (this.dataName) {
27020             this.el.update(t.apply(this.store.meta)); //????
27021             el = this.el.child('.roo-tpl-' + this.dataName);
27022         }
27023         
27024         for(var i = 0, len = records.length; i < len; i++){
27025             var data = this.prepareData(records[i].data, i, records[i]);
27026             this.fireEvent("preparedata", this, data, i, records[i]);
27027             
27028             var d = Roo.apply({}, data);
27029             
27030             if(this.tickable){
27031                 Roo.apply(d, {'roo-id' : Roo.id()});
27032                 
27033                 var _this = this;
27034             
27035                 Roo.each(this.parent.item, function(item){
27036                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27037                         return;
27038                     }
27039                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27040                 });
27041             }
27042             
27043             html[html.length] = Roo.util.Format.trim(
27044                 this.dataName ?
27045                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27046                     t.apply(d)
27047             );
27048         }
27049         
27050         
27051         
27052         el.update(html.join(""));
27053         this.nodes = el.dom.childNodes;
27054         this.updateIndexes(0);
27055     },
27056     
27057
27058     /**
27059      * Function to override to reformat the data that is sent to
27060      * the template for each node.
27061      * DEPRICATED - use the preparedata event handler.
27062      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27063      * a JSON object for an UpdateManager bound view).
27064      */
27065     prepareData : function(data, index, record)
27066     {
27067         this.fireEvent("preparedata", this, data, index, record);
27068         return data;
27069     },
27070
27071     onUpdate : function(ds, record){
27072         // Roo.log('on update');   
27073         this.clearSelections();
27074         var index = this.store.indexOf(record);
27075         var n = this.nodes[index];
27076         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27077         n.parentNode.removeChild(n);
27078         this.updateIndexes(index, index);
27079     },
27080
27081     
27082     
27083 // --------- FIXME     
27084     onAdd : function(ds, records, index)
27085     {
27086         //Roo.log(['on Add', ds, records, index] );        
27087         this.clearSelections();
27088         if(this.nodes.length == 0){
27089             this.refresh();
27090             return;
27091         }
27092         var n = this.nodes[index];
27093         for(var i = 0, len = records.length; i < len; i++){
27094             var d = this.prepareData(records[i].data, i, records[i]);
27095             if(n){
27096                 this.tpl.insertBefore(n, d);
27097             }else{
27098                 
27099                 this.tpl.append(this.el, d);
27100             }
27101         }
27102         this.updateIndexes(index);
27103     },
27104
27105     onRemove : function(ds, record, index){
27106        // Roo.log('onRemove');
27107         this.clearSelections();
27108         var el = this.dataName  ?
27109             this.el.child('.roo-tpl-' + this.dataName) :
27110             this.el; 
27111         
27112         el.dom.removeChild(this.nodes[index]);
27113         this.updateIndexes(index);
27114     },
27115
27116     /**
27117      * Refresh an individual node.
27118      * @param {Number} index
27119      */
27120     refreshNode : function(index){
27121         this.onUpdate(this.store, this.store.getAt(index));
27122     },
27123
27124     updateIndexes : function(startIndex, endIndex){
27125         var ns = this.nodes;
27126         startIndex = startIndex || 0;
27127         endIndex = endIndex || ns.length - 1;
27128         for(var i = startIndex; i <= endIndex; i++){
27129             ns[i].nodeIndex = i;
27130         }
27131     },
27132
27133     /**
27134      * Changes the data store this view uses and refresh the view.
27135      * @param {Store} store
27136      */
27137     setStore : function(store, initial){
27138         if(!initial && this.store){
27139             this.store.un("datachanged", this.refresh);
27140             this.store.un("add", this.onAdd);
27141             this.store.un("remove", this.onRemove);
27142             this.store.un("update", this.onUpdate);
27143             this.store.un("clear", this.refresh);
27144             this.store.un("beforeload", this.onBeforeLoad);
27145             this.store.un("load", this.onLoad);
27146             this.store.un("loadexception", this.onLoad);
27147         }
27148         if(store){
27149           
27150             store.on("datachanged", this.refresh, this);
27151             store.on("add", this.onAdd, this);
27152             store.on("remove", this.onRemove, this);
27153             store.on("update", this.onUpdate, this);
27154             store.on("clear", this.refresh, this);
27155             store.on("beforeload", this.onBeforeLoad, this);
27156             store.on("load", this.onLoad, this);
27157             store.on("loadexception", this.onLoad, this);
27158         }
27159         
27160         if(store){
27161             this.refresh();
27162         }
27163     },
27164     /**
27165      * onbeforeLoad - masks the loading area.
27166      *
27167      */
27168     onBeforeLoad : function(store,opts)
27169     {
27170          //Roo.log('onBeforeLoad');   
27171         if (!opts.add) {
27172             this.el.update("");
27173         }
27174         this.el.mask(this.mask ? this.mask : "Loading" ); 
27175     },
27176     onLoad : function ()
27177     {
27178         this.el.unmask();
27179     },
27180     
27181
27182     /**
27183      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27184      * @param {HTMLElement} node
27185      * @return {HTMLElement} The template node
27186      */
27187     findItemFromChild : function(node){
27188         var el = this.dataName  ?
27189             this.el.child('.roo-tpl-' + this.dataName,true) :
27190             this.el.dom; 
27191         
27192         if(!node || node.parentNode == el){
27193                     return node;
27194             }
27195             var p = node.parentNode;
27196             while(p && p != el){
27197             if(p.parentNode == el){
27198                 return p;
27199             }
27200             p = p.parentNode;
27201         }
27202             return null;
27203     },
27204
27205     /** @ignore */
27206     onClick : function(e){
27207         var item = this.findItemFromChild(e.getTarget());
27208         if(item){
27209             var index = this.indexOf(item);
27210             if(this.onItemClick(item, index, e) !== false){
27211                 this.fireEvent("click", this, index, item, e);
27212             }
27213         }else{
27214             this.clearSelections();
27215         }
27216     },
27217
27218     /** @ignore */
27219     onContextMenu : function(e){
27220         var item = this.findItemFromChild(e.getTarget());
27221         if(item){
27222             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27223         }
27224     },
27225
27226     /** @ignore */
27227     onDblClick : function(e){
27228         var item = this.findItemFromChild(e.getTarget());
27229         if(item){
27230             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27231         }
27232     },
27233
27234     onItemClick : function(item, index, e)
27235     {
27236         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27237             return false;
27238         }
27239         if (this.toggleSelect) {
27240             var m = this.isSelected(item) ? 'unselect' : 'select';
27241             //Roo.log(m);
27242             var _t = this;
27243             _t[m](item, true, false);
27244             return true;
27245         }
27246         if(this.multiSelect || this.singleSelect){
27247             if(this.multiSelect && e.shiftKey && this.lastSelection){
27248                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27249             }else{
27250                 this.select(item, this.multiSelect && e.ctrlKey);
27251                 this.lastSelection = item;
27252             }
27253             
27254             if(!this.tickable){
27255                 e.preventDefault();
27256             }
27257             
27258         }
27259         return true;
27260     },
27261
27262     /**
27263      * Get the number of selected nodes.
27264      * @return {Number}
27265      */
27266     getSelectionCount : function(){
27267         return this.selections.length;
27268     },
27269
27270     /**
27271      * Get the currently selected nodes.
27272      * @return {Array} An array of HTMLElements
27273      */
27274     getSelectedNodes : function(){
27275         return this.selections;
27276     },
27277
27278     /**
27279      * Get the indexes of the selected nodes.
27280      * @return {Array}
27281      */
27282     getSelectedIndexes : function(){
27283         var indexes = [], s = this.selections;
27284         for(var i = 0, len = s.length; i < len; i++){
27285             indexes.push(s[i].nodeIndex);
27286         }
27287         return indexes;
27288     },
27289
27290     /**
27291      * Clear all selections
27292      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27293      */
27294     clearSelections : function(suppressEvent){
27295         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27296             this.cmp.elements = this.selections;
27297             this.cmp.removeClass(this.selectedClass);
27298             this.selections = [];
27299             if(!suppressEvent){
27300                 this.fireEvent("selectionchange", this, this.selections);
27301             }
27302         }
27303     },
27304
27305     /**
27306      * Returns true if the passed node is selected
27307      * @param {HTMLElement/Number} node The node or node index
27308      * @return {Boolean}
27309      */
27310     isSelected : function(node){
27311         var s = this.selections;
27312         if(s.length < 1){
27313             return false;
27314         }
27315         node = this.getNode(node);
27316         return s.indexOf(node) !== -1;
27317     },
27318
27319     /**
27320      * Selects nodes.
27321      * @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
27322      * @param {Boolean} keepExisting (optional) true to keep existing selections
27323      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27324      */
27325     select : function(nodeInfo, keepExisting, suppressEvent){
27326         if(nodeInfo instanceof Array){
27327             if(!keepExisting){
27328                 this.clearSelections(true);
27329             }
27330             for(var i = 0, len = nodeInfo.length; i < len; i++){
27331                 this.select(nodeInfo[i], true, true);
27332             }
27333             return;
27334         } 
27335         var node = this.getNode(nodeInfo);
27336         if(!node || this.isSelected(node)){
27337             return; // already selected.
27338         }
27339         if(!keepExisting){
27340             this.clearSelections(true);
27341         }
27342         
27343         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27344             Roo.fly(node).addClass(this.selectedClass);
27345             this.selections.push(node);
27346             if(!suppressEvent){
27347                 this.fireEvent("selectionchange", this, this.selections);
27348             }
27349         }
27350         
27351         
27352     },
27353       /**
27354      * Unselects nodes.
27355      * @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
27356      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27357      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27358      */
27359     unselect : function(nodeInfo, keepExisting, suppressEvent)
27360     {
27361         if(nodeInfo instanceof Array){
27362             Roo.each(this.selections, function(s) {
27363                 this.unselect(s, nodeInfo);
27364             }, this);
27365             return;
27366         }
27367         var node = this.getNode(nodeInfo);
27368         if(!node || !this.isSelected(node)){
27369             //Roo.log("not selected");
27370             return; // not selected.
27371         }
27372         // fireevent???
27373         var ns = [];
27374         Roo.each(this.selections, function(s) {
27375             if (s == node ) {
27376                 Roo.fly(node).removeClass(this.selectedClass);
27377
27378                 return;
27379             }
27380             ns.push(s);
27381         },this);
27382         
27383         this.selections= ns;
27384         this.fireEvent("selectionchange", this, this.selections);
27385     },
27386
27387     /**
27388      * Gets a template node.
27389      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27390      * @return {HTMLElement} The node or null if it wasn't found
27391      */
27392     getNode : function(nodeInfo){
27393         if(typeof nodeInfo == "string"){
27394             return document.getElementById(nodeInfo);
27395         }else if(typeof nodeInfo == "number"){
27396             return this.nodes[nodeInfo];
27397         }
27398         return nodeInfo;
27399     },
27400
27401     /**
27402      * Gets a range template nodes.
27403      * @param {Number} startIndex
27404      * @param {Number} endIndex
27405      * @return {Array} An array of nodes
27406      */
27407     getNodes : function(start, end){
27408         var ns = this.nodes;
27409         start = start || 0;
27410         end = typeof end == "undefined" ? ns.length - 1 : end;
27411         var nodes = [];
27412         if(start <= end){
27413             for(var i = start; i <= end; i++){
27414                 nodes.push(ns[i]);
27415             }
27416         } else{
27417             for(var i = start; i >= end; i--){
27418                 nodes.push(ns[i]);
27419             }
27420         }
27421         return nodes;
27422     },
27423
27424     /**
27425      * Finds the index of the passed node
27426      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27427      * @return {Number} The index of the node or -1
27428      */
27429     indexOf : function(node){
27430         node = this.getNode(node);
27431         if(typeof node.nodeIndex == "number"){
27432             return node.nodeIndex;
27433         }
27434         var ns = this.nodes;
27435         for(var i = 0, len = ns.length; i < len; i++){
27436             if(ns[i] == node){
27437                 return i;
27438             }
27439         }
27440         return -1;
27441     }
27442 });
27443 /*
27444  * Based on:
27445  * Ext JS Library 1.1.1
27446  * Copyright(c) 2006-2007, Ext JS, LLC.
27447  *
27448  * Originally Released Under LGPL - original licence link has changed is not relivant.
27449  *
27450  * Fork - LGPL
27451  * <script type="text/javascript">
27452  */
27453
27454 /**
27455  * @class Roo.JsonView
27456  * @extends Roo.View
27457  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27458 <pre><code>
27459 var view = new Roo.JsonView({
27460     container: "my-element",
27461     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27462     multiSelect: true, 
27463     jsonRoot: "data" 
27464 });
27465
27466 // listen for node click?
27467 view.on("click", function(vw, index, node, e){
27468     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27469 });
27470
27471 // direct load of JSON data
27472 view.load("foobar.php");
27473
27474 // Example from my blog list
27475 var tpl = new Roo.Template(
27476     '&lt;div class="entry"&gt;' +
27477     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27478     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27479     "&lt;/div&gt;&lt;hr /&gt;"
27480 );
27481
27482 var moreView = new Roo.JsonView({
27483     container :  "entry-list", 
27484     template : tpl,
27485     jsonRoot: "posts"
27486 });
27487 moreView.on("beforerender", this.sortEntries, this);
27488 moreView.load({
27489     url: "/blog/get-posts.php",
27490     params: "allposts=true",
27491     text: "Loading Blog Entries..."
27492 });
27493 </code></pre>
27494
27495 * Note: old code is supported with arguments : (container, template, config)
27496
27497
27498  * @constructor
27499  * Create a new JsonView
27500  * 
27501  * @param {Object} config The config object
27502  * 
27503  */
27504 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27505     
27506     
27507     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27508
27509     var um = this.el.getUpdateManager();
27510     um.setRenderer(this);
27511     um.on("update", this.onLoad, this);
27512     um.on("failure", this.onLoadException, this);
27513
27514     /**
27515      * @event beforerender
27516      * Fires before rendering of the downloaded JSON data.
27517      * @param {Roo.JsonView} this
27518      * @param {Object} data The JSON data loaded
27519      */
27520     /**
27521      * @event load
27522      * Fires when data is loaded.
27523      * @param {Roo.JsonView} this
27524      * @param {Object} data The JSON data loaded
27525      * @param {Object} response The raw Connect response object
27526      */
27527     /**
27528      * @event loadexception
27529      * Fires when loading fails.
27530      * @param {Roo.JsonView} this
27531      * @param {Object} response The raw Connect response object
27532      */
27533     this.addEvents({
27534         'beforerender' : true,
27535         'load' : true,
27536         'loadexception' : true
27537     });
27538 };
27539 Roo.extend(Roo.JsonView, Roo.View, {
27540     /**
27541      * @type {String} The root property in the loaded JSON object that contains the data
27542      */
27543     jsonRoot : "",
27544
27545     /**
27546      * Refreshes the view.
27547      */
27548     refresh : function(){
27549         this.clearSelections();
27550         this.el.update("");
27551         var html = [];
27552         var o = this.jsonData;
27553         if(o && o.length > 0){
27554             for(var i = 0, len = o.length; i < len; i++){
27555                 var data = this.prepareData(o[i], i, o);
27556                 html[html.length] = this.tpl.apply(data);
27557             }
27558         }else{
27559             html.push(this.emptyText);
27560         }
27561         this.el.update(html.join(""));
27562         this.nodes = this.el.dom.childNodes;
27563         this.updateIndexes(0);
27564     },
27565
27566     /**
27567      * 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.
27568      * @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:
27569      <pre><code>
27570      view.load({
27571          url: "your-url.php",
27572          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27573          callback: yourFunction,
27574          scope: yourObject, //(optional scope)
27575          discardUrl: false,
27576          nocache: false,
27577          text: "Loading...",
27578          timeout: 30,
27579          scripts: false
27580      });
27581      </code></pre>
27582      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27583      * 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.
27584      * @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}
27585      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27586      * @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.
27587      */
27588     load : function(){
27589         var um = this.el.getUpdateManager();
27590         um.update.apply(um, arguments);
27591     },
27592
27593     // note - render is a standard framework call...
27594     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27595     render : function(el, response){
27596         
27597         this.clearSelections();
27598         this.el.update("");
27599         var o;
27600         try{
27601             if (response != '') {
27602                 o = Roo.util.JSON.decode(response.responseText);
27603                 if(this.jsonRoot){
27604                     
27605                     o = o[this.jsonRoot];
27606                 }
27607             }
27608         } catch(e){
27609         }
27610         /**
27611          * The current JSON data or null
27612          */
27613         this.jsonData = o;
27614         this.beforeRender();
27615         this.refresh();
27616     },
27617
27618 /**
27619  * Get the number of records in the current JSON dataset
27620  * @return {Number}
27621  */
27622     getCount : function(){
27623         return this.jsonData ? this.jsonData.length : 0;
27624     },
27625
27626 /**
27627  * Returns the JSON object for the specified node(s)
27628  * @param {HTMLElement/Array} node The node or an array of nodes
27629  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27630  * you get the JSON object for the node
27631  */
27632     getNodeData : function(node){
27633         if(node instanceof Array){
27634             var data = [];
27635             for(var i = 0, len = node.length; i < len; i++){
27636                 data.push(this.getNodeData(node[i]));
27637             }
27638             return data;
27639         }
27640         return this.jsonData[this.indexOf(node)] || null;
27641     },
27642
27643     beforeRender : function(){
27644         this.snapshot = this.jsonData;
27645         if(this.sortInfo){
27646             this.sort.apply(this, this.sortInfo);
27647         }
27648         this.fireEvent("beforerender", this, this.jsonData);
27649     },
27650
27651     onLoad : function(el, o){
27652         this.fireEvent("load", this, this.jsonData, o);
27653     },
27654
27655     onLoadException : function(el, o){
27656         this.fireEvent("loadexception", this, o);
27657     },
27658
27659 /**
27660  * Filter the data by a specific property.
27661  * @param {String} property A property on your JSON objects
27662  * @param {String/RegExp} value Either string that the property values
27663  * should start with, or a RegExp to test against the property
27664  */
27665     filter : function(property, value){
27666         if(this.jsonData){
27667             var data = [];
27668             var ss = this.snapshot;
27669             if(typeof value == "string"){
27670                 var vlen = value.length;
27671                 if(vlen == 0){
27672                     this.clearFilter();
27673                     return;
27674                 }
27675                 value = value.toLowerCase();
27676                 for(var i = 0, len = ss.length; i < len; i++){
27677                     var o = ss[i];
27678                     if(o[property].substr(0, vlen).toLowerCase() == value){
27679                         data.push(o);
27680                     }
27681                 }
27682             } else if(value.exec){ // regex?
27683                 for(var i = 0, len = ss.length; i < len; i++){
27684                     var o = ss[i];
27685                     if(value.test(o[property])){
27686                         data.push(o);
27687                     }
27688                 }
27689             } else{
27690                 return;
27691             }
27692             this.jsonData = data;
27693             this.refresh();
27694         }
27695     },
27696
27697 /**
27698  * Filter by a function. The passed function will be called with each
27699  * object in the current dataset. If the function returns true the value is kept,
27700  * otherwise it is filtered.
27701  * @param {Function} fn
27702  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27703  */
27704     filterBy : function(fn, scope){
27705         if(this.jsonData){
27706             var data = [];
27707             var ss = this.snapshot;
27708             for(var i = 0, len = ss.length; i < len; i++){
27709                 var o = ss[i];
27710                 if(fn.call(scope || this, o)){
27711                     data.push(o);
27712                 }
27713             }
27714             this.jsonData = data;
27715             this.refresh();
27716         }
27717     },
27718
27719 /**
27720  * Clears the current filter.
27721  */
27722     clearFilter : function(){
27723         if(this.snapshot && this.jsonData != this.snapshot){
27724             this.jsonData = this.snapshot;
27725             this.refresh();
27726         }
27727     },
27728
27729
27730 /**
27731  * Sorts the data for this view and refreshes it.
27732  * @param {String} property A property on your JSON objects to sort on
27733  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27734  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27735  */
27736     sort : function(property, dir, sortType){
27737         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27738         if(this.jsonData){
27739             var p = property;
27740             var dsc = dir && dir.toLowerCase() == "desc";
27741             var f = function(o1, o2){
27742                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27743                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27744                 ;
27745                 if(v1 < v2){
27746                     return dsc ? +1 : -1;
27747                 } else if(v1 > v2){
27748                     return dsc ? -1 : +1;
27749                 } else{
27750                     return 0;
27751                 }
27752             };
27753             this.jsonData.sort(f);
27754             this.refresh();
27755             if(this.jsonData != this.snapshot){
27756                 this.snapshot.sort(f);
27757             }
27758         }
27759     }
27760 });/*
27761  * Based on:
27762  * Ext JS Library 1.1.1
27763  * Copyright(c) 2006-2007, Ext JS, LLC.
27764  *
27765  * Originally Released Under LGPL - original licence link has changed is not relivant.
27766  *
27767  * Fork - LGPL
27768  * <script type="text/javascript">
27769  */
27770  
27771
27772 /**
27773  * @class Roo.ColorPalette
27774  * @extends Roo.Component
27775  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27776  * Here's an example of typical usage:
27777  * <pre><code>
27778 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27779 cp.render('my-div');
27780
27781 cp.on('select', function(palette, selColor){
27782     // do something with selColor
27783 });
27784 </code></pre>
27785  * @constructor
27786  * Create a new ColorPalette
27787  * @param {Object} config The config object
27788  */
27789 Roo.ColorPalette = function(config){
27790     Roo.ColorPalette.superclass.constructor.call(this, config);
27791     this.addEvents({
27792         /**
27793              * @event select
27794              * Fires when a color is selected
27795              * @param {ColorPalette} this
27796              * @param {String} color The 6-digit color hex code (without the # symbol)
27797              */
27798         select: true
27799     });
27800
27801     if(this.handler){
27802         this.on("select", this.handler, this.scope, true);
27803     }
27804 };
27805 Roo.extend(Roo.ColorPalette, Roo.Component, {
27806     /**
27807      * @cfg {String} itemCls
27808      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27809      */
27810     itemCls : "x-color-palette",
27811     /**
27812      * @cfg {String} value
27813      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27814      * the hex codes are case-sensitive.
27815      */
27816     value : null,
27817     clickEvent:'click',
27818     // private
27819     ctype: "Roo.ColorPalette",
27820
27821     /**
27822      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27823      */
27824     allowReselect : false,
27825
27826     /**
27827      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27828      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27829      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27830      * of colors with the width setting until the box is symmetrical.</p>
27831      * <p>You can override individual colors if needed:</p>
27832      * <pre><code>
27833 var cp = new Roo.ColorPalette();
27834 cp.colors[0] = "FF0000";  // change the first box to red
27835 </code></pre>
27836
27837 Or you can provide a custom array of your own for complete control:
27838 <pre><code>
27839 var cp = new Roo.ColorPalette();
27840 cp.colors = ["000000", "993300", "333300"];
27841 </code></pre>
27842      * @type Array
27843      */
27844     colors : [
27845         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27846         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27847         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27848         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27849         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27850     ],
27851
27852     // private
27853     onRender : function(container, position){
27854         var t = new Roo.MasterTemplate(
27855             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27856         );
27857         var c = this.colors;
27858         for(var i = 0, len = c.length; i < len; i++){
27859             t.add([c[i]]);
27860         }
27861         var el = document.createElement("div");
27862         el.className = this.itemCls;
27863         t.overwrite(el);
27864         container.dom.insertBefore(el, position);
27865         this.el = Roo.get(el);
27866         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27867         if(this.clickEvent != 'click'){
27868             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27869         }
27870     },
27871
27872     // private
27873     afterRender : function(){
27874         Roo.ColorPalette.superclass.afterRender.call(this);
27875         if(this.value){
27876             var s = this.value;
27877             this.value = null;
27878             this.select(s);
27879         }
27880     },
27881
27882     // private
27883     handleClick : function(e, t){
27884         e.preventDefault();
27885         if(!this.disabled){
27886             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27887             this.select(c.toUpperCase());
27888         }
27889     },
27890
27891     /**
27892      * Selects the specified color in the palette (fires the select event)
27893      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27894      */
27895     select : function(color){
27896         color = color.replace("#", "");
27897         if(color != this.value || this.allowReselect){
27898             var el = this.el;
27899             if(this.value){
27900                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27901             }
27902             el.child("a.color-"+color).addClass("x-color-palette-sel");
27903             this.value = color;
27904             this.fireEvent("select", this, color);
27905         }
27906     }
27907 });/*
27908  * Based on:
27909  * Ext JS Library 1.1.1
27910  * Copyright(c) 2006-2007, Ext JS, LLC.
27911  *
27912  * Originally Released Under LGPL - original licence link has changed is not relivant.
27913  *
27914  * Fork - LGPL
27915  * <script type="text/javascript">
27916  */
27917  
27918 /**
27919  * @class Roo.DatePicker
27920  * @extends Roo.Component
27921  * Simple date picker class.
27922  * @constructor
27923  * Create a new DatePicker
27924  * @param {Object} config The config object
27925  */
27926 Roo.DatePicker = function(config){
27927     Roo.DatePicker.superclass.constructor.call(this, config);
27928
27929     this.value = config && config.value ?
27930                  config.value.clearTime() : new Date().clearTime();
27931
27932     this.addEvents({
27933         /**
27934              * @event select
27935              * Fires when a date is selected
27936              * @param {DatePicker} this
27937              * @param {Date} date The selected date
27938              */
27939         'select': true,
27940         /**
27941              * @event monthchange
27942              * Fires when the displayed month changes 
27943              * @param {DatePicker} this
27944              * @param {Date} date The selected month
27945              */
27946         'monthchange': true
27947     });
27948
27949     if(this.handler){
27950         this.on("select", this.handler,  this.scope || this);
27951     }
27952     // build the disabledDatesRE
27953     if(!this.disabledDatesRE && this.disabledDates){
27954         var dd = this.disabledDates;
27955         var re = "(?:";
27956         for(var i = 0; i < dd.length; i++){
27957             re += dd[i];
27958             if(i != dd.length-1) {
27959                 re += "|";
27960             }
27961         }
27962         this.disabledDatesRE = new RegExp(re + ")");
27963     }
27964 };
27965
27966 Roo.extend(Roo.DatePicker, Roo.Component, {
27967     /**
27968      * @cfg {String} todayText
27969      * The text to display on the button that selects the current date (defaults to "Today")
27970      */
27971     todayText : "Today",
27972     /**
27973      * @cfg {String} okText
27974      * The text to display on the ok button
27975      */
27976     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27977     /**
27978      * @cfg {String} cancelText
27979      * The text to display on the cancel button
27980      */
27981     cancelText : "Cancel",
27982     /**
27983      * @cfg {String} todayTip
27984      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27985      */
27986     todayTip : "{0} (Spacebar)",
27987     /**
27988      * @cfg {Date} minDate
27989      * Minimum allowable date (JavaScript date object, defaults to null)
27990      */
27991     minDate : null,
27992     /**
27993      * @cfg {Date} maxDate
27994      * Maximum allowable date (JavaScript date object, defaults to null)
27995      */
27996     maxDate : null,
27997     /**
27998      * @cfg {String} minText
27999      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28000      */
28001     minText : "This date is before the minimum date",
28002     /**
28003      * @cfg {String} maxText
28004      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28005      */
28006     maxText : "This date is after the maximum date",
28007     /**
28008      * @cfg {String} format
28009      * The default date format string which can be overriden for localization support.  The format must be
28010      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28011      */
28012     format : "m/d/y",
28013     /**
28014      * @cfg {Array} disabledDays
28015      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28016      */
28017     disabledDays : null,
28018     /**
28019      * @cfg {String} disabledDaysText
28020      * The tooltip to display when the date falls on a disabled day (defaults to "")
28021      */
28022     disabledDaysText : "",
28023     /**
28024      * @cfg {RegExp} disabledDatesRE
28025      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28026      */
28027     disabledDatesRE : null,
28028     /**
28029      * @cfg {String} disabledDatesText
28030      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28031      */
28032     disabledDatesText : "",
28033     /**
28034      * @cfg {Boolean} constrainToViewport
28035      * True to constrain the date picker to the viewport (defaults to true)
28036      */
28037     constrainToViewport : true,
28038     /**
28039      * @cfg {Array} monthNames
28040      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28041      */
28042     monthNames : Date.monthNames,
28043     /**
28044      * @cfg {Array} dayNames
28045      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28046      */
28047     dayNames : Date.dayNames,
28048     /**
28049      * @cfg {String} nextText
28050      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28051      */
28052     nextText: 'Next Month (Control+Right)',
28053     /**
28054      * @cfg {String} prevText
28055      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28056      */
28057     prevText: 'Previous Month (Control+Left)',
28058     /**
28059      * @cfg {String} monthYearText
28060      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28061      */
28062     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28063     /**
28064      * @cfg {Number} startDay
28065      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28066      */
28067     startDay : 0,
28068     /**
28069      * @cfg {Bool} showClear
28070      * Show a clear button (usefull for date form elements that can be blank.)
28071      */
28072     
28073     showClear: false,
28074     
28075     /**
28076      * Sets the value of the date field
28077      * @param {Date} value The date to set
28078      */
28079     setValue : function(value){
28080         var old = this.value;
28081         
28082         if (typeof(value) == 'string') {
28083          
28084             value = Date.parseDate(value, this.format);
28085         }
28086         if (!value) {
28087             value = new Date();
28088         }
28089         
28090         this.value = value.clearTime(true);
28091         if(this.el){
28092             this.update(this.value);
28093         }
28094     },
28095
28096     /**
28097      * Gets the current selected value of the date field
28098      * @return {Date} The selected date
28099      */
28100     getValue : function(){
28101         return this.value;
28102     },
28103
28104     // private
28105     focus : function(){
28106         if(this.el){
28107             this.update(this.activeDate);
28108         }
28109     },
28110
28111     // privateval
28112     onRender : function(container, position){
28113         
28114         var m = [
28115              '<table cellspacing="0">',
28116                 '<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>',
28117                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28118         var dn = this.dayNames;
28119         for(var i = 0; i < 7; i++){
28120             var d = this.startDay+i;
28121             if(d > 6){
28122                 d = d-7;
28123             }
28124             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28125         }
28126         m[m.length] = "</tr></thead><tbody><tr>";
28127         for(var i = 0; i < 42; i++) {
28128             if(i % 7 == 0 && i != 0){
28129                 m[m.length] = "</tr><tr>";
28130             }
28131             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28132         }
28133         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28134             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28135
28136         var el = document.createElement("div");
28137         el.className = "x-date-picker";
28138         el.innerHTML = m.join("");
28139
28140         container.dom.insertBefore(el, position);
28141
28142         this.el = Roo.get(el);
28143         this.eventEl = Roo.get(el.firstChild);
28144
28145         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28146             handler: this.showPrevMonth,
28147             scope: this,
28148             preventDefault:true,
28149             stopDefault:true
28150         });
28151
28152         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28153             handler: this.showNextMonth,
28154             scope: this,
28155             preventDefault:true,
28156             stopDefault:true
28157         });
28158
28159         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28160
28161         this.monthPicker = this.el.down('div.x-date-mp');
28162         this.monthPicker.enableDisplayMode('block');
28163         
28164         var kn = new Roo.KeyNav(this.eventEl, {
28165             "left" : function(e){
28166                 e.ctrlKey ?
28167                     this.showPrevMonth() :
28168                     this.update(this.activeDate.add("d", -1));
28169             },
28170
28171             "right" : function(e){
28172                 e.ctrlKey ?
28173                     this.showNextMonth() :
28174                     this.update(this.activeDate.add("d", 1));
28175             },
28176
28177             "up" : function(e){
28178                 e.ctrlKey ?
28179                     this.showNextYear() :
28180                     this.update(this.activeDate.add("d", -7));
28181             },
28182
28183             "down" : function(e){
28184                 e.ctrlKey ?
28185                     this.showPrevYear() :
28186                     this.update(this.activeDate.add("d", 7));
28187             },
28188
28189             "pageUp" : function(e){
28190                 this.showNextMonth();
28191             },
28192
28193             "pageDown" : function(e){
28194                 this.showPrevMonth();
28195             },
28196
28197             "enter" : function(e){
28198                 e.stopPropagation();
28199                 return true;
28200             },
28201
28202             scope : this
28203         });
28204
28205         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28206
28207         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28208
28209         this.el.unselectable();
28210         
28211         this.cells = this.el.select("table.x-date-inner tbody td");
28212         this.textNodes = this.el.query("table.x-date-inner tbody span");
28213
28214         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28215             text: "&#160;",
28216             tooltip: this.monthYearText
28217         });
28218
28219         this.mbtn.on('click', this.showMonthPicker, this);
28220         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28221
28222
28223         var today = (new Date()).dateFormat(this.format);
28224         
28225         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28226         if (this.showClear) {
28227             baseTb.add( new Roo.Toolbar.Fill());
28228         }
28229         baseTb.add({
28230             text: String.format(this.todayText, today),
28231             tooltip: String.format(this.todayTip, today),
28232             handler: this.selectToday,
28233             scope: this
28234         });
28235         
28236         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28237             
28238         //});
28239         if (this.showClear) {
28240             
28241             baseTb.add( new Roo.Toolbar.Fill());
28242             baseTb.add({
28243                 text: '&#160;',
28244                 cls: 'x-btn-icon x-btn-clear',
28245                 handler: function() {
28246                     //this.value = '';
28247                     this.fireEvent("select", this, '');
28248                 },
28249                 scope: this
28250             });
28251         }
28252         
28253         
28254         if(Roo.isIE){
28255             this.el.repaint();
28256         }
28257         this.update(this.value);
28258     },
28259
28260     createMonthPicker : function(){
28261         if(!this.monthPicker.dom.firstChild){
28262             var buf = ['<table border="0" cellspacing="0">'];
28263             for(var i = 0; i < 6; i++){
28264                 buf.push(
28265                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28266                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28267                     i == 0 ?
28268                     '<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>' :
28269                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28270                 );
28271             }
28272             buf.push(
28273                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28274                     this.okText,
28275                     '</button><button type="button" class="x-date-mp-cancel">',
28276                     this.cancelText,
28277                     '</button></td></tr>',
28278                 '</table>'
28279             );
28280             this.monthPicker.update(buf.join(''));
28281             this.monthPicker.on('click', this.onMonthClick, this);
28282             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28283
28284             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28285             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28286
28287             this.mpMonths.each(function(m, a, i){
28288                 i += 1;
28289                 if((i%2) == 0){
28290                     m.dom.xmonth = 5 + Math.round(i * .5);
28291                 }else{
28292                     m.dom.xmonth = Math.round((i-1) * .5);
28293                 }
28294             });
28295         }
28296     },
28297
28298     showMonthPicker : function(){
28299         this.createMonthPicker();
28300         var size = this.el.getSize();
28301         this.monthPicker.setSize(size);
28302         this.monthPicker.child('table').setSize(size);
28303
28304         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28305         this.updateMPMonth(this.mpSelMonth);
28306         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28307         this.updateMPYear(this.mpSelYear);
28308
28309         this.monthPicker.slideIn('t', {duration:.2});
28310     },
28311
28312     updateMPYear : function(y){
28313         this.mpyear = y;
28314         var ys = this.mpYears.elements;
28315         for(var i = 1; i <= 10; i++){
28316             var td = ys[i-1], y2;
28317             if((i%2) == 0){
28318                 y2 = y + Math.round(i * .5);
28319                 td.firstChild.innerHTML = y2;
28320                 td.xyear = y2;
28321             }else{
28322                 y2 = y - (5-Math.round(i * .5));
28323                 td.firstChild.innerHTML = y2;
28324                 td.xyear = y2;
28325             }
28326             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28327         }
28328     },
28329
28330     updateMPMonth : function(sm){
28331         this.mpMonths.each(function(m, a, i){
28332             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28333         });
28334     },
28335
28336     selectMPMonth: function(m){
28337         
28338     },
28339
28340     onMonthClick : function(e, t){
28341         e.stopEvent();
28342         var el = new Roo.Element(t), pn;
28343         if(el.is('button.x-date-mp-cancel')){
28344             this.hideMonthPicker();
28345         }
28346         else if(el.is('button.x-date-mp-ok')){
28347             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28348             this.hideMonthPicker();
28349         }
28350         else if(pn = el.up('td.x-date-mp-month', 2)){
28351             this.mpMonths.removeClass('x-date-mp-sel');
28352             pn.addClass('x-date-mp-sel');
28353             this.mpSelMonth = pn.dom.xmonth;
28354         }
28355         else if(pn = el.up('td.x-date-mp-year', 2)){
28356             this.mpYears.removeClass('x-date-mp-sel');
28357             pn.addClass('x-date-mp-sel');
28358             this.mpSelYear = pn.dom.xyear;
28359         }
28360         else if(el.is('a.x-date-mp-prev')){
28361             this.updateMPYear(this.mpyear-10);
28362         }
28363         else if(el.is('a.x-date-mp-next')){
28364             this.updateMPYear(this.mpyear+10);
28365         }
28366     },
28367
28368     onMonthDblClick : function(e, t){
28369         e.stopEvent();
28370         var el = new Roo.Element(t), pn;
28371         if(pn = el.up('td.x-date-mp-month', 2)){
28372             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28373             this.hideMonthPicker();
28374         }
28375         else if(pn = el.up('td.x-date-mp-year', 2)){
28376             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28377             this.hideMonthPicker();
28378         }
28379     },
28380
28381     hideMonthPicker : function(disableAnim){
28382         if(this.monthPicker){
28383             if(disableAnim === true){
28384                 this.monthPicker.hide();
28385             }else{
28386                 this.monthPicker.slideOut('t', {duration:.2});
28387             }
28388         }
28389     },
28390
28391     // private
28392     showPrevMonth : function(e){
28393         this.update(this.activeDate.add("mo", -1));
28394     },
28395
28396     // private
28397     showNextMonth : function(e){
28398         this.update(this.activeDate.add("mo", 1));
28399     },
28400
28401     // private
28402     showPrevYear : function(){
28403         this.update(this.activeDate.add("y", -1));
28404     },
28405
28406     // private
28407     showNextYear : function(){
28408         this.update(this.activeDate.add("y", 1));
28409     },
28410
28411     // private
28412     handleMouseWheel : function(e){
28413         var delta = e.getWheelDelta();
28414         if(delta > 0){
28415             this.showPrevMonth();
28416             e.stopEvent();
28417         } else if(delta < 0){
28418             this.showNextMonth();
28419             e.stopEvent();
28420         }
28421     },
28422
28423     // private
28424     handleDateClick : function(e, t){
28425         e.stopEvent();
28426         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28427             this.setValue(new Date(t.dateValue));
28428             this.fireEvent("select", this, this.value);
28429         }
28430     },
28431
28432     // private
28433     selectToday : function(){
28434         this.setValue(new Date().clearTime());
28435         this.fireEvent("select", this, this.value);
28436     },
28437
28438     // private
28439     update : function(date)
28440     {
28441         var vd = this.activeDate;
28442         this.activeDate = date;
28443         if(vd && this.el){
28444             var t = date.getTime();
28445             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28446                 this.cells.removeClass("x-date-selected");
28447                 this.cells.each(function(c){
28448                    if(c.dom.firstChild.dateValue == t){
28449                        c.addClass("x-date-selected");
28450                        setTimeout(function(){
28451                             try{c.dom.firstChild.focus();}catch(e){}
28452                        }, 50);
28453                        return false;
28454                    }
28455                 });
28456                 return;
28457             }
28458         }
28459         
28460         var days = date.getDaysInMonth();
28461         var firstOfMonth = date.getFirstDateOfMonth();
28462         var startingPos = firstOfMonth.getDay()-this.startDay;
28463
28464         if(startingPos <= this.startDay){
28465             startingPos += 7;
28466         }
28467
28468         var pm = date.add("mo", -1);
28469         var prevStart = pm.getDaysInMonth()-startingPos;
28470
28471         var cells = this.cells.elements;
28472         var textEls = this.textNodes;
28473         days += startingPos;
28474
28475         // convert everything to numbers so it's fast
28476         var day = 86400000;
28477         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28478         var today = new Date().clearTime().getTime();
28479         var sel = date.clearTime().getTime();
28480         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28481         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28482         var ddMatch = this.disabledDatesRE;
28483         var ddText = this.disabledDatesText;
28484         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28485         var ddaysText = this.disabledDaysText;
28486         var format = this.format;
28487
28488         var setCellClass = function(cal, cell){
28489             cell.title = "";
28490             var t = d.getTime();
28491             cell.firstChild.dateValue = t;
28492             if(t == today){
28493                 cell.className += " x-date-today";
28494                 cell.title = cal.todayText;
28495             }
28496             if(t == sel){
28497                 cell.className += " x-date-selected";
28498                 setTimeout(function(){
28499                     try{cell.firstChild.focus();}catch(e){}
28500                 }, 50);
28501             }
28502             // disabling
28503             if(t < min) {
28504                 cell.className = " x-date-disabled";
28505                 cell.title = cal.minText;
28506                 return;
28507             }
28508             if(t > max) {
28509                 cell.className = " x-date-disabled";
28510                 cell.title = cal.maxText;
28511                 return;
28512             }
28513             if(ddays){
28514                 if(ddays.indexOf(d.getDay()) != -1){
28515                     cell.title = ddaysText;
28516                     cell.className = " x-date-disabled";
28517                 }
28518             }
28519             if(ddMatch && format){
28520                 var fvalue = d.dateFormat(format);
28521                 if(ddMatch.test(fvalue)){
28522                     cell.title = ddText.replace("%0", fvalue);
28523                     cell.className = " x-date-disabled";
28524                 }
28525             }
28526         };
28527
28528         var i = 0;
28529         for(; i < startingPos; i++) {
28530             textEls[i].innerHTML = (++prevStart);
28531             d.setDate(d.getDate()+1);
28532             cells[i].className = "x-date-prevday";
28533             setCellClass(this, cells[i]);
28534         }
28535         for(; i < days; i++){
28536             intDay = i - startingPos + 1;
28537             textEls[i].innerHTML = (intDay);
28538             d.setDate(d.getDate()+1);
28539             cells[i].className = "x-date-active";
28540             setCellClass(this, cells[i]);
28541         }
28542         var extraDays = 0;
28543         for(; i < 42; i++) {
28544              textEls[i].innerHTML = (++extraDays);
28545              d.setDate(d.getDate()+1);
28546              cells[i].className = "x-date-nextday";
28547              setCellClass(this, cells[i]);
28548         }
28549
28550         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28551         this.fireEvent('monthchange', this, date);
28552         
28553         if(!this.internalRender){
28554             var main = this.el.dom.firstChild;
28555             var w = main.offsetWidth;
28556             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28557             Roo.fly(main).setWidth(w);
28558             this.internalRender = true;
28559             // opera does not respect the auto grow header center column
28560             // then, after it gets a width opera refuses to recalculate
28561             // without a second pass
28562             if(Roo.isOpera && !this.secondPass){
28563                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28564                 this.secondPass = true;
28565                 this.update.defer(10, this, [date]);
28566             }
28567         }
28568         
28569         
28570     }
28571 });        /*
28572  * Based on:
28573  * Ext JS Library 1.1.1
28574  * Copyright(c) 2006-2007, Ext JS, LLC.
28575  *
28576  * Originally Released Under LGPL - original licence link has changed is not relivant.
28577  *
28578  * Fork - LGPL
28579  * <script type="text/javascript">
28580  */
28581 /**
28582  * @class Roo.TabPanel
28583  * @extends Roo.util.Observable
28584  * A lightweight tab container.
28585  * <br><br>
28586  * Usage:
28587  * <pre><code>
28588 // basic tabs 1, built from existing content
28589 var tabs = new Roo.TabPanel("tabs1");
28590 tabs.addTab("script", "View Script");
28591 tabs.addTab("markup", "View Markup");
28592 tabs.activate("script");
28593
28594 // more advanced tabs, built from javascript
28595 var jtabs = new Roo.TabPanel("jtabs");
28596 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28597
28598 // set up the UpdateManager
28599 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28600 var updater = tab2.getUpdateManager();
28601 updater.setDefaultUrl("ajax1.htm");
28602 tab2.on('activate', updater.refresh, updater, true);
28603
28604 // Use setUrl for Ajax loading
28605 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28606 tab3.setUrl("ajax2.htm", null, true);
28607
28608 // Disabled tab
28609 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28610 tab4.disable();
28611
28612 jtabs.activate("jtabs-1");
28613  * </code></pre>
28614  * @constructor
28615  * Create a new TabPanel.
28616  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28617  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28618  */
28619 Roo.TabPanel = function(container, config){
28620     /**
28621     * The container element for this TabPanel.
28622     * @type Roo.Element
28623     */
28624     this.el = Roo.get(container, true);
28625     if(config){
28626         if(typeof config == "boolean"){
28627             this.tabPosition = config ? "bottom" : "top";
28628         }else{
28629             Roo.apply(this, config);
28630         }
28631     }
28632     if(this.tabPosition == "bottom"){
28633         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28634         this.el.addClass("x-tabs-bottom");
28635     }
28636     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28637     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28638     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28639     if(Roo.isIE){
28640         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28641     }
28642     if(this.tabPosition != "bottom"){
28643         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28644          * @type Roo.Element
28645          */
28646         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28647         this.el.addClass("x-tabs-top");
28648     }
28649     this.items = [];
28650
28651     this.bodyEl.setStyle("position", "relative");
28652
28653     this.active = null;
28654     this.activateDelegate = this.activate.createDelegate(this);
28655
28656     this.addEvents({
28657         /**
28658          * @event tabchange
28659          * Fires when the active tab changes
28660          * @param {Roo.TabPanel} this
28661          * @param {Roo.TabPanelItem} activePanel The new active tab
28662          */
28663         "tabchange": true,
28664         /**
28665          * @event beforetabchange
28666          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28667          * @param {Roo.TabPanel} this
28668          * @param {Object} e Set cancel to true on this object to cancel the tab change
28669          * @param {Roo.TabPanelItem} tab The tab being changed to
28670          */
28671         "beforetabchange" : true
28672     });
28673
28674     Roo.EventManager.onWindowResize(this.onResize, this);
28675     this.cpad = this.el.getPadding("lr");
28676     this.hiddenCount = 0;
28677
28678
28679     // toolbar on the tabbar support...
28680     if (this.toolbar) {
28681         var tcfg = this.toolbar;
28682         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28683         this.toolbar = new Roo.Toolbar(tcfg);
28684         if (Roo.isSafari) {
28685             var tbl = tcfg.container.child('table', true);
28686             tbl.setAttribute('width', '100%');
28687         }
28688         
28689     }
28690    
28691
28692
28693     Roo.TabPanel.superclass.constructor.call(this);
28694 };
28695
28696 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28697     /*
28698      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28699      */
28700     tabPosition : "top",
28701     /*
28702      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28703      */
28704     currentTabWidth : 0,
28705     /*
28706      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28707      */
28708     minTabWidth : 40,
28709     /*
28710      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28711      */
28712     maxTabWidth : 250,
28713     /*
28714      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28715      */
28716     preferredTabWidth : 175,
28717     /*
28718      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28719      */
28720     resizeTabs : false,
28721     /*
28722      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28723      */
28724     monitorResize : true,
28725     /*
28726      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28727      */
28728     toolbar : false,
28729
28730     /**
28731      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28732      * @param {String} id The id of the div to use <b>or create</b>
28733      * @param {String} text The text for the tab
28734      * @param {String} content (optional) Content to put in the TabPanelItem body
28735      * @param {Boolean} closable (optional) True to create a close icon on the tab
28736      * @return {Roo.TabPanelItem} The created TabPanelItem
28737      */
28738     addTab : function(id, text, content, closable){
28739         var item = new Roo.TabPanelItem(this, id, text, closable);
28740         this.addTabItem(item);
28741         if(content){
28742             item.setContent(content);
28743         }
28744         return item;
28745     },
28746
28747     /**
28748      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28749      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28750      * @return {Roo.TabPanelItem}
28751      */
28752     getTab : function(id){
28753         return this.items[id];
28754     },
28755
28756     /**
28757      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28758      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28759      */
28760     hideTab : function(id){
28761         var t = this.items[id];
28762         if(!t.isHidden()){
28763            t.setHidden(true);
28764            this.hiddenCount++;
28765            this.autoSizeTabs();
28766         }
28767     },
28768
28769     /**
28770      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28771      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28772      */
28773     unhideTab : function(id){
28774         var t = this.items[id];
28775         if(t.isHidden()){
28776            t.setHidden(false);
28777            this.hiddenCount--;
28778            this.autoSizeTabs();
28779         }
28780     },
28781
28782     /**
28783      * Adds an existing {@link Roo.TabPanelItem}.
28784      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28785      */
28786     addTabItem : function(item){
28787         this.items[item.id] = item;
28788         this.items.push(item);
28789         if(this.resizeTabs){
28790            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28791            this.autoSizeTabs();
28792         }else{
28793             item.autoSize();
28794         }
28795     },
28796
28797     /**
28798      * Removes a {@link Roo.TabPanelItem}.
28799      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28800      */
28801     removeTab : function(id){
28802         var items = this.items;
28803         var tab = items[id];
28804         if(!tab) { return; }
28805         var index = items.indexOf(tab);
28806         if(this.active == tab && items.length > 1){
28807             var newTab = this.getNextAvailable(index);
28808             if(newTab) {
28809                 newTab.activate();
28810             }
28811         }
28812         this.stripEl.dom.removeChild(tab.pnode.dom);
28813         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28814             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28815         }
28816         items.splice(index, 1);
28817         delete this.items[tab.id];
28818         tab.fireEvent("close", tab);
28819         tab.purgeListeners();
28820         this.autoSizeTabs();
28821     },
28822
28823     getNextAvailable : function(start){
28824         var items = this.items;
28825         var index = start;
28826         // look for a next tab that will slide over to
28827         // replace the one being removed
28828         while(index < items.length){
28829             var item = items[++index];
28830             if(item && !item.isHidden()){
28831                 return item;
28832             }
28833         }
28834         // if one isn't found select the previous tab (on the left)
28835         index = start;
28836         while(index >= 0){
28837             var item = items[--index];
28838             if(item && !item.isHidden()){
28839                 return item;
28840             }
28841         }
28842         return null;
28843     },
28844
28845     /**
28846      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28847      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28848      */
28849     disableTab : function(id){
28850         var tab = this.items[id];
28851         if(tab && this.active != tab){
28852             tab.disable();
28853         }
28854     },
28855
28856     /**
28857      * Enables a {@link Roo.TabPanelItem} that is disabled.
28858      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28859      */
28860     enableTab : function(id){
28861         var tab = this.items[id];
28862         tab.enable();
28863     },
28864
28865     /**
28866      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28867      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28868      * @return {Roo.TabPanelItem} The TabPanelItem.
28869      */
28870     activate : function(id){
28871         var tab = this.items[id];
28872         if(!tab){
28873             return null;
28874         }
28875         if(tab == this.active || tab.disabled){
28876             return tab;
28877         }
28878         var e = {};
28879         this.fireEvent("beforetabchange", this, e, tab);
28880         if(e.cancel !== true && !tab.disabled){
28881             if(this.active){
28882                 this.active.hide();
28883             }
28884             this.active = this.items[id];
28885             this.active.show();
28886             this.fireEvent("tabchange", this, this.active);
28887         }
28888         return tab;
28889     },
28890
28891     /**
28892      * Gets the active {@link Roo.TabPanelItem}.
28893      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28894      */
28895     getActiveTab : function(){
28896         return this.active;
28897     },
28898
28899     /**
28900      * Updates the tab body element to fit the height of the container element
28901      * for overflow scrolling
28902      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28903      */
28904     syncHeight : function(targetHeight){
28905         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28906         var bm = this.bodyEl.getMargins();
28907         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28908         this.bodyEl.setHeight(newHeight);
28909         return newHeight;
28910     },
28911
28912     onResize : function(){
28913         if(this.monitorResize){
28914             this.autoSizeTabs();
28915         }
28916     },
28917
28918     /**
28919      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28920      */
28921     beginUpdate : function(){
28922         this.updating = true;
28923     },
28924
28925     /**
28926      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28927      */
28928     endUpdate : function(){
28929         this.updating = false;
28930         this.autoSizeTabs();
28931     },
28932
28933     /**
28934      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28935      */
28936     autoSizeTabs : function(){
28937         var count = this.items.length;
28938         var vcount = count - this.hiddenCount;
28939         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28940             return;
28941         }
28942         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28943         var availWidth = Math.floor(w / vcount);
28944         var b = this.stripBody;
28945         if(b.getWidth() > w){
28946             var tabs = this.items;
28947             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28948             if(availWidth < this.minTabWidth){
28949                 /*if(!this.sleft){    // incomplete scrolling code
28950                     this.createScrollButtons();
28951                 }
28952                 this.showScroll();
28953                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28954             }
28955         }else{
28956             if(this.currentTabWidth < this.preferredTabWidth){
28957                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28958             }
28959         }
28960     },
28961
28962     /**
28963      * Returns the number of tabs in this TabPanel.
28964      * @return {Number}
28965      */
28966      getCount : function(){
28967          return this.items.length;
28968      },
28969
28970     /**
28971      * Resizes all the tabs to the passed width
28972      * @param {Number} The new width
28973      */
28974     setTabWidth : function(width){
28975         this.currentTabWidth = width;
28976         for(var i = 0, len = this.items.length; i < len; i++) {
28977                 if(!this.items[i].isHidden()) {
28978                 this.items[i].setWidth(width);
28979             }
28980         }
28981     },
28982
28983     /**
28984      * Destroys this TabPanel
28985      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28986      */
28987     destroy : function(removeEl){
28988         Roo.EventManager.removeResizeListener(this.onResize, this);
28989         for(var i = 0, len = this.items.length; i < len; i++){
28990             this.items[i].purgeListeners();
28991         }
28992         if(removeEl === true){
28993             this.el.update("");
28994             this.el.remove();
28995         }
28996     }
28997 });
28998
28999 /**
29000  * @class Roo.TabPanelItem
29001  * @extends Roo.util.Observable
29002  * Represents an individual item (tab plus body) in a TabPanel.
29003  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29004  * @param {String} id The id of this TabPanelItem
29005  * @param {String} text The text for the tab of this TabPanelItem
29006  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29007  */
29008 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29009     /**
29010      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29011      * @type Roo.TabPanel
29012      */
29013     this.tabPanel = tabPanel;
29014     /**
29015      * The id for this TabPanelItem
29016      * @type String
29017      */
29018     this.id = id;
29019     /** @private */
29020     this.disabled = false;
29021     /** @private */
29022     this.text = text;
29023     /** @private */
29024     this.loaded = false;
29025     this.closable = closable;
29026
29027     /**
29028      * The body element for this TabPanelItem.
29029      * @type Roo.Element
29030      */
29031     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29032     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29033     this.bodyEl.setStyle("display", "block");
29034     this.bodyEl.setStyle("zoom", "1");
29035     this.hideAction();
29036
29037     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29038     /** @private */
29039     this.el = Roo.get(els.el, true);
29040     this.inner = Roo.get(els.inner, true);
29041     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29042     this.pnode = Roo.get(els.el.parentNode, true);
29043     this.el.on("mousedown", this.onTabMouseDown, this);
29044     this.el.on("click", this.onTabClick, this);
29045     /** @private */
29046     if(closable){
29047         var c = Roo.get(els.close, true);
29048         c.dom.title = this.closeText;
29049         c.addClassOnOver("close-over");
29050         c.on("click", this.closeClick, this);
29051      }
29052
29053     this.addEvents({
29054          /**
29055          * @event activate
29056          * Fires when this tab becomes the active tab.
29057          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29058          * @param {Roo.TabPanelItem} this
29059          */
29060         "activate": true,
29061         /**
29062          * @event beforeclose
29063          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29064          * @param {Roo.TabPanelItem} this
29065          * @param {Object} e Set cancel to true on this object to cancel the close.
29066          */
29067         "beforeclose": true,
29068         /**
29069          * @event close
29070          * Fires when this tab is closed.
29071          * @param {Roo.TabPanelItem} this
29072          */
29073          "close": true,
29074         /**
29075          * @event deactivate
29076          * Fires when this tab is no longer the active tab.
29077          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29078          * @param {Roo.TabPanelItem} this
29079          */
29080          "deactivate" : true
29081     });
29082     this.hidden = false;
29083
29084     Roo.TabPanelItem.superclass.constructor.call(this);
29085 };
29086
29087 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29088     purgeListeners : function(){
29089        Roo.util.Observable.prototype.purgeListeners.call(this);
29090        this.el.removeAllListeners();
29091     },
29092     /**
29093      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29094      */
29095     show : function(){
29096         this.pnode.addClass("on");
29097         this.showAction();
29098         if(Roo.isOpera){
29099             this.tabPanel.stripWrap.repaint();
29100         }
29101         this.fireEvent("activate", this.tabPanel, this);
29102     },
29103
29104     /**
29105      * Returns true if this tab is the active tab.
29106      * @return {Boolean}
29107      */
29108     isActive : function(){
29109         return this.tabPanel.getActiveTab() == this;
29110     },
29111
29112     /**
29113      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29114      */
29115     hide : function(){
29116         this.pnode.removeClass("on");
29117         this.hideAction();
29118         this.fireEvent("deactivate", this.tabPanel, this);
29119     },
29120
29121     hideAction : function(){
29122         this.bodyEl.hide();
29123         this.bodyEl.setStyle("position", "absolute");
29124         this.bodyEl.setLeft("-20000px");
29125         this.bodyEl.setTop("-20000px");
29126     },
29127
29128     showAction : function(){
29129         this.bodyEl.setStyle("position", "relative");
29130         this.bodyEl.setTop("");
29131         this.bodyEl.setLeft("");
29132         this.bodyEl.show();
29133     },
29134
29135     /**
29136      * Set the tooltip for the tab.
29137      * @param {String} tooltip The tab's tooltip
29138      */
29139     setTooltip : function(text){
29140         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29141             this.textEl.dom.qtip = text;
29142             this.textEl.dom.removeAttribute('title');
29143         }else{
29144             this.textEl.dom.title = text;
29145         }
29146     },
29147
29148     onTabClick : function(e){
29149         e.preventDefault();
29150         this.tabPanel.activate(this.id);
29151     },
29152
29153     onTabMouseDown : function(e){
29154         e.preventDefault();
29155         this.tabPanel.activate(this.id);
29156     },
29157
29158     getWidth : function(){
29159         return this.inner.getWidth();
29160     },
29161
29162     setWidth : function(width){
29163         var iwidth = width - this.pnode.getPadding("lr");
29164         this.inner.setWidth(iwidth);
29165         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29166         this.pnode.setWidth(width);
29167     },
29168
29169     /**
29170      * Show or hide the tab
29171      * @param {Boolean} hidden True to hide or false to show.
29172      */
29173     setHidden : function(hidden){
29174         this.hidden = hidden;
29175         this.pnode.setStyle("display", hidden ? "none" : "");
29176     },
29177
29178     /**
29179      * Returns true if this tab is "hidden"
29180      * @return {Boolean}
29181      */
29182     isHidden : function(){
29183         return this.hidden;
29184     },
29185
29186     /**
29187      * Returns the text for this tab
29188      * @return {String}
29189      */
29190     getText : function(){
29191         return this.text;
29192     },
29193
29194     autoSize : function(){
29195         //this.el.beginMeasure();
29196         this.textEl.setWidth(1);
29197         /*
29198          *  #2804 [new] Tabs in Roojs
29199          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29200          */
29201         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29202         //this.el.endMeasure();
29203     },
29204
29205     /**
29206      * Sets the text for the tab (Note: this also sets the tooltip text)
29207      * @param {String} text The tab's text and tooltip
29208      */
29209     setText : function(text){
29210         this.text = text;
29211         this.textEl.update(text);
29212         this.setTooltip(text);
29213         if(!this.tabPanel.resizeTabs){
29214             this.autoSize();
29215         }
29216     },
29217     /**
29218      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29219      */
29220     activate : function(){
29221         this.tabPanel.activate(this.id);
29222     },
29223
29224     /**
29225      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29226      */
29227     disable : function(){
29228         if(this.tabPanel.active != this){
29229             this.disabled = true;
29230             this.pnode.addClass("disabled");
29231         }
29232     },
29233
29234     /**
29235      * Enables this TabPanelItem if it was previously disabled.
29236      */
29237     enable : function(){
29238         this.disabled = false;
29239         this.pnode.removeClass("disabled");
29240     },
29241
29242     /**
29243      * Sets the content for this TabPanelItem.
29244      * @param {String} content The content
29245      * @param {Boolean} loadScripts true to look for and load scripts
29246      */
29247     setContent : function(content, loadScripts){
29248         this.bodyEl.update(content, loadScripts);
29249     },
29250
29251     /**
29252      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29253      * @return {Roo.UpdateManager} The UpdateManager
29254      */
29255     getUpdateManager : function(){
29256         return this.bodyEl.getUpdateManager();
29257     },
29258
29259     /**
29260      * Set a URL to be used to load the content for this TabPanelItem.
29261      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29262      * @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)
29263      * @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)
29264      * @return {Roo.UpdateManager} The UpdateManager
29265      */
29266     setUrl : function(url, params, loadOnce){
29267         if(this.refreshDelegate){
29268             this.un('activate', this.refreshDelegate);
29269         }
29270         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29271         this.on("activate", this.refreshDelegate);
29272         return this.bodyEl.getUpdateManager();
29273     },
29274
29275     /** @private */
29276     _handleRefresh : function(url, params, loadOnce){
29277         if(!loadOnce || !this.loaded){
29278             var updater = this.bodyEl.getUpdateManager();
29279             updater.update(url, params, this._setLoaded.createDelegate(this));
29280         }
29281     },
29282
29283     /**
29284      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29285      *   Will fail silently if the setUrl method has not been called.
29286      *   This does not activate the panel, just updates its content.
29287      */
29288     refresh : function(){
29289         if(this.refreshDelegate){
29290            this.loaded = false;
29291            this.refreshDelegate();
29292         }
29293     },
29294
29295     /** @private */
29296     _setLoaded : function(){
29297         this.loaded = true;
29298     },
29299
29300     /** @private */
29301     closeClick : function(e){
29302         var o = {};
29303         e.stopEvent();
29304         this.fireEvent("beforeclose", this, o);
29305         if(o.cancel !== true){
29306             this.tabPanel.removeTab(this.id);
29307         }
29308     },
29309     /**
29310      * The text displayed in the tooltip for the close icon.
29311      * @type String
29312      */
29313     closeText : "Close this tab"
29314 });
29315
29316 /** @private */
29317 Roo.TabPanel.prototype.createStrip = function(container){
29318     var strip = document.createElement("div");
29319     strip.className = "x-tabs-wrap";
29320     container.appendChild(strip);
29321     return strip;
29322 };
29323 /** @private */
29324 Roo.TabPanel.prototype.createStripList = function(strip){
29325     // div wrapper for retard IE
29326     // returns the "tr" element.
29327     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29328         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29329         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29330     return strip.firstChild.firstChild.firstChild.firstChild;
29331 };
29332 /** @private */
29333 Roo.TabPanel.prototype.createBody = function(container){
29334     var body = document.createElement("div");
29335     Roo.id(body, "tab-body");
29336     Roo.fly(body).addClass("x-tabs-body");
29337     container.appendChild(body);
29338     return body;
29339 };
29340 /** @private */
29341 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29342     var body = Roo.getDom(id);
29343     if(!body){
29344         body = document.createElement("div");
29345         body.id = id;
29346     }
29347     Roo.fly(body).addClass("x-tabs-item-body");
29348     bodyEl.insertBefore(body, bodyEl.firstChild);
29349     return body;
29350 };
29351 /** @private */
29352 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29353     var td = document.createElement("td");
29354     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29355     //stripEl.appendChild(td);
29356     if(closable){
29357         td.className = "x-tabs-closable";
29358         if(!this.closeTpl){
29359             this.closeTpl = new Roo.Template(
29360                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29361                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29362                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29363             );
29364         }
29365         var el = this.closeTpl.overwrite(td, {"text": text});
29366         var close = el.getElementsByTagName("div")[0];
29367         var inner = el.getElementsByTagName("em")[0];
29368         return {"el": el, "close": close, "inner": inner};
29369     } else {
29370         if(!this.tabTpl){
29371             this.tabTpl = new Roo.Template(
29372                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29373                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29374             );
29375         }
29376         var el = this.tabTpl.overwrite(td, {"text": text});
29377         var inner = el.getElementsByTagName("em")[0];
29378         return {"el": el, "inner": inner};
29379     }
29380 };/*
29381  * Based on:
29382  * Ext JS Library 1.1.1
29383  * Copyright(c) 2006-2007, Ext JS, LLC.
29384  *
29385  * Originally Released Under LGPL - original licence link has changed is not relivant.
29386  *
29387  * Fork - LGPL
29388  * <script type="text/javascript">
29389  */
29390
29391 /**
29392  * @class Roo.Button
29393  * @extends Roo.util.Observable
29394  * Simple Button class
29395  * @cfg {String} text The button text
29396  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29397  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29398  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29399  * @cfg {Object} scope The scope of the handler
29400  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29401  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29402  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29403  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29404  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29405  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29406    applies if enableToggle = true)
29407  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29408  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29409   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29410  * @constructor
29411  * Create a new button
29412  * @param {Object} config The config object
29413  */
29414 Roo.Button = function(renderTo, config)
29415 {
29416     if (!config) {
29417         config = renderTo;
29418         renderTo = config.renderTo || false;
29419     }
29420     
29421     Roo.apply(this, config);
29422     this.addEvents({
29423         /**
29424              * @event click
29425              * Fires when this button is clicked
29426              * @param {Button} this
29427              * @param {EventObject} e The click event
29428              */
29429             "click" : true,
29430         /**
29431              * @event toggle
29432              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29433              * @param {Button} this
29434              * @param {Boolean} pressed
29435              */
29436             "toggle" : true,
29437         /**
29438              * @event mouseover
29439              * Fires when the mouse hovers over the button
29440              * @param {Button} this
29441              * @param {Event} e The event object
29442              */
29443         'mouseover' : true,
29444         /**
29445              * @event mouseout
29446              * Fires when the mouse exits the button
29447              * @param {Button} this
29448              * @param {Event} e The event object
29449              */
29450         'mouseout': true,
29451          /**
29452              * @event render
29453              * Fires when the button is rendered
29454              * @param {Button} this
29455              */
29456         'render': true
29457     });
29458     if(this.menu){
29459         this.menu = Roo.menu.MenuMgr.get(this.menu);
29460     }
29461     // register listeners first!!  - so render can be captured..
29462     Roo.util.Observable.call(this);
29463     if(renderTo){
29464         this.render(renderTo);
29465     }
29466     
29467   
29468 };
29469
29470 Roo.extend(Roo.Button, Roo.util.Observable, {
29471     /**
29472      * 
29473      */
29474     
29475     /**
29476      * Read-only. True if this button is hidden
29477      * @type Boolean
29478      */
29479     hidden : false,
29480     /**
29481      * Read-only. True if this button is disabled
29482      * @type Boolean
29483      */
29484     disabled : false,
29485     /**
29486      * Read-only. True if this button is pressed (only if enableToggle = true)
29487      * @type Boolean
29488      */
29489     pressed : false,
29490
29491     /**
29492      * @cfg {Number} tabIndex 
29493      * The DOM tabIndex for this button (defaults to undefined)
29494      */
29495     tabIndex : undefined,
29496
29497     /**
29498      * @cfg {Boolean} enableToggle
29499      * True to enable pressed/not pressed toggling (defaults to false)
29500      */
29501     enableToggle: false,
29502     /**
29503      * @cfg {Mixed} menu
29504      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29505      */
29506     menu : undefined,
29507     /**
29508      * @cfg {String} menuAlign
29509      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29510      */
29511     menuAlign : "tl-bl?",
29512
29513     /**
29514      * @cfg {String} iconCls
29515      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29516      */
29517     iconCls : undefined,
29518     /**
29519      * @cfg {String} type
29520      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29521      */
29522     type : 'button',
29523
29524     // private
29525     menuClassTarget: 'tr',
29526
29527     /**
29528      * @cfg {String} clickEvent
29529      * The type of event to map to the button's event handler (defaults to 'click')
29530      */
29531     clickEvent : 'click',
29532
29533     /**
29534      * @cfg {Boolean} handleMouseEvents
29535      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29536      */
29537     handleMouseEvents : true,
29538
29539     /**
29540      * @cfg {String} tooltipType
29541      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29542      */
29543     tooltipType : 'qtip',
29544
29545     /**
29546      * @cfg {String} cls
29547      * A CSS class to apply to the button's main element.
29548      */
29549     
29550     /**
29551      * @cfg {Roo.Template} template (Optional)
29552      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29553      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29554      * require code modifications if required elements (e.g. a button) aren't present.
29555      */
29556
29557     // private
29558     render : function(renderTo){
29559         var btn;
29560         if(this.hideParent){
29561             this.parentEl = Roo.get(renderTo);
29562         }
29563         if(!this.dhconfig){
29564             if(!this.template){
29565                 if(!Roo.Button.buttonTemplate){
29566                     // hideous table template
29567                     Roo.Button.buttonTemplate = new Roo.Template(
29568                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29569                         '<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>',
29570                         "</tr></tbody></table>");
29571                 }
29572                 this.template = Roo.Button.buttonTemplate;
29573             }
29574             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29575             var btnEl = btn.child("button:first");
29576             btnEl.on('focus', this.onFocus, this);
29577             btnEl.on('blur', this.onBlur, this);
29578             if(this.cls){
29579                 btn.addClass(this.cls);
29580             }
29581             if(this.icon){
29582                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29583             }
29584             if(this.iconCls){
29585                 btnEl.addClass(this.iconCls);
29586                 if(!this.cls){
29587                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29588                 }
29589             }
29590             if(this.tabIndex !== undefined){
29591                 btnEl.dom.tabIndex = this.tabIndex;
29592             }
29593             if(this.tooltip){
29594                 if(typeof this.tooltip == 'object'){
29595                     Roo.QuickTips.tips(Roo.apply({
29596                           target: btnEl.id
29597                     }, this.tooltip));
29598                 } else {
29599                     btnEl.dom[this.tooltipType] = this.tooltip;
29600                 }
29601             }
29602         }else{
29603             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29604         }
29605         this.el = btn;
29606         if(this.id){
29607             this.el.dom.id = this.el.id = this.id;
29608         }
29609         if(this.menu){
29610             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29611             this.menu.on("show", this.onMenuShow, this);
29612             this.menu.on("hide", this.onMenuHide, this);
29613         }
29614         btn.addClass("x-btn");
29615         if(Roo.isIE && !Roo.isIE7){
29616             this.autoWidth.defer(1, this);
29617         }else{
29618             this.autoWidth();
29619         }
29620         if(this.handleMouseEvents){
29621             btn.on("mouseover", this.onMouseOver, this);
29622             btn.on("mouseout", this.onMouseOut, this);
29623             btn.on("mousedown", this.onMouseDown, this);
29624         }
29625         btn.on(this.clickEvent, this.onClick, this);
29626         //btn.on("mouseup", this.onMouseUp, this);
29627         if(this.hidden){
29628             this.hide();
29629         }
29630         if(this.disabled){
29631             this.disable();
29632         }
29633         Roo.ButtonToggleMgr.register(this);
29634         if(this.pressed){
29635             this.el.addClass("x-btn-pressed");
29636         }
29637         if(this.repeat){
29638             var repeater = new Roo.util.ClickRepeater(btn,
29639                 typeof this.repeat == "object" ? this.repeat : {}
29640             );
29641             repeater.on("click", this.onClick,  this);
29642         }
29643         
29644         this.fireEvent('render', this);
29645         
29646     },
29647     /**
29648      * Returns the button's underlying element
29649      * @return {Roo.Element} The element
29650      */
29651     getEl : function(){
29652         return this.el;  
29653     },
29654     
29655     /**
29656      * Destroys this Button and removes any listeners.
29657      */
29658     destroy : function(){
29659         Roo.ButtonToggleMgr.unregister(this);
29660         this.el.removeAllListeners();
29661         this.purgeListeners();
29662         this.el.remove();
29663     },
29664
29665     // private
29666     autoWidth : function(){
29667         if(this.el){
29668             this.el.setWidth("auto");
29669             if(Roo.isIE7 && Roo.isStrict){
29670                 var ib = this.el.child('button');
29671                 if(ib && ib.getWidth() > 20){
29672                     ib.clip();
29673                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29674                 }
29675             }
29676             if(this.minWidth){
29677                 if(this.hidden){
29678                     this.el.beginMeasure();
29679                 }
29680                 if(this.el.getWidth() < this.minWidth){
29681                     this.el.setWidth(this.minWidth);
29682                 }
29683                 if(this.hidden){
29684                     this.el.endMeasure();
29685                 }
29686             }
29687         }
29688     },
29689
29690     /**
29691      * Assigns this button's click handler
29692      * @param {Function} handler The function to call when the button is clicked
29693      * @param {Object} scope (optional) Scope for the function passed in
29694      */
29695     setHandler : function(handler, scope){
29696         this.handler = handler;
29697         this.scope = scope;  
29698     },
29699     
29700     /**
29701      * Sets this button's text
29702      * @param {String} text The button text
29703      */
29704     setText : function(text){
29705         this.text = text;
29706         if(this.el){
29707             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29708         }
29709         this.autoWidth();
29710     },
29711     
29712     /**
29713      * Gets the text for this button
29714      * @return {String} The button text
29715      */
29716     getText : function(){
29717         return this.text;  
29718     },
29719     
29720     /**
29721      * Show this button
29722      */
29723     show: function(){
29724         this.hidden = false;
29725         if(this.el){
29726             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29727         }
29728     },
29729     
29730     /**
29731      * Hide this button
29732      */
29733     hide: function(){
29734         this.hidden = true;
29735         if(this.el){
29736             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29737         }
29738     },
29739     
29740     /**
29741      * Convenience function for boolean show/hide
29742      * @param {Boolean} visible True to show, false to hide
29743      */
29744     setVisible: function(visible){
29745         if(visible) {
29746             this.show();
29747         }else{
29748             this.hide();
29749         }
29750     },
29751     
29752     /**
29753      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29754      * @param {Boolean} state (optional) Force a particular state
29755      */
29756     toggle : function(state){
29757         state = state === undefined ? !this.pressed : state;
29758         if(state != this.pressed){
29759             if(state){
29760                 this.el.addClass("x-btn-pressed");
29761                 this.pressed = true;
29762                 this.fireEvent("toggle", this, true);
29763             }else{
29764                 this.el.removeClass("x-btn-pressed");
29765                 this.pressed = false;
29766                 this.fireEvent("toggle", this, false);
29767             }
29768             if(this.toggleHandler){
29769                 this.toggleHandler.call(this.scope || this, this, state);
29770             }
29771         }
29772     },
29773     
29774     /**
29775      * Focus the button
29776      */
29777     focus : function(){
29778         this.el.child('button:first').focus();
29779     },
29780     
29781     /**
29782      * Disable this button
29783      */
29784     disable : function(){
29785         if(this.el){
29786             this.el.addClass("x-btn-disabled");
29787         }
29788         this.disabled = true;
29789     },
29790     
29791     /**
29792      * Enable this button
29793      */
29794     enable : function(){
29795         if(this.el){
29796             this.el.removeClass("x-btn-disabled");
29797         }
29798         this.disabled = false;
29799     },
29800
29801     /**
29802      * Convenience function for boolean enable/disable
29803      * @param {Boolean} enabled True to enable, false to disable
29804      */
29805     setDisabled : function(v){
29806         this[v !== true ? "enable" : "disable"]();
29807     },
29808
29809     // private
29810     onClick : function(e)
29811     {
29812         if(e){
29813             e.preventDefault();
29814         }
29815         if(e.button != 0){
29816             return;
29817         }
29818         if(!this.disabled){
29819             if(this.enableToggle){
29820                 this.toggle();
29821             }
29822             if(this.menu && !this.menu.isVisible()){
29823                 this.menu.show(this.el, this.menuAlign);
29824             }
29825             this.fireEvent("click", this, e);
29826             if(this.handler){
29827                 this.el.removeClass("x-btn-over");
29828                 this.handler.call(this.scope || this, this, e);
29829             }
29830         }
29831     },
29832     // private
29833     onMouseOver : function(e){
29834         if(!this.disabled){
29835             this.el.addClass("x-btn-over");
29836             this.fireEvent('mouseover', this, e);
29837         }
29838     },
29839     // private
29840     onMouseOut : function(e){
29841         if(!e.within(this.el,  true)){
29842             this.el.removeClass("x-btn-over");
29843             this.fireEvent('mouseout', this, e);
29844         }
29845     },
29846     // private
29847     onFocus : function(e){
29848         if(!this.disabled){
29849             this.el.addClass("x-btn-focus");
29850         }
29851     },
29852     // private
29853     onBlur : function(e){
29854         this.el.removeClass("x-btn-focus");
29855     },
29856     // private
29857     onMouseDown : function(e){
29858         if(!this.disabled && e.button == 0){
29859             this.el.addClass("x-btn-click");
29860             Roo.get(document).on('mouseup', this.onMouseUp, this);
29861         }
29862     },
29863     // private
29864     onMouseUp : function(e){
29865         if(e.button == 0){
29866             this.el.removeClass("x-btn-click");
29867             Roo.get(document).un('mouseup', this.onMouseUp, this);
29868         }
29869     },
29870     // private
29871     onMenuShow : function(e){
29872         this.el.addClass("x-btn-menu-active");
29873     },
29874     // private
29875     onMenuHide : function(e){
29876         this.el.removeClass("x-btn-menu-active");
29877     }   
29878 });
29879
29880 // Private utility class used by Button
29881 Roo.ButtonToggleMgr = function(){
29882    var groups = {};
29883    
29884    function toggleGroup(btn, state){
29885        if(state){
29886            var g = groups[btn.toggleGroup];
29887            for(var i = 0, l = g.length; i < l; i++){
29888                if(g[i] != btn){
29889                    g[i].toggle(false);
29890                }
29891            }
29892        }
29893    }
29894    
29895    return {
29896        register : function(btn){
29897            if(!btn.toggleGroup){
29898                return;
29899            }
29900            var g = groups[btn.toggleGroup];
29901            if(!g){
29902                g = groups[btn.toggleGroup] = [];
29903            }
29904            g.push(btn);
29905            btn.on("toggle", toggleGroup);
29906        },
29907        
29908        unregister : function(btn){
29909            if(!btn.toggleGroup){
29910                return;
29911            }
29912            var g = groups[btn.toggleGroup];
29913            if(g){
29914                g.remove(btn);
29915                btn.un("toggle", toggleGroup);
29916            }
29917        }
29918    };
29919 }();/*
29920  * Based on:
29921  * Ext JS Library 1.1.1
29922  * Copyright(c) 2006-2007, Ext JS, LLC.
29923  *
29924  * Originally Released Under LGPL - original licence link has changed is not relivant.
29925  *
29926  * Fork - LGPL
29927  * <script type="text/javascript">
29928  */
29929  
29930 /**
29931  * @class Roo.SplitButton
29932  * @extends Roo.Button
29933  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29934  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29935  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29936  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29937  * @cfg {String} arrowTooltip The title attribute of the arrow
29938  * @constructor
29939  * Create a new menu button
29940  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29941  * @param {Object} config The config object
29942  */
29943 Roo.SplitButton = function(renderTo, config){
29944     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29945     /**
29946      * @event arrowclick
29947      * Fires when this button's arrow is clicked
29948      * @param {SplitButton} this
29949      * @param {EventObject} e The click event
29950      */
29951     this.addEvents({"arrowclick":true});
29952 };
29953
29954 Roo.extend(Roo.SplitButton, Roo.Button, {
29955     render : function(renderTo){
29956         // this is one sweet looking template!
29957         var tpl = new Roo.Template(
29958             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29959             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29960             '<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>',
29961             "</tbody></table></td><td>",
29962             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29963             '<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>',
29964             "</tbody></table></td></tr></table>"
29965         );
29966         var btn = tpl.append(renderTo, [this.text, this.type], true);
29967         var btnEl = btn.child("button");
29968         if(this.cls){
29969             btn.addClass(this.cls);
29970         }
29971         if(this.icon){
29972             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29973         }
29974         if(this.iconCls){
29975             btnEl.addClass(this.iconCls);
29976             if(!this.cls){
29977                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29978             }
29979         }
29980         this.el = btn;
29981         if(this.handleMouseEvents){
29982             btn.on("mouseover", this.onMouseOver, this);
29983             btn.on("mouseout", this.onMouseOut, this);
29984             btn.on("mousedown", this.onMouseDown, this);
29985             btn.on("mouseup", this.onMouseUp, this);
29986         }
29987         btn.on(this.clickEvent, this.onClick, this);
29988         if(this.tooltip){
29989             if(typeof this.tooltip == 'object'){
29990                 Roo.QuickTips.tips(Roo.apply({
29991                       target: btnEl.id
29992                 }, this.tooltip));
29993             } else {
29994                 btnEl.dom[this.tooltipType] = this.tooltip;
29995             }
29996         }
29997         if(this.arrowTooltip){
29998             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29999         }
30000         if(this.hidden){
30001             this.hide();
30002         }
30003         if(this.disabled){
30004             this.disable();
30005         }
30006         if(this.pressed){
30007             this.el.addClass("x-btn-pressed");
30008         }
30009         if(Roo.isIE && !Roo.isIE7){
30010             this.autoWidth.defer(1, this);
30011         }else{
30012             this.autoWidth();
30013         }
30014         if(this.menu){
30015             this.menu.on("show", this.onMenuShow, this);
30016             this.menu.on("hide", this.onMenuHide, this);
30017         }
30018         this.fireEvent('render', this);
30019     },
30020
30021     // private
30022     autoWidth : function(){
30023         if(this.el){
30024             var tbl = this.el.child("table:first");
30025             var tbl2 = this.el.child("table:last");
30026             this.el.setWidth("auto");
30027             tbl.setWidth("auto");
30028             if(Roo.isIE7 && Roo.isStrict){
30029                 var ib = this.el.child('button:first');
30030                 if(ib && ib.getWidth() > 20){
30031                     ib.clip();
30032                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30033                 }
30034             }
30035             if(this.minWidth){
30036                 if(this.hidden){
30037                     this.el.beginMeasure();
30038                 }
30039                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30040                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30041                 }
30042                 if(this.hidden){
30043                     this.el.endMeasure();
30044                 }
30045             }
30046             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30047         } 
30048     },
30049     /**
30050      * Sets this button's click handler
30051      * @param {Function} handler The function to call when the button is clicked
30052      * @param {Object} scope (optional) Scope for the function passed above
30053      */
30054     setHandler : function(handler, scope){
30055         this.handler = handler;
30056         this.scope = scope;  
30057     },
30058     
30059     /**
30060      * Sets this button's arrow click handler
30061      * @param {Function} handler The function to call when the arrow is clicked
30062      * @param {Object} scope (optional) Scope for the function passed above
30063      */
30064     setArrowHandler : function(handler, scope){
30065         this.arrowHandler = handler;
30066         this.scope = scope;  
30067     },
30068     
30069     /**
30070      * Focus the button
30071      */
30072     focus : function(){
30073         if(this.el){
30074             this.el.child("button:first").focus();
30075         }
30076     },
30077
30078     // private
30079     onClick : function(e){
30080         e.preventDefault();
30081         if(!this.disabled){
30082             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30083                 if(this.menu && !this.menu.isVisible()){
30084                     this.menu.show(this.el, this.menuAlign);
30085                 }
30086                 this.fireEvent("arrowclick", this, e);
30087                 if(this.arrowHandler){
30088                     this.arrowHandler.call(this.scope || this, this, e);
30089                 }
30090             }else{
30091                 this.fireEvent("click", this, e);
30092                 if(this.handler){
30093                     this.handler.call(this.scope || this, this, e);
30094                 }
30095             }
30096         }
30097     },
30098     // private
30099     onMouseDown : function(e){
30100         if(!this.disabled){
30101             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30102         }
30103     },
30104     // private
30105     onMouseUp : function(e){
30106         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30107     }   
30108 });
30109
30110
30111 // backwards compat
30112 Roo.MenuButton = Roo.SplitButton;/*
30113  * Based on:
30114  * Ext JS Library 1.1.1
30115  * Copyright(c) 2006-2007, Ext JS, LLC.
30116  *
30117  * Originally Released Under LGPL - original licence link has changed is not relivant.
30118  *
30119  * Fork - LGPL
30120  * <script type="text/javascript">
30121  */
30122
30123 /**
30124  * @class Roo.Toolbar
30125  * Basic Toolbar class.
30126  * @constructor
30127  * Creates a new Toolbar
30128  * @param {Object} container The config object
30129  */ 
30130 Roo.Toolbar = function(container, buttons, config)
30131 {
30132     /// old consturctor format still supported..
30133     if(container instanceof Array){ // omit the container for later rendering
30134         buttons = container;
30135         config = buttons;
30136         container = null;
30137     }
30138     if (typeof(container) == 'object' && container.xtype) {
30139         config = container;
30140         container = config.container;
30141         buttons = config.buttons || []; // not really - use items!!
30142     }
30143     var xitems = [];
30144     if (config && config.items) {
30145         xitems = config.items;
30146         delete config.items;
30147     }
30148     Roo.apply(this, config);
30149     this.buttons = buttons;
30150     
30151     if(container){
30152         this.render(container);
30153     }
30154     this.xitems = xitems;
30155     Roo.each(xitems, function(b) {
30156         this.add(b);
30157     }, this);
30158     
30159 };
30160
30161 Roo.Toolbar.prototype = {
30162     /**
30163      * @cfg {Array} items
30164      * array of button configs or elements to add (will be converted to a MixedCollection)
30165      */
30166     
30167     /**
30168      * @cfg {String/HTMLElement/Element} container
30169      * The id or element that will contain the toolbar
30170      */
30171     // private
30172     render : function(ct){
30173         this.el = Roo.get(ct);
30174         if(this.cls){
30175             this.el.addClass(this.cls);
30176         }
30177         // using a table allows for vertical alignment
30178         // 100% width is needed by Safari...
30179         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30180         this.tr = this.el.child("tr", true);
30181         var autoId = 0;
30182         this.items = new Roo.util.MixedCollection(false, function(o){
30183             return o.id || ("item" + (++autoId));
30184         });
30185         if(this.buttons){
30186             this.add.apply(this, this.buttons);
30187             delete this.buttons;
30188         }
30189     },
30190
30191     /**
30192      * Adds element(s) to the toolbar -- this function takes a variable number of 
30193      * arguments of mixed type and adds them to the toolbar.
30194      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30195      * <ul>
30196      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30197      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30198      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30199      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30200      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30201      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30202      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30203      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30204      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30205      * </ul>
30206      * @param {Mixed} arg2
30207      * @param {Mixed} etc.
30208      */
30209     add : function(){
30210         var a = arguments, l = a.length;
30211         for(var i = 0; i < l; i++){
30212             this._add(a[i]);
30213         }
30214     },
30215     // private..
30216     _add : function(el) {
30217         
30218         if (el.xtype) {
30219             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30220         }
30221         
30222         if (el.applyTo){ // some kind of form field
30223             return this.addField(el);
30224         } 
30225         if (el.render){ // some kind of Toolbar.Item
30226             return this.addItem(el);
30227         }
30228         if (typeof el == "string"){ // string
30229             if(el == "separator" || el == "-"){
30230                 return this.addSeparator();
30231             }
30232             if (el == " "){
30233                 return this.addSpacer();
30234             }
30235             if(el == "->"){
30236                 return this.addFill();
30237             }
30238             return this.addText(el);
30239             
30240         }
30241         if(el.tagName){ // element
30242             return this.addElement(el);
30243         }
30244         if(typeof el == "object"){ // must be button config?
30245             return this.addButton(el);
30246         }
30247         // and now what?!?!
30248         return false;
30249         
30250     },
30251     
30252     /**
30253      * Add an Xtype element
30254      * @param {Object} xtype Xtype Object
30255      * @return {Object} created Object
30256      */
30257     addxtype : function(e){
30258         return this.add(e);  
30259     },
30260     
30261     /**
30262      * Returns the Element for this toolbar.
30263      * @return {Roo.Element}
30264      */
30265     getEl : function(){
30266         return this.el;  
30267     },
30268     
30269     /**
30270      * Adds a separator
30271      * @return {Roo.Toolbar.Item} The separator item
30272      */
30273     addSeparator : function(){
30274         return this.addItem(new Roo.Toolbar.Separator());
30275     },
30276
30277     /**
30278      * Adds a spacer element
30279      * @return {Roo.Toolbar.Spacer} The spacer item
30280      */
30281     addSpacer : function(){
30282         return this.addItem(new Roo.Toolbar.Spacer());
30283     },
30284
30285     /**
30286      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30287      * @return {Roo.Toolbar.Fill} The fill item
30288      */
30289     addFill : function(){
30290         return this.addItem(new Roo.Toolbar.Fill());
30291     },
30292
30293     /**
30294      * Adds any standard HTML element to the toolbar
30295      * @param {String/HTMLElement/Element} el The element or id of the element to add
30296      * @return {Roo.Toolbar.Item} The element's item
30297      */
30298     addElement : function(el){
30299         return this.addItem(new Roo.Toolbar.Item(el));
30300     },
30301     /**
30302      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30303      * @type Roo.util.MixedCollection  
30304      */
30305     items : false,
30306      
30307     /**
30308      * Adds any Toolbar.Item or subclass
30309      * @param {Roo.Toolbar.Item} item
30310      * @return {Roo.Toolbar.Item} The item
30311      */
30312     addItem : function(item){
30313         var td = this.nextBlock();
30314         item.render(td);
30315         this.items.add(item);
30316         return item;
30317     },
30318     
30319     /**
30320      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30321      * @param {Object/Array} config A button config or array of configs
30322      * @return {Roo.Toolbar.Button/Array}
30323      */
30324     addButton : function(config){
30325         if(config instanceof Array){
30326             var buttons = [];
30327             for(var i = 0, len = config.length; i < len; i++) {
30328                 buttons.push(this.addButton(config[i]));
30329             }
30330             return buttons;
30331         }
30332         var b = config;
30333         if(!(config instanceof Roo.Toolbar.Button)){
30334             b = config.split ?
30335                 new Roo.Toolbar.SplitButton(config) :
30336                 new Roo.Toolbar.Button(config);
30337         }
30338         var td = this.nextBlock();
30339         b.render(td);
30340         this.items.add(b);
30341         return b;
30342     },
30343     
30344     /**
30345      * Adds text to the toolbar
30346      * @param {String} text The text to add
30347      * @return {Roo.Toolbar.Item} The element's item
30348      */
30349     addText : function(text){
30350         return this.addItem(new Roo.Toolbar.TextItem(text));
30351     },
30352     
30353     /**
30354      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30355      * @param {Number} index The index where the item is to be inserted
30356      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30357      * @return {Roo.Toolbar.Button/Item}
30358      */
30359     insertButton : function(index, item){
30360         if(item instanceof Array){
30361             var buttons = [];
30362             for(var i = 0, len = item.length; i < len; i++) {
30363                buttons.push(this.insertButton(index + i, item[i]));
30364             }
30365             return buttons;
30366         }
30367         if (!(item instanceof Roo.Toolbar.Button)){
30368            item = new Roo.Toolbar.Button(item);
30369         }
30370         var td = document.createElement("td");
30371         this.tr.insertBefore(td, this.tr.childNodes[index]);
30372         item.render(td);
30373         this.items.insert(index, item);
30374         return item;
30375     },
30376     
30377     /**
30378      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30379      * @param {Object} config
30380      * @return {Roo.Toolbar.Item} The element's item
30381      */
30382     addDom : function(config, returnEl){
30383         var td = this.nextBlock();
30384         Roo.DomHelper.overwrite(td, config);
30385         var ti = new Roo.Toolbar.Item(td.firstChild);
30386         ti.render(td);
30387         this.items.add(ti);
30388         return ti;
30389     },
30390
30391     /**
30392      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30393      * @type Roo.util.MixedCollection  
30394      */
30395     fields : false,
30396     
30397     /**
30398      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30399      * Note: the field should not have been rendered yet. For a field that has already been
30400      * rendered, use {@link #addElement}.
30401      * @param {Roo.form.Field} field
30402      * @return {Roo.ToolbarItem}
30403      */
30404      
30405       
30406     addField : function(field) {
30407         if (!this.fields) {
30408             var autoId = 0;
30409             this.fields = new Roo.util.MixedCollection(false, function(o){
30410                 return o.id || ("item" + (++autoId));
30411             });
30412
30413         }
30414         
30415         var td = this.nextBlock();
30416         field.render(td);
30417         var ti = new Roo.Toolbar.Item(td.firstChild);
30418         ti.render(td);
30419         this.items.add(ti);
30420         this.fields.add(field);
30421         return ti;
30422     },
30423     /**
30424      * Hide the toolbar
30425      * @method hide
30426      */
30427      
30428       
30429     hide : function()
30430     {
30431         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30432         this.el.child('div').hide();
30433     },
30434     /**
30435      * Show the toolbar
30436      * @method show
30437      */
30438     show : function()
30439     {
30440         this.el.child('div').show();
30441     },
30442       
30443     // private
30444     nextBlock : function(){
30445         var td = document.createElement("td");
30446         this.tr.appendChild(td);
30447         return td;
30448     },
30449
30450     // private
30451     destroy : function(){
30452         if(this.items){ // rendered?
30453             Roo.destroy.apply(Roo, this.items.items);
30454         }
30455         if(this.fields){ // rendered?
30456             Roo.destroy.apply(Roo, this.fields.items);
30457         }
30458         Roo.Element.uncache(this.el, this.tr);
30459     }
30460 };
30461
30462 /**
30463  * @class Roo.Toolbar.Item
30464  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30465  * @constructor
30466  * Creates a new Item
30467  * @param {HTMLElement} el 
30468  */
30469 Roo.Toolbar.Item = function(el){
30470     var cfg = {};
30471     if (typeof (el.xtype) != 'undefined') {
30472         cfg = el;
30473         el = cfg.el;
30474     }
30475     
30476     this.el = Roo.getDom(el);
30477     this.id = Roo.id(this.el);
30478     this.hidden = false;
30479     
30480     this.addEvents({
30481          /**
30482              * @event render
30483              * Fires when the button is rendered
30484              * @param {Button} this
30485              */
30486         'render': true
30487     });
30488     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30489 };
30490 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30491 //Roo.Toolbar.Item.prototype = {
30492     
30493     /**
30494      * Get this item's HTML Element
30495      * @return {HTMLElement}
30496      */
30497     getEl : function(){
30498        return this.el;  
30499     },
30500
30501     // private
30502     render : function(td){
30503         
30504          this.td = td;
30505         td.appendChild(this.el);
30506         
30507         this.fireEvent('render', this);
30508     },
30509     
30510     /**
30511      * Removes and destroys this item.
30512      */
30513     destroy : function(){
30514         this.td.parentNode.removeChild(this.td);
30515     },
30516     
30517     /**
30518      * Shows this item.
30519      */
30520     show: function(){
30521         this.hidden = false;
30522         this.td.style.display = "";
30523     },
30524     
30525     /**
30526      * Hides this item.
30527      */
30528     hide: function(){
30529         this.hidden = true;
30530         this.td.style.display = "none";
30531     },
30532     
30533     /**
30534      * Convenience function for boolean show/hide.
30535      * @param {Boolean} visible true to show/false to hide
30536      */
30537     setVisible: function(visible){
30538         if(visible) {
30539             this.show();
30540         }else{
30541             this.hide();
30542         }
30543     },
30544     
30545     /**
30546      * Try to focus this item.
30547      */
30548     focus : function(){
30549         Roo.fly(this.el).focus();
30550     },
30551     
30552     /**
30553      * Disables this item.
30554      */
30555     disable : function(){
30556         Roo.fly(this.td).addClass("x-item-disabled");
30557         this.disabled = true;
30558         this.el.disabled = true;
30559     },
30560     
30561     /**
30562      * Enables this item.
30563      */
30564     enable : function(){
30565         Roo.fly(this.td).removeClass("x-item-disabled");
30566         this.disabled = false;
30567         this.el.disabled = false;
30568     }
30569 });
30570
30571
30572 /**
30573  * @class Roo.Toolbar.Separator
30574  * @extends Roo.Toolbar.Item
30575  * A simple toolbar separator class
30576  * @constructor
30577  * Creates a new Separator
30578  */
30579 Roo.Toolbar.Separator = function(cfg){
30580     
30581     var s = document.createElement("span");
30582     s.className = "ytb-sep";
30583     if (cfg) {
30584         cfg.el = s;
30585     }
30586     
30587     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30588 };
30589 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30590     enable:Roo.emptyFn,
30591     disable:Roo.emptyFn,
30592     focus:Roo.emptyFn
30593 });
30594
30595 /**
30596  * @class Roo.Toolbar.Spacer
30597  * @extends Roo.Toolbar.Item
30598  * A simple element that adds extra horizontal space to a toolbar.
30599  * @constructor
30600  * Creates a new Spacer
30601  */
30602 Roo.Toolbar.Spacer = function(cfg){
30603     var s = document.createElement("div");
30604     s.className = "ytb-spacer";
30605     if (cfg) {
30606         cfg.el = s;
30607     }
30608     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30609 };
30610 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30611     enable:Roo.emptyFn,
30612     disable:Roo.emptyFn,
30613     focus:Roo.emptyFn
30614 });
30615
30616 /**
30617  * @class Roo.Toolbar.Fill
30618  * @extends Roo.Toolbar.Spacer
30619  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30620  * @constructor
30621  * Creates a new Spacer
30622  */
30623 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30624     // private
30625     render : function(td){
30626         td.style.width = '100%';
30627         Roo.Toolbar.Fill.superclass.render.call(this, td);
30628     }
30629 });
30630
30631 /**
30632  * @class Roo.Toolbar.TextItem
30633  * @extends Roo.Toolbar.Item
30634  * A simple class that renders text directly into a toolbar.
30635  * @constructor
30636  * Creates a new TextItem
30637  * @cfg {string} text 
30638  */
30639 Roo.Toolbar.TextItem = function(cfg){
30640     var  text = cfg || "";
30641     if (typeof(cfg) == 'object') {
30642         text = cfg.text || "";
30643     }  else {
30644         cfg = null;
30645     }
30646     var s = document.createElement("span");
30647     s.className = "ytb-text";
30648     s.innerHTML = text;
30649     if (cfg) {
30650         cfg.el  = s;
30651     }
30652     
30653     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30654 };
30655 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30656     
30657      
30658     enable:Roo.emptyFn,
30659     disable:Roo.emptyFn,
30660     focus:Roo.emptyFn
30661 });
30662
30663 /**
30664  * @class Roo.Toolbar.Button
30665  * @extends Roo.Button
30666  * A button that renders into a toolbar.
30667  * @constructor
30668  * Creates a new Button
30669  * @param {Object} config A standard {@link Roo.Button} config object
30670  */
30671 Roo.Toolbar.Button = function(config){
30672     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30673 };
30674 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30675     render : function(td){
30676         this.td = td;
30677         Roo.Toolbar.Button.superclass.render.call(this, td);
30678     },
30679     
30680     /**
30681      * Removes and destroys this button
30682      */
30683     destroy : function(){
30684         Roo.Toolbar.Button.superclass.destroy.call(this);
30685         this.td.parentNode.removeChild(this.td);
30686     },
30687     
30688     /**
30689      * Shows this button
30690      */
30691     show: function(){
30692         this.hidden = false;
30693         this.td.style.display = "";
30694     },
30695     
30696     /**
30697      * Hides this button
30698      */
30699     hide: function(){
30700         this.hidden = true;
30701         this.td.style.display = "none";
30702     },
30703
30704     /**
30705      * Disables this item
30706      */
30707     disable : function(){
30708         Roo.fly(this.td).addClass("x-item-disabled");
30709         this.disabled = true;
30710     },
30711
30712     /**
30713      * Enables this item
30714      */
30715     enable : function(){
30716         Roo.fly(this.td).removeClass("x-item-disabled");
30717         this.disabled = false;
30718     }
30719 });
30720 // backwards compat
30721 Roo.ToolbarButton = Roo.Toolbar.Button;
30722
30723 /**
30724  * @class Roo.Toolbar.SplitButton
30725  * @extends Roo.SplitButton
30726  * A menu button that renders into a toolbar.
30727  * @constructor
30728  * Creates a new SplitButton
30729  * @param {Object} config A standard {@link Roo.SplitButton} config object
30730  */
30731 Roo.Toolbar.SplitButton = function(config){
30732     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30733 };
30734 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30735     render : function(td){
30736         this.td = td;
30737         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30738     },
30739     
30740     /**
30741      * Removes and destroys this button
30742      */
30743     destroy : function(){
30744         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30745         this.td.parentNode.removeChild(this.td);
30746     },
30747     
30748     /**
30749      * Shows this button
30750      */
30751     show: function(){
30752         this.hidden = false;
30753         this.td.style.display = "";
30754     },
30755     
30756     /**
30757      * Hides this button
30758      */
30759     hide: function(){
30760         this.hidden = true;
30761         this.td.style.display = "none";
30762     }
30763 });
30764
30765 // backwards compat
30766 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30767  * Based on:
30768  * Ext JS Library 1.1.1
30769  * Copyright(c) 2006-2007, Ext JS, LLC.
30770  *
30771  * Originally Released Under LGPL - original licence link has changed is not relivant.
30772  *
30773  * Fork - LGPL
30774  * <script type="text/javascript">
30775  */
30776  
30777 /**
30778  * @class Roo.PagingToolbar
30779  * @extends Roo.Toolbar
30780  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30781  * @constructor
30782  * Create a new PagingToolbar
30783  * @param {Object} config The config object
30784  */
30785 Roo.PagingToolbar = function(el, ds, config)
30786 {
30787     // old args format still supported... - xtype is prefered..
30788     if (typeof(el) == 'object' && el.xtype) {
30789         // created from xtype...
30790         config = el;
30791         ds = el.dataSource;
30792         el = config.container;
30793     }
30794     var items = [];
30795     if (config.items) {
30796         items = config.items;
30797         config.items = [];
30798     }
30799     
30800     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30801     this.ds = ds;
30802     this.cursor = 0;
30803     this.renderButtons(this.el);
30804     this.bind(ds);
30805     
30806     // supprot items array.
30807    
30808     Roo.each(items, function(e) {
30809         this.add(Roo.factory(e));
30810     },this);
30811     
30812 };
30813
30814 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30815     /**
30816      * @cfg {Roo.data.Store} dataSource
30817      * The underlying data store providing the paged data
30818      */
30819     /**
30820      * @cfg {String/HTMLElement/Element} container
30821      * container The id or element that will contain the toolbar
30822      */
30823     /**
30824      * @cfg {Boolean} displayInfo
30825      * True to display the displayMsg (defaults to false)
30826      */
30827     /**
30828      * @cfg {Number} pageSize
30829      * The number of records to display per page (defaults to 20)
30830      */
30831     pageSize: 20,
30832     /**
30833      * @cfg {String} displayMsg
30834      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30835      */
30836     displayMsg : 'Displaying {0} - {1} of {2}',
30837     /**
30838      * @cfg {String} emptyMsg
30839      * The message to display when no records are found (defaults to "No data to display")
30840      */
30841     emptyMsg : 'No data to display',
30842     /**
30843      * Customizable piece of the default paging text (defaults to "Page")
30844      * @type String
30845      */
30846     beforePageText : "Page",
30847     /**
30848      * Customizable piece of the default paging text (defaults to "of %0")
30849      * @type String
30850      */
30851     afterPageText : "of {0}",
30852     /**
30853      * Customizable piece of the default paging text (defaults to "First Page")
30854      * @type String
30855      */
30856     firstText : "First Page",
30857     /**
30858      * Customizable piece of the default paging text (defaults to "Previous Page")
30859      * @type String
30860      */
30861     prevText : "Previous Page",
30862     /**
30863      * Customizable piece of the default paging text (defaults to "Next Page")
30864      * @type String
30865      */
30866     nextText : "Next Page",
30867     /**
30868      * Customizable piece of the default paging text (defaults to "Last Page")
30869      * @type String
30870      */
30871     lastText : "Last Page",
30872     /**
30873      * Customizable piece of the default paging text (defaults to "Refresh")
30874      * @type String
30875      */
30876     refreshText : "Refresh",
30877
30878     // private
30879     renderButtons : function(el){
30880         Roo.PagingToolbar.superclass.render.call(this, el);
30881         this.first = this.addButton({
30882             tooltip: this.firstText,
30883             cls: "x-btn-icon x-grid-page-first",
30884             disabled: true,
30885             handler: this.onClick.createDelegate(this, ["first"])
30886         });
30887         this.prev = this.addButton({
30888             tooltip: this.prevText,
30889             cls: "x-btn-icon x-grid-page-prev",
30890             disabled: true,
30891             handler: this.onClick.createDelegate(this, ["prev"])
30892         });
30893         //this.addSeparator();
30894         this.add(this.beforePageText);
30895         this.field = Roo.get(this.addDom({
30896            tag: "input",
30897            type: "text",
30898            size: "3",
30899            value: "1",
30900            cls: "x-grid-page-number"
30901         }).el);
30902         this.field.on("keydown", this.onPagingKeydown, this);
30903         this.field.on("focus", function(){this.dom.select();});
30904         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30905         this.field.setHeight(18);
30906         //this.addSeparator();
30907         this.next = this.addButton({
30908             tooltip: this.nextText,
30909             cls: "x-btn-icon x-grid-page-next",
30910             disabled: true,
30911             handler: this.onClick.createDelegate(this, ["next"])
30912         });
30913         this.last = this.addButton({
30914             tooltip: this.lastText,
30915             cls: "x-btn-icon x-grid-page-last",
30916             disabled: true,
30917             handler: this.onClick.createDelegate(this, ["last"])
30918         });
30919         //this.addSeparator();
30920         this.loading = this.addButton({
30921             tooltip: this.refreshText,
30922             cls: "x-btn-icon x-grid-loading",
30923             handler: this.onClick.createDelegate(this, ["refresh"])
30924         });
30925
30926         if(this.displayInfo){
30927             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30928         }
30929     },
30930
30931     // private
30932     updateInfo : function(){
30933         if(this.displayEl){
30934             var count = this.ds.getCount();
30935             var msg = count == 0 ?
30936                 this.emptyMsg :
30937                 String.format(
30938                     this.displayMsg,
30939                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30940                 );
30941             this.displayEl.update(msg);
30942         }
30943     },
30944
30945     // private
30946     onLoad : function(ds, r, o){
30947        this.cursor = o.params ? o.params.start : 0;
30948        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30949
30950        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30951        this.field.dom.value = ap;
30952        this.first.setDisabled(ap == 1);
30953        this.prev.setDisabled(ap == 1);
30954        this.next.setDisabled(ap == ps);
30955        this.last.setDisabled(ap == ps);
30956        this.loading.enable();
30957        this.updateInfo();
30958     },
30959
30960     // private
30961     getPageData : function(){
30962         var total = this.ds.getTotalCount();
30963         return {
30964             total : total,
30965             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30966             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30967         };
30968     },
30969
30970     // private
30971     onLoadError : function(){
30972         this.loading.enable();
30973     },
30974
30975     // private
30976     onPagingKeydown : function(e){
30977         var k = e.getKey();
30978         var d = this.getPageData();
30979         if(k == e.RETURN){
30980             var v = this.field.dom.value, pageNum;
30981             if(!v || isNaN(pageNum = parseInt(v, 10))){
30982                 this.field.dom.value = d.activePage;
30983                 return;
30984             }
30985             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30986             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30987             e.stopEvent();
30988         }
30989         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))
30990         {
30991           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30992           this.field.dom.value = pageNum;
30993           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30994           e.stopEvent();
30995         }
30996         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30997         {
30998           var v = this.field.dom.value, pageNum; 
30999           var increment = (e.shiftKey) ? 10 : 1;
31000           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31001             increment *= -1;
31002           }
31003           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31004             this.field.dom.value = d.activePage;
31005             return;
31006           }
31007           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31008           {
31009             this.field.dom.value = parseInt(v, 10) + increment;
31010             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31011             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31012           }
31013           e.stopEvent();
31014         }
31015     },
31016
31017     // private
31018     beforeLoad : function(){
31019         if(this.loading){
31020             this.loading.disable();
31021         }
31022     },
31023
31024     // private
31025     onClick : function(which){
31026         var ds = this.ds;
31027         switch(which){
31028             case "first":
31029                 ds.load({params:{start: 0, limit: this.pageSize}});
31030             break;
31031             case "prev":
31032                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31033             break;
31034             case "next":
31035                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31036             break;
31037             case "last":
31038                 var total = ds.getTotalCount();
31039                 var extra = total % this.pageSize;
31040                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31041                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31042             break;
31043             case "refresh":
31044                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31045             break;
31046         }
31047     },
31048
31049     /**
31050      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31051      * @param {Roo.data.Store} store The data store to unbind
31052      */
31053     unbind : function(ds){
31054         ds.un("beforeload", this.beforeLoad, this);
31055         ds.un("load", this.onLoad, this);
31056         ds.un("loadexception", this.onLoadError, this);
31057         ds.un("remove", this.updateInfo, this);
31058         ds.un("add", this.updateInfo, this);
31059         this.ds = undefined;
31060     },
31061
31062     /**
31063      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31064      * @param {Roo.data.Store} store The data store to bind
31065      */
31066     bind : function(ds){
31067         ds.on("beforeload", this.beforeLoad, this);
31068         ds.on("load", this.onLoad, this);
31069         ds.on("loadexception", this.onLoadError, this);
31070         ds.on("remove", this.updateInfo, this);
31071         ds.on("add", this.updateInfo, this);
31072         this.ds = ds;
31073     }
31074 });/*
31075  * Based on:
31076  * Ext JS Library 1.1.1
31077  * Copyright(c) 2006-2007, Ext JS, LLC.
31078  *
31079  * Originally Released Under LGPL - original licence link has changed is not relivant.
31080  *
31081  * Fork - LGPL
31082  * <script type="text/javascript">
31083  */
31084
31085 /**
31086  * @class Roo.Resizable
31087  * @extends Roo.util.Observable
31088  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31089  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31090  * 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
31091  * the element will be wrapped for you automatically.</p>
31092  * <p>Here is the list of valid resize handles:</p>
31093  * <pre>
31094 Value   Description
31095 ------  -------------------
31096  'n'     north
31097  's'     south
31098  'e'     east
31099  'w'     west
31100  'nw'    northwest
31101  'sw'    southwest
31102  'se'    southeast
31103  'ne'    northeast
31104  'hd'    horizontal drag
31105  'all'   all
31106 </pre>
31107  * <p>Here's an example showing the creation of a typical Resizable:</p>
31108  * <pre><code>
31109 var resizer = new Roo.Resizable("element-id", {
31110     handles: 'all',
31111     minWidth: 200,
31112     minHeight: 100,
31113     maxWidth: 500,
31114     maxHeight: 400,
31115     pinned: true
31116 });
31117 resizer.on("resize", myHandler);
31118 </code></pre>
31119  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31120  * resizer.east.setDisplayed(false);</p>
31121  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31122  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31123  * resize operation's new size (defaults to [0, 0])
31124  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31125  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31126  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31127  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31128  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31129  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31130  * @cfg {Number} width The width of the element in pixels (defaults to null)
31131  * @cfg {Number} height The height of the element in pixels (defaults to null)
31132  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31133  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31134  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31135  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31136  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31137  * in favor of the handles config option (defaults to false)
31138  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31139  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31140  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31141  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31142  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31143  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31144  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31145  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31146  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31147  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31148  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31149  * @constructor
31150  * Create a new resizable component
31151  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31152  * @param {Object} config configuration options
31153   */
31154 Roo.Resizable = function(el, config)
31155 {
31156     this.el = Roo.get(el);
31157
31158     if(config && config.wrap){
31159         config.resizeChild = this.el;
31160         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31161         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31162         this.el.setStyle("overflow", "hidden");
31163         this.el.setPositioning(config.resizeChild.getPositioning());
31164         config.resizeChild.clearPositioning();
31165         if(!config.width || !config.height){
31166             var csize = config.resizeChild.getSize();
31167             this.el.setSize(csize.width, csize.height);
31168         }
31169         if(config.pinned && !config.adjustments){
31170             config.adjustments = "auto";
31171         }
31172     }
31173
31174     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31175     this.proxy.unselectable();
31176     this.proxy.enableDisplayMode('block');
31177
31178     Roo.apply(this, config);
31179
31180     if(this.pinned){
31181         this.disableTrackOver = true;
31182         this.el.addClass("x-resizable-pinned");
31183     }
31184     // if the element isn't positioned, make it relative
31185     var position = this.el.getStyle("position");
31186     if(position != "absolute" && position != "fixed"){
31187         this.el.setStyle("position", "relative");
31188     }
31189     if(!this.handles){ // no handles passed, must be legacy style
31190         this.handles = 's,e,se';
31191         if(this.multiDirectional){
31192             this.handles += ',n,w';
31193         }
31194     }
31195     if(this.handles == "all"){
31196         this.handles = "n s e w ne nw se sw";
31197     }
31198     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31199     var ps = Roo.Resizable.positions;
31200     for(var i = 0, len = hs.length; i < len; i++){
31201         if(hs[i] && ps[hs[i]]){
31202             var pos = ps[hs[i]];
31203             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31204         }
31205     }
31206     // legacy
31207     this.corner = this.southeast;
31208     
31209     // updateBox = the box can move..
31210     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31211         this.updateBox = true;
31212     }
31213
31214     this.activeHandle = null;
31215
31216     if(this.resizeChild){
31217         if(typeof this.resizeChild == "boolean"){
31218             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31219         }else{
31220             this.resizeChild = Roo.get(this.resizeChild, true);
31221         }
31222     }
31223     
31224     if(this.adjustments == "auto"){
31225         var rc = this.resizeChild;
31226         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31227         if(rc && (hw || hn)){
31228             rc.position("relative");
31229             rc.setLeft(hw ? hw.el.getWidth() : 0);
31230             rc.setTop(hn ? hn.el.getHeight() : 0);
31231         }
31232         this.adjustments = [
31233             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31234             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31235         ];
31236     }
31237
31238     if(this.draggable){
31239         this.dd = this.dynamic ?
31240             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31241         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31242     }
31243
31244     // public events
31245     this.addEvents({
31246         /**
31247          * @event beforeresize
31248          * Fired before resize is allowed. Set enabled to false to cancel resize.
31249          * @param {Roo.Resizable} this
31250          * @param {Roo.EventObject} e The mousedown event
31251          */
31252         "beforeresize" : true,
31253         /**
31254          * @event resizing
31255          * Fired a resizing.
31256          * @param {Roo.Resizable} this
31257          * @param {Number} x The new x position
31258          * @param {Number} y The new y position
31259          * @param {Number} w The new w width
31260          * @param {Number} h The new h hight
31261          * @param {Roo.EventObject} e The mouseup event
31262          */
31263         "resizing" : true,
31264         /**
31265          * @event resize
31266          * Fired after a resize.
31267          * @param {Roo.Resizable} this
31268          * @param {Number} width The new width
31269          * @param {Number} height The new height
31270          * @param {Roo.EventObject} e The mouseup event
31271          */
31272         "resize" : true
31273     });
31274
31275     if(this.width !== null && this.height !== null){
31276         this.resizeTo(this.width, this.height);
31277     }else{
31278         this.updateChildSize();
31279     }
31280     if(Roo.isIE){
31281         this.el.dom.style.zoom = 1;
31282     }
31283     Roo.Resizable.superclass.constructor.call(this);
31284 };
31285
31286 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31287         resizeChild : false,
31288         adjustments : [0, 0],
31289         minWidth : 5,
31290         minHeight : 5,
31291         maxWidth : 10000,
31292         maxHeight : 10000,
31293         enabled : true,
31294         animate : false,
31295         duration : .35,
31296         dynamic : false,
31297         handles : false,
31298         multiDirectional : false,
31299         disableTrackOver : false,
31300         easing : 'easeOutStrong',
31301         widthIncrement : 0,
31302         heightIncrement : 0,
31303         pinned : false,
31304         width : null,
31305         height : null,
31306         preserveRatio : false,
31307         transparent: false,
31308         minX: 0,
31309         minY: 0,
31310         draggable: false,
31311
31312         /**
31313          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31314          */
31315         constrainTo: undefined,
31316         /**
31317          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31318          */
31319         resizeRegion: undefined,
31320
31321
31322     /**
31323      * Perform a manual resize
31324      * @param {Number} width
31325      * @param {Number} height
31326      */
31327     resizeTo : function(width, height){
31328         this.el.setSize(width, height);
31329         this.updateChildSize();
31330         this.fireEvent("resize", this, width, height, null);
31331     },
31332
31333     // private
31334     startSizing : function(e, handle){
31335         this.fireEvent("beforeresize", this, e);
31336         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31337
31338             if(!this.overlay){
31339                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31340                 this.overlay.unselectable();
31341                 this.overlay.enableDisplayMode("block");
31342                 this.overlay.on("mousemove", this.onMouseMove, this);
31343                 this.overlay.on("mouseup", this.onMouseUp, this);
31344             }
31345             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31346
31347             this.resizing = true;
31348             this.startBox = this.el.getBox();
31349             this.startPoint = e.getXY();
31350             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31351                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31352
31353             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31354             this.overlay.show();
31355
31356             if(this.constrainTo) {
31357                 var ct = Roo.get(this.constrainTo);
31358                 this.resizeRegion = ct.getRegion().adjust(
31359                     ct.getFrameWidth('t'),
31360                     ct.getFrameWidth('l'),
31361                     -ct.getFrameWidth('b'),
31362                     -ct.getFrameWidth('r')
31363                 );
31364             }
31365
31366             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31367             this.proxy.show();
31368             this.proxy.setBox(this.startBox);
31369             if(!this.dynamic){
31370                 this.proxy.setStyle('visibility', 'visible');
31371             }
31372         }
31373     },
31374
31375     // private
31376     onMouseDown : function(handle, e){
31377         if(this.enabled){
31378             e.stopEvent();
31379             this.activeHandle = handle;
31380             this.startSizing(e, handle);
31381         }
31382     },
31383
31384     // private
31385     onMouseUp : function(e){
31386         var size = this.resizeElement();
31387         this.resizing = false;
31388         this.handleOut();
31389         this.overlay.hide();
31390         this.proxy.hide();
31391         this.fireEvent("resize", this, size.width, size.height, e);
31392     },
31393
31394     // private
31395     updateChildSize : function(){
31396         
31397         if(this.resizeChild){
31398             var el = this.el;
31399             var child = this.resizeChild;
31400             var adj = this.adjustments;
31401             if(el.dom.offsetWidth){
31402                 var b = el.getSize(true);
31403                 child.setSize(b.width+adj[0], b.height+adj[1]);
31404             }
31405             // Second call here for IE
31406             // The first call enables instant resizing and
31407             // the second call corrects scroll bars if they
31408             // exist
31409             if(Roo.isIE){
31410                 setTimeout(function(){
31411                     if(el.dom.offsetWidth){
31412                         var b = el.getSize(true);
31413                         child.setSize(b.width+adj[0], b.height+adj[1]);
31414                     }
31415                 }, 10);
31416             }
31417         }
31418     },
31419
31420     // private
31421     snap : function(value, inc, min){
31422         if(!inc || !value) {
31423             return value;
31424         }
31425         var newValue = value;
31426         var m = value % inc;
31427         if(m > 0){
31428             if(m > (inc/2)){
31429                 newValue = value + (inc-m);
31430             }else{
31431                 newValue = value - m;
31432             }
31433         }
31434         return Math.max(min, newValue);
31435     },
31436
31437     // private
31438     resizeElement : function(){
31439         var box = this.proxy.getBox();
31440         if(this.updateBox){
31441             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31442         }else{
31443             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31444         }
31445         this.updateChildSize();
31446         if(!this.dynamic){
31447             this.proxy.hide();
31448         }
31449         return box;
31450     },
31451
31452     // private
31453     constrain : function(v, diff, m, mx){
31454         if(v - diff < m){
31455             diff = v - m;
31456         }else if(v - diff > mx){
31457             diff = mx - v;
31458         }
31459         return diff;
31460     },
31461
31462     // private
31463     onMouseMove : function(e){
31464         
31465         if(this.enabled){
31466             try{// try catch so if something goes wrong the user doesn't get hung
31467
31468             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31469                 return;
31470             }
31471
31472             //var curXY = this.startPoint;
31473             var curSize = this.curSize || this.startBox;
31474             var x = this.startBox.x, y = this.startBox.y;
31475             var ox = x, oy = y;
31476             var w = curSize.width, h = curSize.height;
31477             var ow = w, oh = h;
31478             var mw = this.minWidth, mh = this.minHeight;
31479             var mxw = this.maxWidth, mxh = this.maxHeight;
31480             var wi = this.widthIncrement;
31481             var hi = this.heightIncrement;
31482
31483             var eventXY = e.getXY();
31484             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31485             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31486
31487             var pos = this.activeHandle.position;
31488
31489             switch(pos){
31490                 case "east":
31491                     w += diffX;
31492                     w = Math.min(Math.max(mw, w), mxw);
31493                     break;
31494              
31495                 case "south":
31496                     h += diffY;
31497                     h = Math.min(Math.max(mh, h), mxh);
31498                     break;
31499                 case "southeast":
31500                     w += diffX;
31501                     h += diffY;
31502                     w = Math.min(Math.max(mw, w), mxw);
31503                     h = Math.min(Math.max(mh, h), mxh);
31504                     break;
31505                 case "north":
31506                     diffY = this.constrain(h, diffY, mh, mxh);
31507                     y += diffY;
31508                     h -= diffY;
31509                     break;
31510                 case "hdrag":
31511                     
31512                     if (wi) {
31513                         var adiffX = Math.abs(diffX);
31514                         var sub = (adiffX % wi); // how much 
31515                         if (sub > (wi/2)) { // far enough to snap
31516                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31517                         } else {
31518                             // remove difference.. 
31519                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31520                         }
31521                     }
31522                     x += diffX;
31523                     x = Math.max(this.minX, x);
31524                     break;
31525                 case "west":
31526                     diffX = this.constrain(w, diffX, mw, mxw);
31527                     x += diffX;
31528                     w -= diffX;
31529                     break;
31530                 case "northeast":
31531                     w += diffX;
31532                     w = Math.min(Math.max(mw, w), mxw);
31533                     diffY = this.constrain(h, diffY, mh, mxh);
31534                     y += diffY;
31535                     h -= diffY;
31536                     break;
31537                 case "northwest":
31538                     diffX = this.constrain(w, diffX, mw, mxw);
31539                     diffY = this.constrain(h, diffY, mh, mxh);
31540                     y += diffY;
31541                     h -= diffY;
31542                     x += diffX;
31543                     w -= diffX;
31544                     break;
31545                case "southwest":
31546                     diffX = this.constrain(w, diffX, mw, mxw);
31547                     h += diffY;
31548                     h = Math.min(Math.max(mh, h), mxh);
31549                     x += diffX;
31550                     w -= diffX;
31551                     break;
31552             }
31553
31554             var sw = this.snap(w, wi, mw);
31555             var sh = this.snap(h, hi, mh);
31556             if(sw != w || sh != h){
31557                 switch(pos){
31558                     case "northeast":
31559                         y -= sh - h;
31560                     break;
31561                     case "north":
31562                         y -= sh - h;
31563                         break;
31564                     case "southwest":
31565                         x -= sw - w;
31566                     break;
31567                     case "west":
31568                         x -= sw - w;
31569                         break;
31570                     case "northwest":
31571                         x -= sw - w;
31572                         y -= sh - h;
31573                     break;
31574                 }
31575                 w = sw;
31576                 h = sh;
31577             }
31578
31579             if(this.preserveRatio){
31580                 switch(pos){
31581                     case "southeast":
31582                     case "east":
31583                         h = oh * (w/ow);
31584                         h = Math.min(Math.max(mh, h), mxh);
31585                         w = ow * (h/oh);
31586                        break;
31587                     case "south":
31588                         w = ow * (h/oh);
31589                         w = Math.min(Math.max(mw, w), mxw);
31590                         h = oh * (w/ow);
31591                         break;
31592                     case "northeast":
31593                         w = ow * (h/oh);
31594                         w = Math.min(Math.max(mw, w), mxw);
31595                         h = oh * (w/ow);
31596                     break;
31597                     case "north":
31598                         var tw = w;
31599                         w = ow * (h/oh);
31600                         w = Math.min(Math.max(mw, w), mxw);
31601                         h = oh * (w/ow);
31602                         x += (tw - w) / 2;
31603                         break;
31604                     case "southwest":
31605                         h = oh * (w/ow);
31606                         h = Math.min(Math.max(mh, h), mxh);
31607                         var tw = w;
31608                         w = ow * (h/oh);
31609                         x += tw - w;
31610                         break;
31611                     case "west":
31612                         var th = h;
31613                         h = oh * (w/ow);
31614                         h = Math.min(Math.max(mh, h), mxh);
31615                         y += (th - h) / 2;
31616                         var tw = w;
31617                         w = ow * (h/oh);
31618                         x += tw - w;
31619                        break;
31620                     case "northwest":
31621                         var tw = w;
31622                         var th = h;
31623                         h = oh * (w/ow);
31624                         h = Math.min(Math.max(mh, h), mxh);
31625                         w = ow * (h/oh);
31626                         y += th - h;
31627                         x += tw - w;
31628                        break;
31629
31630                 }
31631             }
31632             if (pos == 'hdrag') {
31633                 w = ow;
31634             }
31635             this.proxy.setBounds(x, y, w, h);
31636             if(this.dynamic){
31637                 this.resizeElement();
31638             }
31639             }catch(e){}
31640         }
31641         this.fireEvent("resizing", this, x, y, w, h, e);
31642     },
31643
31644     // private
31645     handleOver : function(){
31646         if(this.enabled){
31647             this.el.addClass("x-resizable-over");
31648         }
31649     },
31650
31651     // private
31652     handleOut : function(){
31653         if(!this.resizing){
31654             this.el.removeClass("x-resizable-over");
31655         }
31656     },
31657
31658     /**
31659      * Returns the element this component is bound to.
31660      * @return {Roo.Element}
31661      */
31662     getEl : function(){
31663         return this.el;
31664     },
31665
31666     /**
31667      * Returns the resizeChild element (or null).
31668      * @return {Roo.Element}
31669      */
31670     getResizeChild : function(){
31671         return this.resizeChild;
31672     },
31673     groupHandler : function()
31674     {
31675         
31676     },
31677     /**
31678      * Destroys this resizable. If the element was wrapped and
31679      * removeEl is not true then the element remains.
31680      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31681      */
31682     destroy : function(removeEl){
31683         this.proxy.remove();
31684         if(this.overlay){
31685             this.overlay.removeAllListeners();
31686             this.overlay.remove();
31687         }
31688         var ps = Roo.Resizable.positions;
31689         for(var k in ps){
31690             if(typeof ps[k] != "function" && this[ps[k]]){
31691                 var h = this[ps[k]];
31692                 h.el.removeAllListeners();
31693                 h.el.remove();
31694             }
31695         }
31696         if(removeEl){
31697             this.el.update("");
31698             this.el.remove();
31699         }
31700     }
31701 });
31702
31703 // private
31704 // hash to map config positions to true positions
31705 Roo.Resizable.positions = {
31706     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31707     hd: "hdrag"
31708 };
31709
31710 // private
31711 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31712     if(!this.tpl){
31713         // only initialize the template if resizable is used
31714         var tpl = Roo.DomHelper.createTemplate(
31715             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31716         );
31717         tpl.compile();
31718         Roo.Resizable.Handle.prototype.tpl = tpl;
31719     }
31720     this.position = pos;
31721     this.rz = rz;
31722     // show north drag fro topdra
31723     var handlepos = pos == 'hdrag' ? 'north' : pos;
31724     
31725     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31726     if (pos == 'hdrag') {
31727         this.el.setStyle('cursor', 'pointer');
31728     }
31729     this.el.unselectable();
31730     if(transparent){
31731         this.el.setOpacity(0);
31732     }
31733     this.el.on("mousedown", this.onMouseDown, this);
31734     if(!disableTrackOver){
31735         this.el.on("mouseover", this.onMouseOver, this);
31736         this.el.on("mouseout", this.onMouseOut, this);
31737     }
31738 };
31739
31740 // private
31741 Roo.Resizable.Handle.prototype = {
31742     afterResize : function(rz){
31743         Roo.log('after?');
31744         // do nothing
31745     },
31746     // private
31747     onMouseDown : function(e){
31748         this.rz.onMouseDown(this, e);
31749     },
31750     // private
31751     onMouseOver : function(e){
31752         this.rz.handleOver(this, e);
31753     },
31754     // private
31755     onMouseOut : function(e){
31756         this.rz.handleOut(this, e);
31757     }
31758 };/*
31759  * Based on:
31760  * Ext JS Library 1.1.1
31761  * Copyright(c) 2006-2007, Ext JS, LLC.
31762  *
31763  * Originally Released Under LGPL - original licence link has changed is not relivant.
31764  *
31765  * Fork - LGPL
31766  * <script type="text/javascript">
31767  */
31768
31769 /**
31770  * @class Roo.Editor
31771  * @extends Roo.Component
31772  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31773  * @constructor
31774  * Create a new Editor
31775  * @param {Roo.form.Field} field The Field object (or descendant)
31776  * @param {Object} config The config object
31777  */
31778 Roo.Editor = function(field, config){
31779     Roo.Editor.superclass.constructor.call(this, config);
31780     this.field = field;
31781     this.addEvents({
31782         /**
31783              * @event beforestartedit
31784              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31785              * false from the handler of this event.
31786              * @param {Editor} this
31787              * @param {Roo.Element} boundEl The underlying element bound to this editor
31788              * @param {Mixed} value The field value being set
31789              */
31790         "beforestartedit" : true,
31791         /**
31792              * @event startedit
31793              * Fires when this editor is displayed
31794              * @param {Roo.Element} boundEl The underlying element bound to this editor
31795              * @param {Mixed} value The starting field value
31796              */
31797         "startedit" : true,
31798         /**
31799              * @event beforecomplete
31800              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31801              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31802              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31803              * event will not fire since no edit actually occurred.
31804              * @param {Editor} this
31805              * @param {Mixed} value The current field value
31806              * @param {Mixed} startValue The original field value
31807              */
31808         "beforecomplete" : true,
31809         /**
31810              * @event complete
31811              * Fires after editing is complete and any changed value has been written to the underlying field.
31812              * @param {Editor} this
31813              * @param {Mixed} value The current field value
31814              * @param {Mixed} startValue The original field value
31815              */
31816         "complete" : true,
31817         /**
31818          * @event specialkey
31819          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31820          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31821          * @param {Roo.form.Field} this
31822          * @param {Roo.EventObject} e The event object
31823          */
31824         "specialkey" : true
31825     });
31826 };
31827
31828 Roo.extend(Roo.Editor, Roo.Component, {
31829     /**
31830      * @cfg {Boolean/String} autosize
31831      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31832      * or "height" to adopt the height only (defaults to false)
31833      */
31834     /**
31835      * @cfg {Boolean} revertInvalid
31836      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31837      * validation fails (defaults to true)
31838      */
31839     /**
31840      * @cfg {Boolean} ignoreNoChange
31841      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31842      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31843      * will never be ignored.
31844      */
31845     /**
31846      * @cfg {Boolean} hideEl
31847      * False to keep the bound element visible while the editor is displayed (defaults to true)
31848      */
31849     /**
31850      * @cfg {Mixed} value
31851      * The data value of the underlying field (defaults to "")
31852      */
31853     value : "",
31854     /**
31855      * @cfg {String} alignment
31856      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31857      */
31858     alignment: "c-c?",
31859     /**
31860      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31861      * for bottom-right shadow (defaults to "frame")
31862      */
31863     shadow : "frame",
31864     /**
31865      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31866      */
31867     constrain : false,
31868     /**
31869      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31870      */
31871     completeOnEnter : false,
31872     /**
31873      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31874      */
31875     cancelOnEsc : false,
31876     /**
31877      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31878      */
31879     updateEl : false,
31880
31881     // private
31882     onRender : function(ct, position){
31883         this.el = new Roo.Layer({
31884             shadow: this.shadow,
31885             cls: "x-editor",
31886             parentEl : ct,
31887             shim : this.shim,
31888             shadowOffset:4,
31889             id: this.id,
31890             constrain: this.constrain
31891         });
31892         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31893         if(this.field.msgTarget != 'title'){
31894             this.field.msgTarget = 'qtip';
31895         }
31896         this.field.render(this.el);
31897         if(Roo.isGecko){
31898             this.field.el.dom.setAttribute('autocomplete', 'off');
31899         }
31900         this.field.on("specialkey", this.onSpecialKey, this);
31901         if(this.swallowKeys){
31902             this.field.el.swallowEvent(['keydown','keypress']);
31903         }
31904         this.field.show();
31905         this.field.on("blur", this.onBlur, this);
31906         if(this.field.grow){
31907             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31908         }
31909     },
31910
31911     onSpecialKey : function(field, e)
31912     {
31913         //Roo.log('editor onSpecialKey');
31914         if(this.completeOnEnter && e.getKey() == e.ENTER){
31915             e.stopEvent();
31916             this.completeEdit();
31917             return;
31918         }
31919         // do not fire special key otherwise it might hide close the editor...
31920         if(e.getKey() == e.ENTER){    
31921             return;
31922         }
31923         if(this.cancelOnEsc && e.getKey() == e.ESC){
31924             this.cancelEdit();
31925             return;
31926         } 
31927         this.fireEvent('specialkey', field, e);
31928     
31929     },
31930
31931     /**
31932      * Starts the editing process and shows the editor.
31933      * @param {String/HTMLElement/Element} el The element to edit
31934      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31935       * to the innerHTML of el.
31936      */
31937     startEdit : function(el, value){
31938         if(this.editing){
31939             this.completeEdit();
31940         }
31941         this.boundEl = Roo.get(el);
31942         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31943         if(!this.rendered){
31944             this.render(this.parentEl || document.body);
31945         }
31946         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31947             return;
31948         }
31949         this.startValue = v;
31950         this.field.setValue(v);
31951         if(this.autoSize){
31952             var sz = this.boundEl.getSize();
31953             switch(this.autoSize){
31954                 case "width":
31955                 this.setSize(sz.width,  "");
31956                 break;
31957                 case "height":
31958                 this.setSize("",  sz.height);
31959                 break;
31960                 default:
31961                 this.setSize(sz.width,  sz.height);
31962             }
31963         }
31964         this.el.alignTo(this.boundEl, this.alignment);
31965         this.editing = true;
31966         if(Roo.QuickTips){
31967             Roo.QuickTips.disable();
31968         }
31969         this.show();
31970     },
31971
31972     /**
31973      * Sets the height and width of this editor.
31974      * @param {Number} width The new width
31975      * @param {Number} height The new height
31976      */
31977     setSize : function(w, h){
31978         this.field.setSize(w, h);
31979         if(this.el){
31980             this.el.sync();
31981         }
31982     },
31983
31984     /**
31985      * Realigns the editor to the bound field based on the current alignment config value.
31986      */
31987     realign : function(){
31988         this.el.alignTo(this.boundEl, this.alignment);
31989     },
31990
31991     /**
31992      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31993      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31994      */
31995     completeEdit : function(remainVisible){
31996         if(!this.editing){
31997             return;
31998         }
31999         var v = this.getValue();
32000         if(this.revertInvalid !== false && !this.field.isValid()){
32001             v = this.startValue;
32002             this.cancelEdit(true);
32003         }
32004         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32005             this.editing = false;
32006             this.hide();
32007             return;
32008         }
32009         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32010             this.editing = false;
32011             if(this.updateEl && this.boundEl){
32012                 this.boundEl.update(v);
32013             }
32014             if(remainVisible !== true){
32015                 this.hide();
32016             }
32017             this.fireEvent("complete", this, v, this.startValue);
32018         }
32019     },
32020
32021     // private
32022     onShow : function(){
32023         this.el.show();
32024         if(this.hideEl !== false){
32025             this.boundEl.hide();
32026         }
32027         this.field.show();
32028         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32029             this.fixIEFocus = true;
32030             this.deferredFocus.defer(50, this);
32031         }else{
32032             this.field.focus();
32033         }
32034         this.fireEvent("startedit", this.boundEl, this.startValue);
32035     },
32036
32037     deferredFocus : function(){
32038         if(this.editing){
32039             this.field.focus();
32040         }
32041     },
32042
32043     /**
32044      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32045      * reverted to the original starting value.
32046      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32047      * cancel (defaults to false)
32048      */
32049     cancelEdit : function(remainVisible){
32050         if(this.editing){
32051             this.setValue(this.startValue);
32052             if(remainVisible !== true){
32053                 this.hide();
32054             }
32055         }
32056     },
32057
32058     // private
32059     onBlur : function(){
32060         if(this.allowBlur !== true && this.editing){
32061             this.completeEdit();
32062         }
32063     },
32064
32065     // private
32066     onHide : function(){
32067         if(this.editing){
32068             this.completeEdit();
32069             return;
32070         }
32071         this.field.blur();
32072         if(this.field.collapse){
32073             this.field.collapse();
32074         }
32075         this.el.hide();
32076         if(this.hideEl !== false){
32077             this.boundEl.show();
32078         }
32079         if(Roo.QuickTips){
32080             Roo.QuickTips.enable();
32081         }
32082     },
32083
32084     /**
32085      * Sets the data value of the editor
32086      * @param {Mixed} value Any valid value supported by the underlying field
32087      */
32088     setValue : function(v){
32089         this.field.setValue(v);
32090     },
32091
32092     /**
32093      * Gets the data value of the editor
32094      * @return {Mixed} The data value
32095      */
32096     getValue : function(){
32097         return this.field.getValue();
32098     }
32099 });/*
32100  * Based on:
32101  * Ext JS Library 1.1.1
32102  * Copyright(c) 2006-2007, Ext JS, LLC.
32103  *
32104  * Originally Released Under LGPL - original licence link has changed is not relivant.
32105  *
32106  * Fork - LGPL
32107  * <script type="text/javascript">
32108  */
32109  
32110 /**
32111  * @class Roo.BasicDialog
32112  * @extends Roo.util.Observable
32113  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32114  * <pre><code>
32115 var dlg = new Roo.BasicDialog("my-dlg", {
32116     height: 200,
32117     width: 300,
32118     minHeight: 100,
32119     minWidth: 150,
32120     modal: true,
32121     proxyDrag: true,
32122     shadow: true
32123 });
32124 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32125 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32126 dlg.addButton('Cancel', dlg.hide, dlg);
32127 dlg.show();
32128 </code></pre>
32129   <b>A Dialog should always be a direct child of the body element.</b>
32130  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32131  * @cfg {String} title Default text to display in the title bar (defaults to null)
32132  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32133  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32134  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32135  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32136  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32137  * (defaults to null with no animation)
32138  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32139  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32140  * property for valid values (defaults to 'all')
32141  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32142  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32143  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32144  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32145  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32146  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32147  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32148  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32149  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32150  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32151  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32152  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32153  * draggable = true (defaults to false)
32154  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32155  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32156  * shadow (defaults to false)
32157  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32158  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32159  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32160  * @cfg {Array} buttons Array of buttons
32161  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32162  * @constructor
32163  * Create a new BasicDialog.
32164  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32165  * @param {Object} config Configuration options
32166  */
32167 Roo.BasicDialog = function(el, config){
32168     this.el = Roo.get(el);
32169     var dh = Roo.DomHelper;
32170     if(!this.el && config && config.autoCreate){
32171         if(typeof config.autoCreate == "object"){
32172             if(!config.autoCreate.id){
32173                 config.autoCreate.id = el;
32174             }
32175             this.el = dh.append(document.body,
32176                         config.autoCreate, true);
32177         }else{
32178             this.el = dh.append(document.body,
32179                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32180         }
32181     }
32182     el = this.el;
32183     el.setDisplayed(true);
32184     el.hide = this.hideAction;
32185     this.id = el.id;
32186     el.addClass("x-dlg");
32187
32188     Roo.apply(this, config);
32189
32190     this.proxy = el.createProxy("x-dlg-proxy");
32191     this.proxy.hide = this.hideAction;
32192     this.proxy.setOpacity(.5);
32193     this.proxy.hide();
32194
32195     if(config.width){
32196         el.setWidth(config.width);
32197     }
32198     if(config.height){
32199         el.setHeight(config.height);
32200     }
32201     this.size = el.getSize();
32202     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32203         this.xy = [config.x,config.y];
32204     }else{
32205         this.xy = el.getCenterXY(true);
32206     }
32207     /** The header element @type Roo.Element */
32208     this.header = el.child("> .x-dlg-hd");
32209     /** The body element @type Roo.Element */
32210     this.body = el.child("> .x-dlg-bd");
32211     /** The footer element @type Roo.Element */
32212     this.footer = el.child("> .x-dlg-ft");
32213
32214     if(!this.header){
32215         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32216     }
32217     if(!this.body){
32218         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32219     }
32220
32221     this.header.unselectable();
32222     if(this.title){
32223         this.header.update(this.title);
32224     }
32225     // this element allows the dialog to be focused for keyboard event
32226     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32227     this.focusEl.swallowEvent("click", true);
32228
32229     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32230
32231     // wrap the body and footer for special rendering
32232     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32233     if(this.footer){
32234         this.bwrap.dom.appendChild(this.footer.dom);
32235     }
32236
32237     this.bg = this.el.createChild({
32238         tag: "div", cls:"x-dlg-bg",
32239         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32240     });
32241     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32242
32243
32244     if(this.autoScroll !== false && !this.autoTabs){
32245         this.body.setStyle("overflow", "auto");
32246     }
32247
32248     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32249
32250     if(this.closable !== false){
32251         this.el.addClass("x-dlg-closable");
32252         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32253         this.close.on("click", this.closeClick, this);
32254         this.close.addClassOnOver("x-dlg-close-over");
32255     }
32256     if(this.collapsible !== false){
32257         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32258         this.collapseBtn.on("click", this.collapseClick, this);
32259         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32260         this.header.on("dblclick", this.collapseClick, this);
32261     }
32262     if(this.resizable !== false){
32263         this.el.addClass("x-dlg-resizable");
32264         this.resizer = new Roo.Resizable(el, {
32265             minWidth: this.minWidth || 80,
32266             minHeight:this.minHeight || 80,
32267             handles: this.resizeHandles || "all",
32268             pinned: true
32269         });
32270         this.resizer.on("beforeresize", this.beforeResize, this);
32271         this.resizer.on("resize", this.onResize, this);
32272     }
32273     if(this.draggable !== false){
32274         el.addClass("x-dlg-draggable");
32275         if (!this.proxyDrag) {
32276             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32277         }
32278         else {
32279             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32280         }
32281         dd.setHandleElId(this.header.id);
32282         dd.endDrag = this.endMove.createDelegate(this);
32283         dd.startDrag = this.startMove.createDelegate(this);
32284         dd.onDrag = this.onDrag.createDelegate(this);
32285         dd.scroll = false;
32286         this.dd = dd;
32287     }
32288     if(this.modal){
32289         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32290         this.mask.enableDisplayMode("block");
32291         this.mask.hide();
32292         this.el.addClass("x-dlg-modal");
32293     }
32294     if(this.shadow){
32295         this.shadow = new Roo.Shadow({
32296             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32297             offset : this.shadowOffset
32298         });
32299     }else{
32300         this.shadowOffset = 0;
32301     }
32302     if(Roo.useShims && this.shim !== false){
32303         this.shim = this.el.createShim();
32304         this.shim.hide = this.hideAction;
32305         this.shim.hide();
32306     }else{
32307         this.shim = false;
32308     }
32309     if(this.autoTabs){
32310         this.initTabs();
32311     }
32312     if (this.buttons) { 
32313         var bts= this.buttons;
32314         this.buttons = [];
32315         Roo.each(bts, function(b) {
32316             this.addButton(b);
32317         }, this);
32318     }
32319     
32320     
32321     this.addEvents({
32322         /**
32323          * @event keydown
32324          * Fires when a key is pressed
32325          * @param {Roo.BasicDialog} this
32326          * @param {Roo.EventObject} e
32327          */
32328         "keydown" : true,
32329         /**
32330          * @event move
32331          * Fires when this dialog is moved by the user.
32332          * @param {Roo.BasicDialog} this
32333          * @param {Number} x The new page X
32334          * @param {Number} y The new page Y
32335          */
32336         "move" : true,
32337         /**
32338          * @event resize
32339          * Fires when this dialog is resized by the user.
32340          * @param {Roo.BasicDialog} this
32341          * @param {Number} width The new width
32342          * @param {Number} height The new height
32343          */
32344         "resize" : true,
32345         /**
32346          * @event beforehide
32347          * Fires before this dialog is hidden.
32348          * @param {Roo.BasicDialog} this
32349          */
32350         "beforehide" : true,
32351         /**
32352          * @event hide
32353          * Fires when this dialog is hidden.
32354          * @param {Roo.BasicDialog} this
32355          */
32356         "hide" : true,
32357         /**
32358          * @event beforeshow
32359          * Fires before this dialog is shown.
32360          * @param {Roo.BasicDialog} this
32361          */
32362         "beforeshow" : true,
32363         /**
32364          * @event show
32365          * Fires when this dialog is shown.
32366          * @param {Roo.BasicDialog} this
32367          */
32368         "show" : true
32369     });
32370     el.on("keydown", this.onKeyDown, this);
32371     el.on("mousedown", this.toFront, this);
32372     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32373     this.el.hide();
32374     Roo.DialogManager.register(this);
32375     Roo.BasicDialog.superclass.constructor.call(this);
32376 };
32377
32378 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32379     shadowOffset: Roo.isIE ? 6 : 5,
32380     minHeight: 80,
32381     minWidth: 200,
32382     minButtonWidth: 75,
32383     defaultButton: null,
32384     buttonAlign: "right",
32385     tabTag: 'div',
32386     firstShow: true,
32387
32388     /**
32389      * Sets the dialog title text
32390      * @param {String} text The title text to display
32391      * @return {Roo.BasicDialog} this
32392      */
32393     setTitle : function(text){
32394         this.header.update(text);
32395         return this;
32396     },
32397
32398     // private
32399     closeClick : function(){
32400         this.hide();
32401     },
32402
32403     // private
32404     collapseClick : function(){
32405         this[this.collapsed ? "expand" : "collapse"]();
32406     },
32407
32408     /**
32409      * Collapses the dialog to its minimized state (only the title bar is visible).
32410      * Equivalent to the user clicking the collapse dialog button.
32411      */
32412     collapse : function(){
32413         if(!this.collapsed){
32414             this.collapsed = true;
32415             this.el.addClass("x-dlg-collapsed");
32416             this.restoreHeight = this.el.getHeight();
32417             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32418         }
32419     },
32420
32421     /**
32422      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32423      * clicking the expand dialog button.
32424      */
32425     expand : function(){
32426         if(this.collapsed){
32427             this.collapsed = false;
32428             this.el.removeClass("x-dlg-collapsed");
32429             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32430         }
32431     },
32432
32433     /**
32434      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32435      * @return {Roo.TabPanel} The tabs component
32436      */
32437     initTabs : function(){
32438         var tabs = this.getTabs();
32439         while(tabs.getTab(0)){
32440             tabs.removeTab(0);
32441         }
32442         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32443             var dom = el.dom;
32444             tabs.addTab(Roo.id(dom), dom.title);
32445             dom.title = "";
32446         });
32447         tabs.activate(0);
32448         return tabs;
32449     },
32450
32451     // private
32452     beforeResize : function(){
32453         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32454     },
32455
32456     // private
32457     onResize : function(){
32458         this.refreshSize();
32459         this.syncBodyHeight();
32460         this.adjustAssets();
32461         this.focus();
32462         this.fireEvent("resize", this, this.size.width, this.size.height);
32463     },
32464
32465     // private
32466     onKeyDown : function(e){
32467         if(this.isVisible()){
32468             this.fireEvent("keydown", this, e);
32469         }
32470     },
32471
32472     /**
32473      * Resizes the dialog.
32474      * @param {Number} width
32475      * @param {Number} height
32476      * @return {Roo.BasicDialog} this
32477      */
32478     resizeTo : function(width, height){
32479         this.el.setSize(width, height);
32480         this.size = {width: width, height: height};
32481         this.syncBodyHeight();
32482         if(this.fixedcenter){
32483             this.center();
32484         }
32485         if(this.isVisible()){
32486             this.constrainXY();
32487             this.adjustAssets();
32488         }
32489         this.fireEvent("resize", this, width, height);
32490         return this;
32491     },
32492
32493
32494     /**
32495      * Resizes the dialog to fit the specified content size.
32496      * @param {Number} width
32497      * @param {Number} height
32498      * @return {Roo.BasicDialog} this
32499      */
32500     setContentSize : function(w, h){
32501         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32502         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32503         //if(!this.el.isBorderBox()){
32504             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32505             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32506         //}
32507         if(this.tabs){
32508             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32509             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32510         }
32511         this.resizeTo(w, h);
32512         return this;
32513     },
32514
32515     /**
32516      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32517      * executed in response to a particular key being pressed while the dialog is active.
32518      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32519      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32520      * @param {Function} fn The function to call
32521      * @param {Object} scope (optional) The scope of the function
32522      * @return {Roo.BasicDialog} this
32523      */
32524     addKeyListener : function(key, fn, scope){
32525         var keyCode, shift, ctrl, alt;
32526         if(typeof key == "object" && !(key instanceof Array)){
32527             keyCode = key["key"];
32528             shift = key["shift"];
32529             ctrl = key["ctrl"];
32530             alt = key["alt"];
32531         }else{
32532             keyCode = key;
32533         }
32534         var handler = function(dlg, e){
32535             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32536                 var k = e.getKey();
32537                 if(keyCode instanceof Array){
32538                     for(var i = 0, len = keyCode.length; i < len; i++){
32539                         if(keyCode[i] == k){
32540                           fn.call(scope || window, dlg, k, e);
32541                           return;
32542                         }
32543                     }
32544                 }else{
32545                     if(k == keyCode){
32546                         fn.call(scope || window, dlg, k, e);
32547                     }
32548                 }
32549             }
32550         };
32551         this.on("keydown", handler);
32552         return this;
32553     },
32554
32555     /**
32556      * Returns the TabPanel component (creates it if it doesn't exist).
32557      * Note: If you wish to simply check for the existence of tabs without creating them,
32558      * check for a null 'tabs' property.
32559      * @return {Roo.TabPanel} The tabs component
32560      */
32561     getTabs : function(){
32562         if(!this.tabs){
32563             this.el.addClass("x-dlg-auto-tabs");
32564             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32565             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32566         }
32567         return this.tabs;
32568     },
32569
32570     /**
32571      * Adds a button to the footer section of the dialog.
32572      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32573      * object or a valid Roo.DomHelper element config
32574      * @param {Function} handler The function called when the button is clicked
32575      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32576      * @return {Roo.Button} The new button
32577      */
32578     addButton : function(config, handler, scope){
32579         var dh = Roo.DomHelper;
32580         if(!this.footer){
32581             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32582         }
32583         if(!this.btnContainer){
32584             var tb = this.footer.createChild({
32585
32586                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32587                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32588             }, null, true);
32589             this.btnContainer = tb.firstChild.firstChild.firstChild;
32590         }
32591         var bconfig = {
32592             handler: handler,
32593             scope: scope,
32594             minWidth: this.minButtonWidth,
32595             hideParent:true
32596         };
32597         if(typeof config == "string"){
32598             bconfig.text = config;
32599         }else{
32600             if(config.tag){
32601                 bconfig.dhconfig = config;
32602             }else{
32603                 Roo.apply(bconfig, config);
32604             }
32605         }
32606         var fc = false;
32607         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32608             bconfig.position = Math.max(0, bconfig.position);
32609             fc = this.btnContainer.childNodes[bconfig.position];
32610         }
32611          
32612         var btn = new Roo.Button(
32613             fc ? 
32614                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32615                 : this.btnContainer.appendChild(document.createElement("td")),
32616             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32617             bconfig
32618         );
32619         this.syncBodyHeight();
32620         if(!this.buttons){
32621             /**
32622              * Array of all the buttons that have been added to this dialog via addButton
32623              * @type Array
32624              */
32625             this.buttons = [];
32626         }
32627         this.buttons.push(btn);
32628         return btn;
32629     },
32630
32631     /**
32632      * Sets the default button to be focused when the dialog is displayed.
32633      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32634      * @return {Roo.BasicDialog} this
32635      */
32636     setDefaultButton : function(btn){
32637         this.defaultButton = btn;
32638         return this;
32639     },
32640
32641     // private
32642     getHeaderFooterHeight : function(safe){
32643         var height = 0;
32644         if(this.header){
32645            height += this.header.getHeight();
32646         }
32647         if(this.footer){
32648            var fm = this.footer.getMargins();
32649             height += (this.footer.getHeight()+fm.top+fm.bottom);
32650         }
32651         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32652         height += this.centerBg.getPadding("tb");
32653         return height;
32654     },
32655
32656     // private
32657     syncBodyHeight : function()
32658     {
32659         var bd = this.body, // the text
32660             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32661             bw = this.bwrap;
32662         var height = this.size.height - this.getHeaderFooterHeight(false);
32663         bd.setHeight(height-bd.getMargins("tb"));
32664         var hh = this.header.getHeight();
32665         var h = this.size.height-hh;
32666         cb.setHeight(h);
32667         
32668         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32669         bw.setHeight(h-cb.getPadding("tb"));
32670         
32671         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32672         bd.setWidth(bw.getWidth(true));
32673         if(this.tabs){
32674             this.tabs.syncHeight();
32675             if(Roo.isIE){
32676                 this.tabs.el.repaint();
32677             }
32678         }
32679     },
32680
32681     /**
32682      * Restores the previous state of the dialog if Roo.state is configured.
32683      * @return {Roo.BasicDialog} this
32684      */
32685     restoreState : function(){
32686         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32687         if(box && box.width){
32688             this.xy = [box.x, box.y];
32689             this.resizeTo(box.width, box.height);
32690         }
32691         return this;
32692     },
32693
32694     // private
32695     beforeShow : function(){
32696         this.expand();
32697         if(this.fixedcenter){
32698             this.xy = this.el.getCenterXY(true);
32699         }
32700         if(this.modal){
32701             Roo.get(document.body).addClass("x-body-masked");
32702             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32703             this.mask.show();
32704         }
32705         this.constrainXY();
32706     },
32707
32708     // private
32709     animShow : function(){
32710         var b = Roo.get(this.animateTarget).getBox();
32711         this.proxy.setSize(b.width, b.height);
32712         this.proxy.setLocation(b.x, b.y);
32713         this.proxy.show();
32714         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32715                     true, .35, this.showEl.createDelegate(this));
32716     },
32717
32718     /**
32719      * Shows the dialog.
32720      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32721      * @return {Roo.BasicDialog} this
32722      */
32723     show : function(animateTarget){
32724         if (this.fireEvent("beforeshow", this) === false){
32725             return;
32726         }
32727         if(this.syncHeightBeforeShow){
32728             this.syncBodyHeight();
32729         }else if(this.firstShow){
32730             this.firstShow = false;
32731             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32732         }
32733         this.animateTarget = animateTarget || this.animateTarget;
32734         if(!this.el.isVisible()){
32735             this.beforeShow();
32736             if(this.animateTarget && Roo.get(this.animateTarget)){
32737                 this.animShow();
32738             }else{
32739                 this.showEl();
32740             }
32741         }
32742         return this;
32743     },
32744
32745     // private
32746     showEl : function(){
32747         this.proxy.hide();
32748         this.el.setXY(this.xy);
32749         this.el.show();
32750         this.adjustAssets(true);
32751         this.toFront();
32752         this.focus();
32753         // IE peekaboo bug - fix found by Dave Fenwick
32754         if(Roo.isIE){
32755             this.el.repaint();
32756         }
32757         this.fireEvent("show", this);
32758     },
32759
32760     /**
32761      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32762      * dialog itself will receive focus.
32763      */
32764     focus : function(){
32765         if(this.defaultButton){
32766             this.defaultButton.focus();
32767         }else{
32768             this.focusEl.focus();
32769         }
32770     },
32771
32772     // private
32773     constrainXY : function(){
32774         if(this.constraintoviewport !== false){
32775             if(!this.viewSize){
32776                 if(this.container){
32777                     var s = this.container.getSize();
32778                     this.viewSize = [s.width, s.height];
32779                 }else{
32780                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32781                 }
32782             }
32783             var s = Roo.get(this.container||document).getScroll();
32784
32785             var x = this.xy[0], y = this.xy[1];
32786             var w = this.size.width, h = this.size.height;
32787             var vw = this.viewSize[0], vh = this.viewSize[1];
32788             // only move it if it needs it
32789             var moved = false;
32790             // first validate right/bottom
32791             if(x + w > vw+s.left){
32792                 x = vw - w;
32793                 moved = true;
32794             }
32795             if(y + h > vh+s.top){
32796                 y = vh - h;
32797                 moved = true;
32798             }
32799             // then make sure top/left isn't negative
32800             if(x < s.left){
32801                 x = s.left;
32802                 moved = true;
32803             }
32804             if(y < s.top){
32805                 y = s.top;
32806                 moved = true;
32807             }
32808             if(moved){
32809                 // cache xy
32810                 this.xy = [x, y];
32811                 if(this.isVisible()){
32812                     this.el.setLocation(x, y);
32813                     this.adjustAssets();
32814                 }
32815             }
32816         }
32817     },
32818
32819     // private
32820     onDrag : function(){
32821         if(!this.proxyDrag){
32822             this.xy = this.el.getXY();
32823             this.adjustAssets();
32824         }
32825     },
32826
32827     // private
32828     adjustAssets : function(doShow){
32829         var x = this.xy[0], y = this.xy[1];
32830         var w = this.size.width, h = this.size.height;
32831         if(doShow === true){
32832             if(this.shadow){
32833                 this.shadow.show(this.el);
32834             }
32835             if(this.shim){
32836                 this.shim.show();
32837             }
32838         }
32839         if(this.shadow && this.shadow.isVisible()){
32840             this.shadow.show(this.el);
32841         }
32842         if(this.shim && this.shim.isVisible()){
32843             this.shim.setBounds(x, y, w, h);
32844         }
32845     },
32846
32847     // private
32848     adjustViewport : function(w, h){
32849         if(!w || !h){
32850             w = Roo.lib.Dom.getViewWidth();
32851             h = Roo.lib.Dom.getViewHeight();
32852         }
32853         // cache the size
32854         this.viewSize = [w, h];
32855         if(this.modal && this.mask.isVisible()){
32856             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32857             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32858         }
32859         if(this.isVisible()){
32860             this.constrainXY();
32861         }
32862     },
32863
32864     /**
32865      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32866      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32867      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32868      */
32869     destroy : function(removeEl){
32870         if(this.isVisible()){
32871             this.animateTarget = null;
32872             this.hide();
32873         }
32874         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32875         if(this.tabs){
32876             this.tabs.destroy(removeEl);
32877         }
32878         Roo.destroy(
32879              this.shim,
32880              this.proxy,
32881              this.resizer,
32882              this.close,
32883              this.mask
32884         );
32885         if(this.dd){
32886             this.dd.unreg();
32887         }
32888         if(this.buttons){
32889            for(var i = 0, len = this.buttons.length; i < len; i++){
32890                this.buttons[i].destroy();
32891            }
32892         }
32893         this.el.removeAllListeners();
32894         if(removeEl === true){
32895             this.el.update("");
32896             this.el.remove();
32897         }
32898         Roo.DialogManager.unregister(this);
32899     },
32900
32901     // private
32902     startMove : function(){
32903         if(this.proxyDrag){
32904             this.proxy.show();
32905         }
32906         if(this.constraintoviewport !== false){
32907             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32908         }
32909     },
32910
32911     // private
32912     endMove : function(){
32913         if(!this.proxyDrag){
32914             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32915         }else{
32916             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32917             this.proxy.hide();
32918         }
32919         this.refreshSize();
32920         this.adjustAssets();
32921         this.focus();
32922         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32923     },
32924
32925     /**
32926      * Brings this dialog to the front of any other visible dialogs
32927      * @return {Roo.BasicDialog} this
32928      */
32929     toFront : function(){
32930         Roo.DialogManager.bringToFront(this);
32931         return this;
32932     },
32933
32934     /**
32935      * Sends this dialog to the back (under) of any other visible dialogs
32936      * @return {Roo.BasicDialog} this
32937      */
32938     toBack : function(){
32939         Roo.DialogManager.sendToBack(this);
32940         return this;
32941     },
32942
32943     /**
32944      * Centers this dialog in the viewport
32945      * @return {Roo.BasicDialog} this
32946      */
32947     center : function(){
32948         var xy = this.el.getCenterXY(true);
32949         this.moveTo(xy[0], xy[1]);
32950         return this;
32951     },
32952
32953     /**
32954      * Moves the dialog's top-left corner to the specified point
32955      * @param {Number} x
32956      * @param {Number} y
32957      * @return {Roo.BasicDialog} this
32958      */
32959     moveTo : function(x, y){
32960         this.xy = [x,y];
32961         if(this.isVisible()){
32962             this.el.setXY(this.xy);
32963             this.adjustAssets();
32964         }
32965         return this;
32966     },
32967
32968     /**
32969      * Aligns the dialog to the specified element
32970      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32971      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32972      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32973      * @return {Roo.BasicDialog} this
32974      */
32975     alignTo : function(element, position, offsets){
32976         this.xy = this.el.getAlignToXY(element, position, offsets);
32977         if(this.isVisible()){
32978             this.el.setXY(this.xy);
32979             this.adjustAssets();
32980         }
32981         return this;
32982     },
32983
32984     /**
32985      * Anchors an element to another element and realigns it when the window is resized.
32986      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32987      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32988      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32989      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32990      * is a number, it is used as the buffer delay (defaults to 50ms).
32991      * @return {Roo.BasicDialog} this
32992      */
32993     anchorTo : function(el, alignment, offsets, monitorScroll){
32994         var action = function(){
32995             this.alignTo(el, alignment, offsets);
32996         };
32997         Roo.EventManager.onWindowResize(action, this);
32998         var tm = typeof monitorScroll;
32999         if(tm != 'undefined'){
33000             Roo.EventManager.on(window, 'scroll', action, this,
33001                 {buffer: tm == 'number' ? monitorScroll : 50});
33002         }
33003         action.call(this);
33004         return this;
33005     },
33006
33007     /**
33008      * Returns true if the dialog is visible
33009      * @return {Boolean}
33010      */
33011     isVisible : function(){
33012         return this.el.isVisible();
33013     },
33014
33015     // private
33016     animHide : function(callback){
33017         var b = Roo.get(this.animateTarget).getBox();
33018         this.proxy.show();
33019         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33020         this.el.hide();
33021         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33022                     this.hideEl.createDelegate(this, [callback]));
33023     },
33024
33025     /**
33026      * Hides the dialog.
33027      * @param {Function} callback (optional) Function to call when the dialog is hidden
33028      * @return {Roo.BasicDialog} this
33029      */
33030     hide : function(callback){
33031         if (this.fireEvent("beforehide", this) === false){
33032             return;
33033         }
33034         if(this.shadow){
33035             this.shadow.hide();
33036         }
33037         if(this.shim) {
33038           this.shim.hide();
33039         }
33040         // sometimes animateTarget seems to get set.. causing problems...
33041         // this just double checks..
33042         if(this.animateTarget && Roo.get(this.animateTarget)) {
33043            this.animHide(callback);
33044         }else{
33045             this.el.hide();
33046             this.hideEl(callback);
33047         }
33048         return this;
33049     },
33050
33051     // private
33052     hideEl : function(callback){
33053         this.proxy.hide();
33054         if(this.modal){
33055             this.mask.hide();
33056             Roo.get(document.body).removeClass("x-body-masked");
33057         }
33058         this.fireEvent("hide", this);
33059         if(typeof callback == "function"){
33060             callback();
33061         }
33062     },
33063
33064     // private
33065     hideAction : function(){
33066         this.setLeft("-10000px");
33067         this.setTop("-10000px");
33068         this.setStyle("visibility", "hidden");
33069     },
33070
33071     // private
33072     refreshSize : function(){
33073         this.size = this.el.getSize();
33074         this.xy = this.el.getXY();
33075         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33076     },
33077
33078     // private
33079     // z-index is managed by the DialogManager and may be overwritten at any time
33080     setZIndex : function(index){
33081         if(this.modal){
33082             this.mask.setStyle("z-index", index);
33083         }
33084         if(this.shim){
33085             this.shim.setStyle("z-index", ++index);
33086         }
33087         if(this.shadow){
33088             this.shadow.setZIndex(++index);
33089         }
33090         this.el.setStyle("z-index", ++index);
33091         if(this.proxy){
33092             this.proxy.setStyle("z-index", ++index);
33093         }
33094         if(this.resizer){
33095             this.resizer.proxy.setStyle("z-index", ++index);
33096         }
33097
33098         this.lastZIndex = index;
33099     },
33100
33101     /**
33102      * Returns the element for this dialog
33103      * @return {Roo.Element} The underlying dialog Element
33104      */
33105     getEl : function(){
33106         return this.el;
33107     }
33108 });
33109
33110 /**
33111  * @class Roo.DialogManager
33112  * Provides global access to BasicDialogs that have been created and
33113  * support for z-indexing (layering) multiple open dialogs.
33114  */
33115 Roo.DialogManager = function(){
33116     var list = {};
33117     var accessList = [];
33118     var front = null;
33119
33120     // private
33121     var sortDialogs = function(d1, d2){
33122         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33123     };
33124
33125     // private
33126     var orderDialogs = function(){
33127         accessList.sort(sortDialogs);
33128         var seed = Roo.DialogManager.zseed;
33129         for(var i = 0, len = accessList.length; i < len; i++){
33130             var dlg = accessList[i];
33131             if(dlg){
33132                 dlg.setZIndex(seed + (i*10));
33133             }
33134         }
33135     };
33136
33137     return {
33138         /**
33139          * The starting z-index for BasicDialogs (defaults to 9000)
33140          * @type Number The z-index value
33141          */
33142         zseed : 9000,
33143
33144         // private
33145         register : function(dlg){
33146             list[dlg.id] = dlg;
33147             accessList.push(dlg);
33148         },
33149
33150         // private
33151         unregister : function(dlg){
33152             delete list[dlg.id];
33153             var i=0;
33154             var len=0;
33155             if(!accessList.indexOf){
33156                 for(  i = 0, len = accessList.length; i < len; i++){
33157                     if(accessList[i] == dlg){
33158                         accessList.splice(i, 1);
33159                         return;
33160                     }
33161                 }
33162             }else{
33163                  i = accessList.indexOf(dlg);
33164                 if(i != -1){
33165                     accessList.splice(i, 1);
33166                 }
33167             }
33168         },
33169
33170         /**
33171          * Gets a registered dialog by id
33172          * @param {String/Object} id The id of the dialog or a dialog
33173          * @return {Roo.BasicDialog} this
33174          */
33175         get : function(id){
33176             return typeof id == "object" ? id : list[id];
33177         },
33178
33179         /**
33180          * Brings the specified dialog to the front
33181          * @param {String/Object} dlg The id of the dialog or a dialog
33182          * @return {Roo.BasicDialog} this
33183          */
33184         bringToFront : function(dlg){
33185             dlg = this.get(dlg);
33186             if(dlg != front){
33187                 front = dlg;
33188                 dlg._lastAccess = new Date().getTime();
33189                 orderDialogs();
33190             }
33191             return dlg;
33192         },
33193
33194         /**
33195          * Sends the specified dialog to the back
33196          * @param {String/Object} dlg The id of the dialog or a dialog
33197          * @return {Roo.BasicDialog} this
33198          */
33199         sendToBack : function(dlg){
33200             dlg = this.get(dlg);
33201             dlg._lastAccess = -(new Date().getTime());
33202             orderDialogs();
33203             return dlg;
33204         },
33205
33206         /**
33207          * Hides all dialogs
33208          */
33209         hideAll : function(){
33210             for(var id in list){
33211                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33212                     list[id].hide();
33213                 }
33214             }
33215         }
33216     };
33217 }();
33218
33219 /**
33220  * @class Roo.LayoutDialog
33221  * @extends Roo.BasicDialog
33222  * Dialog which provides adjustments for working with a layout in a Dialog.
33223  * Add your necessary layout config options to the dialog's config.<br>
33224  * Example usage (including a nested layout):
33225  * <pre><code>
33226 if(!dialog){
33227     dialog = new Roo.LayoutDialog("download-dlg", {
33228         modal: true,
33229         width:600,
33230         height:450,
33231         shadow:true,
33232         minWidth:500,
33233         minHeight:350,
33234         autoTabs:true,
33235         proxyDrag:true,
33236         // layout config merges with the dialog config
33237         center:{
33238             tabPosition: "top",
33239             alwaysShowTabs: true
33240         }
33241     });
33242     dialog.addKeyListener(27, dialog.hide, dialog);
33243     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33244     dialog.addButton("Build It!", this.getDownload, this);
33245
33246     // we can even add nested layouts
33247     var innerLayout = new Roo.BorderLayout("dl-inner", {
33248         east: {
33249             initialSize: 200,
33250             autoScroll:true,
33251             split:true
33252         },
33253         center: {
33254             autoScroll:true
33255         }
33256     });
33257     innerLayout.beginUpdate();
33258     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33259     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33260     innerLayout.endUpdate(true);
33261
33262     var layout = dialog.getLayout();
33263     layout.beginUpdate();
33264     layout.add("center", new Roo.ContentPanel("standard-panel",
33265                         {title: "Download the Source", fitToFrame:true}));
33266     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33267                {title: "Build your own roo.js"}));
33268     layout.getRegion("center").showPanel(sp);
33269     layout.endUpdate();
33270 }
33271 </code></pre>
33272     * @constructor
33273     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33274     * @param {Object} config configuration options
33275   */
33276 Roo.LayoutDialog = function(el, cfg){
33277     
33278     var config=  cfg;
33279     if (typeof(cfg) == 'undefined') {
33280         config = Roo.apply({}, el);
33281         // not sure why we use documentElement here.. - it should always be body.
33282         // IE7 borks horribly if we use documentElement.
33283         // webkit also does not like documentElement - it creates a body element...
33284         el = Roo.get( document.body || document.documentElement ).createChild();
33285         //config.autoCreate = true;
33286     }
33287     
33288     
33289     config.autoTabs = false;
33290     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33291     this.body.setStyle({overflow:"hidden", position:"relative"});
33292     this.layout = new Roo.BorderLayout(this.body.dom, config);
33293     this.layout.monitorWindowResize = false;
33294     this.el.addClass("x-dlg-auto-layout");
33295     // fix case when center region overwrites center function
33296     this.center = Roo.BasicDialog.prototype.center;
33297     this.on("show", this.layout.layout, this.layout, true);
33298     if (config.items) {
33299         var xitems = config.items;
33300         delete config.items;
33301         Roo.each(xitems, this.addxtype, this);
33302     }
33303     
33304     
33305 };
33306 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33307     /**
33308      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33309      * @deprecated
33310      */
33311     endUpdate : function(){
33312         this.layout.endUpdate();
33313     },
33314
33315     /**
33316      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33317      *  @deprecated
33318      */
33319     beginUpdate : function(){
33320         this.layout.beginUpdate();
33321     },
33322
33323     /**
33324      * Get the BorderLayout for this dialog
33325      * @return {Roo.BorderLayout}
33326      */
33327     getLayout : function(){
33328         return this.layout;
33329     },
33330
33331     showEl : function(){
33332         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33333         if(Roo.isIE7){
33334             this.layout.layout();
33335         }
33336     },
33337
33338     // private
33339     // Use the syncHeightBeforeShow config option to control this automatically
33340     syncBodyHeight : function(){
33341         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33342         if(this.layout){this.layout.layout();}
33343     },
33344     
33345       /**
33346      * Add an xtype element (actually adds to the layout.)
33347      * @return {Object} xdata xtype object data.
33348      */
33349     
33350     addxtype : function(c) {
33351         return this.layout.addxtype(c);
33352     }
33353 });/*
33354  * Based on:
33355  * Ext JS Library 1.1.1
33356  * Copyright(c) 2006-2007, Ext JS, LLC.
33357  *
33358  * Originally Released Under LGPL - original licence link has changed is not relivant.
33359  *
33360  * Fork - LGPL
33361  * <script type="text/javascript">
33362  */
33363  
33364 /**
33365  * @class Roo.MessageBox
33366  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33367  * Example usage:
33368  *<pre><code>
33369 // Basic alert:
33370 Roo.Msg.alert('Status', 'Changes saved successfully.');
33371
33372 // Prompt for user data:
33373 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33374     if (btn == 'ok'){
33375         // process text value...
33376     }
33377 });
33378
33379 // Show a dialog using config options:
33380 Roo.Msg.show({
33381    title:'Save Changes?',
33382    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33383    buttons: Roo.Msg.YESNOCANCEL,
33384    fn: processResult,
33385    animEl: 'elId'
33386 });
33387 </code></pre>
33388  * @singleton
33389  */
33390 Roo.MessageBox = function(){
33391     var dlg, opt, mask, waitTimer;
33392     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33393     var buttons, activeTextEl, bwidth;
33394
33395     // private
33396     var handleButton = function(button){
33397         dlg.hide();
33398         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33399     };
33400
33401     // private
33402     var handleHide = function(){
33403         if(opt && opt.cls){
33404             dlg.el.removeClass(opt.cls);
33405         }
33406         if(waitTimer){
33407             Roo.TaskMgr.stop(waitTimer);
33408             waitTimer = null;
33409         }
33410     };
33411
33412     // private
33413     var updateButtons = function(b){
33414         var width = 0;
33415         if(!b){
33416             buttons["ok"].hide();
33417             buttons["cancel"].hide();
33418             buttons["yes"].hide();
33419             buttons["no"].hide();
33420             dlg.footer.dom.style.display = 'none';
33421             return width;
33422         }
33423         dlg.footer.dom.style.display = '';
33424         for(var k in buttons){
33425             if(typeof buttons[k] != "function"){
33426                 if(b[k]){
33427                     buttons[k].show();
33428                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33429                     width += buttons[k].el.getWidth()+15;
33430                 }else{
33431                     buttons[k].hide();
33432                 }
33433             }
33434         }
33435         return width;
33436     };
33437
33438     // private
33439     var handleEsc = function(d, k, e){
33440         if(opt && opt.closable !== false){
33441             dlg.hide();
33442         }
33443         if(e){
33444             e.stopEvent();
33445         }
33446     };
33447
33448     return {
33449         /**
33450          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33451          * @return {Roo.BasicDialog} The BasicDialog element
33452          */
33453         getDialog : function(){
33454            if(!dlg){
33455                 dlg = new Roo.BasicDialog("x-msg-box", {
33456                     autoCreate : true,
33457                     shadow: true,
33458                     draggable: true,
33459                     resizable:false,
33460                     constraintoviewport:false,
33461                     fixedcenter:true,
33462                     collapsible : false,
33463                     shim:true,
33464                     modal: true,
33465                     width:400, height:100,
33466                     buttonAlign:"center",
33467                     closeClick : function(){
33468                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33469                             handleButton("no");
33470                         }else{
33471                             handleButton("cancel");
33472                         }
33473                     }
33474                 });
33475                 dlg.on("hide", handleHide);
33476                 mask = dlg.mask;
33477                 dlg.addKeyListener(27, handleEsc);
33478                 buttons = {};
33479                 var bt = this.buttonText;
33480                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33481                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33482                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33483                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33484                 bodyEl = dlg.body.createChild({
33485
33486                     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>'
33487                 });
33488                 msgEl = bodyEl.dom.firstChild;
33489                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33490                 textboxEl.enableDisplayMode();
33491                 textboxEl.addKeyListener([10,13], function(){
33492                     if(dlg.isVisible() && opt && opt.buttons){
33493                         if(opt.buttons.ok){
33494                             handleButton("ok");
33495                         }else if(opt.buttons.yes){
33496                             handleButton("yes");
33497                         }
33498                     }
33499                 });
33500                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33501                 textareaEl.enableDisplayMode();
33502                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33503                 progressEl.enableDisplayMode();
33504                 var pf = progressEl.dom.firstChild;
33505                 if (pf) {
33506                     pp = Roo.get(pf.firstChild);
33507                     pp.setHeight(pf.offsetHeight);
33508                 }
33509                 
33510             }
33511             return dlg;
33512         },
33513
33514         /**
33515          * Updates the message box body text
33516          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33517          * the XHTML-compliant non-breaking space character '&amp;#160;')
33518          * @return {Roo.MessageBox} This message box
33519          */
33520         updateText : function(text){
33521             if(!dlg.isVisible() && !opt.width){
33522                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33523             }
33524             msgEl.innerHTML = text || '&#160;';
33525       
33526             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33527             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33528             var w = Math.max(
33529                     Math.min(opt.width || cw , this.maxWidth), 
33530                     Math.max(opt.minWidth || this.minWidth, bwidth)
33531             );
33532             if(opt.prompt){
33533                 activeTextEl.setWidth(w);
33534             }
33535             if(dlg.isVisible()){
33536                 dlg.fixedcenter = false;
33537             }
33538             // to big, make it scroll. = But as usual stupid IE does not support
33539             // !important..
33540             
33541             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33542                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33543                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33544             } else {
33545                 bodyEl.dom.style.height = '';
33546                 bodyEl.dom.style.overflowY = '';
33547             }
33548             if (cw > w) {
33549                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33550             } else {
33551                 bodyEl.dom.style.overflowX = '';
33552             }
33553             
33554             dlg.setContentSize(w, bodyEl.getHeight());
33555             if(dlg.isVisible()){
33556                 dlg.fixedcenter = true;
33557             }
33558             return this;
33559         },
33560
33561         /**
33562          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33563          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33564          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33565          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33566          * @return {Roo.MessageBox} This message box
33567          */
33568         updateProgress : function(value, text){
33569             if(text){
33570                 this.updateText(text);
33571             }
33572             if (pp) { // weird bug on my firefox - for some reason this is not defined
33573                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33574             }
33575             return this;
33576         },        
33577
33578         /**
33579          * Returns true if the message box is currently displayed
33580          * @return {Boolean} True if the message box is visible, else false
33581          */
33582         isVisible : function(){
33583             return dlg && dlg.isVisible();  
33584         },
33585
33586         /**
33587          * Hides the message box if it is displayed
33588          */
33589         hide : function(){
33590             if(this.isVisible()){
33591                 dlg.hide();
33592             }  
33593         },
33594
33595         /**
33596          * Displays a new message box, or reinitializes an existing message box, based on the config options
33597          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33598          * The following config object properties are supported:
33599          * <pre>
33600 Property    Type             Description
33601 ----------  ---------------  ------------------------------------------------------------------------------------
33602 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33603                                    closes (defaults to undefined)
33604 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33605                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33606 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33607                                    progress and wait dialogs will ignore this property and always hide the
33608                                    close button as they can only be closed programmatically.
33609 cls               String           A custom CSS class to apply to the message box element
33610 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33611                                    displayed (defaults to 75)
33612 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33613                                    function will be btn (the name of the button that was clicked, if applicable,
33614                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33615                                    Progress and wait dialogs will ignore this option since they do not respond to
33616                                    user actions and can only be closed programmatically, so any required function
33617                                    should be called by the same code after it closes the dialog.
33618 icon              String           A CSS class that provides a background image to be used as an icon for
33619                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33620 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33621 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33622 modal             Boolean          False to allow user interaction with the page while the message box is
33623                                    displayed (defaults to true)
33624 msg               String           A string that will replace the existing message box body text (defaults
33625                                    to the XHTML-compliant non-breaking space character '&#160;')
33626 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33627 progress          Boolean          True to display a progress bar (defaults to false)
33628 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33629 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33630 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33631 title             String           The title text
33632 value             String           The string value to set into the active textbox element if displayed
33633 wait              Boolean          True to display a progress bar (defaults to false)
33634 width             Number           The width of the dialog in pixels
33635 </pre>
33636          *
33637          * Example usage:
33638          * <pre><code>
33639 Roo.Msg.show({
33640    title: 'Address',
33641    msg: 'Please enter your address:',
33642    width: 300,
33643    buttons: Roo.MessageBox.OKCANCEL,
33644    multiline: true,
33645    fn: saveAddress,
33646    animEl: 'addAddressBtn'
33647 });
33648 </code></pre>
33649          * @param {Object} config Configuration options
33650          * @return {Roo.MessageBox} This message box
33651          */
33652         show : function(options)
33653         {
33654             
33655             // this causes nightmares if you show one dialog after another
33656             // especially on callbacks..
33657              
33658             if(this.isVisible()){
33659                 
33660                 this.hide();
33661                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33662                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33663                 Roo.log("New Dialog Message:" +  options.msg )
33664                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33665                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33666                 
33667             }
33668             var d = this.getDialog();
33669             opt = options;
33670             d.setTitle(opt.title || "&#160;");
33671             d.close.setDisplayed(opt.closable !== false);
33672             activeTextEl = textboxEl;
33673             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33674             if(opt.prompt){
33675                 if(opt.multiline){
33676                     textboxEl.hide();
33677                     textareaEl.show();
33678                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33679                         opt.multiline : this.defaultTextHeight);
33680                     activeTextEl = textareaEl;
33681                 }else{
33682                     textboxEl.show();
33683                     textareaEl.hide();
33684                 }
33685             }else{
33686                 textboxEl.hide();
33687                 textareaEl.hide();
33688             }
33689             progressEl.setDisplayed(opt.progress === true);
33690             this.updateProgress(0);
33691             activeTextEl.dom.value = opt.value || "";
33692             if(opt.prompt){
33693                 dlg.setDefaultButton(activeTextEl);
33694             }else{
33695                 var bs = opt.buttons;
33696                 var db = null;
33697                 if(bs && bs.ok){
33698                     db = buttons["ok"];
33699                 }else if(bs && bs.yes){
33700                     db = buttons["yes"];
33701                 }
33702                 dlg.setDefaultButton(db);
33703             }
33704             bwidth = updateButtons(opt.buttons);
33705             this.updateText(opt.msg);
33706             if(opt.cls){
33707                 d.el.addClass(opt.cls);
33708             }
33709             d.proxyDrag = opt.proxyDrag === true;
33710             d.modal = opt.modal !== false;
33711             d.mask = opt.modal !== false ? mask : false;
33712             if(!d.isVisible()){
33713                 // force it to the end of the z-index stack so it gets a cursor in FF
33714                 document.body.appendChild(dlg.el.dom);
33715                 d.animateTarget = null;
33716                 d.show(options.animEl);
33717             }
33718             return this;
33719         },
33720
33721         /**
33722          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33723          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33724          * and closing the message box when the process is complete.
33725          * @param {String} title The title bar text
33726          * @param {String} msg The message box body text
33727          * @return {Roo.MessageBox} This message box
33728          */
33729         progress : function(title, msg){
33730             this.show({
33731                 title : title,
33732                 msg : msg,
33733                 buttons: false,
33734                 progress:true,
33735                 closable:false,
33736                 minWidth: this.minProgressWidth,
33737                 modal : true
33738             });
33739             return this;
33740         },
33741
33742         /**
33743          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33744          * If a callback function is passed it will be called after the user clicks the button, and the
33745          * id of the button that was clicked will be passed as the only parameter to the callback
33746          * (could also be the top-right close button).
33747          * @param {String} title The title bar text
33748          * @param {String} msg The message box body text
33749          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33750          * @param {Object} scope (optional) The scope of the callback function
33751          * @return {Roo.MessageBox} This message box
33752          */
33753         alert : function(title, msg, fn, scope){
33754             this.show({
33755                 title : title,
33756                 msg : msg,
33757                 buttons: this.OK,
33758                 fn: fn,
33759                 scope : scope,
33760                 modal : true
33761             });
33762             return this;
33763         },
33764
33765         /**
33766          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33767          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33768          * You are responsible for closing the message box when the process is complete.
33769          * @param {String} msg The message box body text
33770          * @param {String} title (optional) The title bar text
33771          * @return {Roo.MessageBox} This message box
33772          */
33773         wait : function(msg, title){
33774             this.show({
33775                 title : title,
33776                 msg : msg,
33777                 buttons: false,
33778                 closable:false,
33779                 progress:true,
33780                 modal:true,
33781                 width:300,
33782                 wait:true
33783             });
33784             waitTimer = Roo.TaskMgr.start({
33785                 run: function(i){
33786                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33787                 },
33788                 interval: 1000
33789             });
33790             return this;
33791         },
33792
33793         /**
33794          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33795          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33796          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33797          * @param {String} title The title bar text
33798          * @param {String} msg The message box body text
33799          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33800          * @param {Object} scope (optional) The scope of the callback function
33801          * @return {Roo.MessageBox} This message box
33802          */
33803         confirm : function(title, msg, fn, scope){
33804             this.show({
33805                 title : title,
33806                 msg : msg,
33807                 buttons: this.YESNO,
33808                 fn: fn,
33809                 scope : scope,
33810                 modal : true
33811             });
33812             return this;
33813         },
33814
33815         /**
33816          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33817          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33818          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33819          * (could also be the top-right close button) and the text that was entered will be passed as the two
33820          * parameters to the callback.
33821          * @param {String} title The title bar text
33822          * @param {String} msg The message box body text
33823          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33824          * @param {Object} scope (optional) The scope of the callback function
33825          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33826          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33827          * @return {Roo.MessageBox} This message box
33828          */
33829         prompt : function(title, msg, fn, scope, multiline){
33830             this.show({
33831                 title : title,
33832                 msg : msg,
33833                 buttons: this.OKCANCEL,
33834                 fn: fn,
33835                 minWidth:250,
33836                 scope : scope,
33837                 prompt:true,
33838                 multiline: multiline,
33839                 modal : true
33840             });
33841             return this;
33842         },
33843
33844         /**
33845          * Button config that displays a single OK button
33846          * @type Object
33847          */
33848         OK : {ok:true},
33849         /**
33850          * Button config that displays Yes and No buttons
33851          * @type Object
33852          */
33853         YESNO : {yes:true, no:true},
33854         /**
33855          * Button config that displays OK and Cancel buttons
33856          * @type Object
33857          */
33858         OKCANCEL : {ok:true, cancel:true},
33859         /**
33860          * Button config that displays Yes, No and Cancel buttons
33861          * @type Object
33862          */
33863         YESNOCANCEL : {yes:true, no:true, cancel:true},
33864
33865         /**
33866          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33867          * @type Number
33868          */
33869         defaultTextHeight : 75,
33870         /**
33871          * The maximum width in pixels of the message box (defaults to 600)
33872          * @type Number
33873          */
33874         maxWidth : 600,
33875         /**
33876          * The minimum width in pixels of the message box (defaults to 100)
33877          * @type Number
33878          */
33879         minWidth : 100,
33880         /**
33881          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33882          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33883          * @type Number
33884          */
33885         minProgressWidth : 250,
33886         /**
33887          * An object containing the default button text strings that can be overriden for localized language support.
33888          * Supported properties are: ok, cancel, yes and no.
33889          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33890          * @type Object
33891          */
33892         buttonText : {
33893             ok : "OK",
33894             cancel : "Cancel",
33895             yes : "Yes",
33896             no : "No"
33897         }
33898     };
33899 }();
33900
33901 /**
33902  * Shorthand for {@link Roo.MessageBox}
33903  */
33904 Roo.Msg = Roo.MessageBox;/*
33905  * Based on:
33906  * Ext JS Library 1.1.1
33907  * Copyright(c) 2006-2007, Ext JS, LLC.
33908  *
33909  * Originally Released Under LGPL - original licence link has changed is not relivant.
33910  *
33911  * Fork - LGPL
33912  * <script type="text/javascript">
33913  */
33914 /**
33915  * @class Roo.QuickTips
33916  * Provides attractive and customizable tooltips for any element.
33917  * @singleton
33918  */
33919 Roo.QuickTips = function(){
33920     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33921     var ce, bd, xy, dd;
33922     var visible = false, disabled = true, inited = false;
33923     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33924     
33925     var onOver = function(e){
33926         if(disabled){
33927             return;
33928         }
33929         var t = e.getTarget();
33930         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33931             return;
33932         }
33933         if(ce && t == ce.el){
33934             clearTimeout(hideProc);
33935             return;
33936         }
33937         if(t && tagEls[t.id]){
33938             tagEls[t.id].el = t;
33939             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33940             return;
33941         }
33942         var ttp, et = Roo.fly(t);
33943         var ns = cfg.namespace;
33944         if(tm.interceptTitles && t.title){
33945             ttp = t.title;
33946             t.qtip = ttp;
33947             t.removeAttribute("title");
33948             e.preventDefault();
33949         }else{
33950             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33951         }
33952         if(ttp){
33953             showProc = show.defer(tm.showDelay, tm, [{
33954                 el: t, 
33955                 text: ttp.replace(/\\n/g,'<br/>'),
33956                 width: et.getAttributeNS(ns, cfg.width),
33957                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33958                 title: et.getAttributeNS(ns, cfg.title),
33959                     cls: et.getAttributeNS(ns, cfg.cls)
33960             }]);
33961         }
33962     };
33963     
33964     var onOut = function(e){
33965         clearTimeout(showProc);
33966         var t = e.getTarget();
33967         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33968             hideProc = setTimeout(hide, tm.hideDelay);
33969         }
33970     };
33971     
33972     var onMove = function(e){
33973         if(disabled){
33974             return;
33975         }
33976         xy = e.getXY();
33977         xy[1] += 18;
33978         if(tm.trackMouse && ce){
33979             el.setXY(xy);
33980         }
33981     };
33982     
33983     var onDown = function(e){
33984         clearTimeout(showProc);
33985         clearTimeout(hideProc);
33986         if(!e.within(el)){
33987             if(tm.hideOnClick){
33988                 hide();
33989                 tm.disable();
33990                 tm.enable.defer(100, tm);
33991             }
33992         }
33993     };
33994     
33995     var getPad = function(){
33996         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33997     };
33998
33999     var show = function(o){
34000         if(disabled){
34001             return;
34002         }
34003         clearTimeout(dismissProc);
34004         ce = o;
34005         if(removeCls){ // in case manually hidden
34006             el.removeClass(removeCls);
34007             removeCls = null;
34008         }
34009         if(ce.cls){
34010             el.addClass(ce.cls);
34011             removeCls = ce.cls;
34012         }
34013         if(ce.title){
34014             tipTitle.update(ce.title);
34015             tipTitle.show();
34016         }else{
34017             tipTitle.update('');
34018             tipTitle.hide();
34019         }
34020         el.dom.style.width  = tm.maxWidth+'px';
34021         //tipBody.dom.style.width = '';
34022         tipBodyText.update(o.text);
34023         var p = getPad(), w = ce.width;
34024         if(!w){
34025             var td = tipBodyText.dom;
34026             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34027             if(aw > tm.maxWidth){
34028                 w = tm.maxWidth;
34029             }else if(aw < tm.minWidth){
34030                 w = tm.minWidth;
34031             }else{
34032                 w = aw;
34033             }
34034         }
34035         //tipBody.setWidth(w);
34036         el.setWidth(parseInt(w, 10) + p);
34037         if(ce.autoHide === false){
34038             close.setDisplayed(true);
34039             if(dd){
34040                 dd.unlock();
34041             }
34042         }else{
34043             close.setDisplayed(false);
34044             if(dd){
34045                 dd.lock();
34046             }
34047         }
34048         if(xy){
34049             el.avoidY = xy[1]-18;
34050             el.setXY(xy);
34051         }
34052         if(tm.animate){
34053             el.setOpacity(.1);
34054             el.setStyle("visibility", "visible");
34055             el.fadeIn({callback: afterShow});
34056         }else{
34057             afterShow();
34058         }
34059     };
34060     
34061     var afterShow = function(){
34062         if(ce){
34063             el.show();
34064             esc.enable();
34065             if(tm.autoDismiss && ce.autoHide !== false){
34066                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34067             }
34068         }
34069     };
34070     
34071     var hide = function(noanim){
34072         clearTimeout(dismissProc);
34073         clearTimeout(hideProc);
34074         ce = null;
34075         if(el.isVisible()){
34076             esc.disable();
34077             if(noanim !== true && tm.animate){
34078                 el.fadeOut({callback: afterHide});
34079             }else{
34080                 afterHide();
34081             } 
34082         }
34083     };
34084     
34085     var afterHide = function(){
34086         el.hide();
34087         if(removeCls){
34088             el.removeClass(removeCls);
34089             removeCls = null;
34090         }
34091     };
34092     
34093     return {
34094         /**
34095         * @cfg {Number} minWidth
34096         * The minimum width of the quick tip (defaults to 40)
34097         */
34098        minWidth : 40,
34099         /**
34100         * @cfg {Number} maxWidth
34101         * The maximum width of the quick tip (defaults to 300)
34102         */
34103        maxWidth : 300,
34104         /**
34105         * @cfg {Boolean} interceptTitles
34106         * True to automatically use the element's DOM title value if available (defaults to false)
34107         */
34108        interceptTitles : false,
34109         /**
34110         * @cfg {Boolean} trackMouse
34111         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34112         */
34113        trackMouse : false,
34114         /**
34115         * @cfg {Boolean} hideOnClick
34116         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34117         */
34118        hideOnClick : true,
34119         /**
34120         * @cfg {Number} showDelay
34121         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34122         */
34123        showDelay : 500,
34124         /**
34125         * @cfg {Number} hideDelay
34126         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34127         */
34128        hideDelay : 200,
34129         /**
34130         * @cfg {Boolean} autoHide
34131         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34132         * Used in conjunction with hideDelay.
34133         */
34134        autoHide : true,
34135         /**
34136         * @cfg {Boolean}
34137         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34138         * (defaults to true).  Used in conjunction with autoDismissDelay.
34139         */
34140        autoDismiss : true,
34141         /**
34142         * @cfg {Number}
34143         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34144         */
34145        autoDismissDelay : 5000,
34146        /**
34147         * @cfg {Boolean} animate
34148         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34149         */
34150        animate : false,
34151
34152        /**
34153         * @cfg {String} title
34154         * Title text to display (defaults to '').  This can be any valid HTML markup.
34155         */
34156         title: '',
34157        /**
34158         * @cfg {String} text
34159         * Body text to display (defaults to '').  This can be any valid HTML markup.
34160         */
34161         text : '',
34162        /**
34163         * @cfg {String} cls
34164         * A CSS class to apply to the base quick tip element (defaults to '').
34165         */
34166         cls : '',
34167        /**
34168         * @cfg {Number} width
34169         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34170         * minWidth or maxWidth.
34171         */
34172         width : null,
34173
34174     /**
34175      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34176      * or display QuickTips in a page.
34177      */
34178        init : function(){
34179           tm = Roo.QuickTips;
34180           cfg = tm.tagConfig;
34181           if(!inited){
34182               if(!Roo.isReady){ // allow calling of init() before onReady
34183                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34184                   return;
34185               }
34186               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34187               el.fxDefaults = {stopFx: true};
34188               // maximum custom styling
34189               //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>');
34190               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>');              
34191               tipTitle = el.child('h3');
34192               tipTitle.enableDisplayMode("block");
34193               tipBody = el.child('div.x-tip-bd');
34194               tipBodyText = el.child('div.x-tip-bd-inner');
34195               //bdLeft = el.child('div.x-tip-bd-left');
34196               //bdRight = el.child('div.x-tip-bd-right');
34197               close = el.child('div.x-tip-close');
34198               close.enableDisplayMode("block");
34199               close.on("click", hide);
34200               var d = Roo.get(document);
34201               d.on("mousedown", onDown);
34202               d.on("mouseover", onOver);
34203               d.on("mouseout", onOut);
34204               d.on("mousemove", onMove);
34205               esc = d.addKeyListener(27, hide);
34206               esc.disable();
34207               if(Roo.dd.DD){
34208                   dd = el.initDD("default", null, {
34209                       onDrag : function(){
34210                           el.sync();  
34211                       }
34212                   });
34213                   dd.setHandleElId(tipTitle.id);
34214                   dd.lock();
34215               }
34216               inited = true;
34217           }
34218           this.enable(); 
34219        },
34220
34221     /**
34222      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34223      * are supported:
34224      * <pre>
34225 Property    Type                   Description
34226 ----------  ---------------------  ------------------------------------------------------------------------
34227 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34228      * </ul>
34229      * @param {Object} config The config object
34230      */
34231        register : function(config){
34232            var cs = config instanceof Array ? config : arguments;
34233            for(var i = 0, len = cs.length; i < len; i++) {
34234                var c = cs[i];
34235                var target = c.target;
34236                if(target){
34237                    if(target instanceof Array){
34238                        for(var j = 0, jlen = target.length; j < jlen; j++){
34239                            tagEls[target[j]] = c;
34240                        }
34241                    }else{
34242                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34243                    }
34244                }
34245            }
34246        },
34247
34248     /**
34249      * Removes this quick tip from its element and destroys it.
34250      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34251      */
34252        unregister : function(el){
34253            delete tagEls[Roo.id(el)];
34254        },
34255
34256     /**
34257      * Enable this quick tip.
34258      */
34259        enable : function(){
34260            if(inited && disabled){
34261                locks.pop();
34262                if(locks.length < 1){
34263                    disabled = false;
34264                }
34265            }
34266        },
34267
34268     /**
34269      * Disable this quick tip.
34270      */
34271        disable : function(){
34272           disabled = true;
34273           clearTimeout(showProc);
34274           clearTimeout(hideProc);
34275           clearTimeout(dismissProc);
34276           if(ce){
34277               hide(true);
34278           }
34279           locks.push(1);
34280        },
34281
34282     /**
34283      * Returns true if the quick tip is enabled, else false.
34284      */
34285        isEnabled : function(){
34286             return !disabled;
34287        },
34288
34289         // private
34290        tagConfig : {
34291            namespace : "roo", // was ext?? this may break..
34292            alt_namespace : "ext",
34293            attribute : "qtip",
34294            width : "width",
34295            target : "target",
34296            title : "qtitle",
34297            hide : "hide",
34298            cls : "qclass"
34299        }
34300    };
34301 }();
34302
34303 // backwards compat
34304 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34305  * Based on:
34306  * Ext JS Library 1.1.1
34307  * Copyright(c) 2006-2007, Ext JS, LLC.
34308  *
34309  * Originally Released Under LGPL - original licence link has changed is not relivant.
34310  *
34311  * Fork - LGPL
34312  * <script type="text/javascript">
34313  */
34314  
34315
34316 /**
34317  * @class Roo.tree.TreePanel
34318  * @extends Roo.data.Tree
34319
34320  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34321  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34322  * @cfg {Boolean} enableDD true to enable drag and drop
34323  * @cfg {Boolean} enableDrag true to enable just drag
34324  * @cfg {Boolean} enableDrop true to enable just drop
34325  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34326  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34327  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34328  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34329  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34330  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34331  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34332  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34333  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34334  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34335  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34336  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34337  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34338  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34339  * @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>
34340  * @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>
34341  * 
34342  * @constructor
34343  * @param {String/HTMLElement/Element} el The container element
34344  * @param {Object} config
34345  */
34346 Roo.tree.TreePanel = function(el, config){
34347     var root = false;
34348     var loader = false;
34349     if (config.root) {
34350         root = config.root;
34351         delete config.root;
34352     }
34353     if (config.loader) {
34354         loader = config.loader;
34355         delete config.loader;
34356     }
34357     
34358     Roo.apply(this, config);
34359     Roo.tree.TreePanel.superclass.constructor.call(this);
34360     this.el = Roo.get(el);
34361     this.el.addClass('x-tree');
34362     //console.log(root);
34363     if (root) {
34364         this.setRootNode( Roo.factory(root, Roo.tree));
34365     }
34366     if (loader) {
34367         this.loader = Roo.factory(loader, Roo.tree);
34368     }
34369    /**
34370     * Read-only. The id of the container element becomes this TreePanel's id.
34371     */
34372     this.id = this.el.id;
34373     this.addEvents({
34374         /**
34375         * @event beforeload
34376         * Fires before a node is loaded, return false to cancel
34377         * @param {Node} node The node being loaded
34378         */
34379         "beforeload" : true,
34380         /**
34381         * @event load
34382         * Fires when a node is loaded
34383         * @param {Node} node The node that was loaded
34384         */
34385         "load" : true,
34386         /**
34387         * @event textchange
34388         * Fires when the text for a node is changed
34389         * @param {Node} node The node
34390         * @param {String} text The new text
34391         * @param {String} oldText The old text
34392         */
34393         "textchange" : true,
34394         /**
34395         * @event beforeexpand
34396         * Fires before a node is expanded, return false to cancel.
34397         * @param {Node} node The node
34398         * @param {Boolean} deep
34399         * @param {Boolean} anim
34400         */
34401         "beforeexpand" : true,
34402         /**
34403         * @event beforecollapse
34404         * Fires before a node is collapsed, return false to cancel.
34405         * @param {Node} node The node
34406         * @param {Boolean} deep
34407         * @param {Boolean} anim
34408         */
34409         "beforecollapse" : true,
34410         /**
34411         * @event expand
34412         * Fires when a node is expanded
34413         * @param {Node} node The node
34414         */
34415         "expand" : true,
34416         /**
34417         * @event disabledchange
34418         * Fires when the disabled status of a node changes
34419         * @param {Node} node The node
34420         * @param {Boolean} disabled
34421         */
34422         "disabledchange" : true,
34423         /**
34424         * @event collapse
34425         * Fires when a node is collapsed
34426         * @param {Node} node The node
34427         */
34428         "collapse" : true,
34429         /**
34430         * @event beforeclick
34431         * Fires before click processing on a node. Return false to cancel the default action.
34432         * @param {Node} node The node
34433         * @param {Roo.EventObject} e The event object
34434         */
34435         "beforeclick":true,
34436         /**
34437         * @event checkchange
34438         * Fires when a node with a checkbox's checked property changes
34439         * @param {Node} this This node
34440         * @param {Boolean} checked
34441         */
34442         "checkchange":true,
34443         /**
34444         * @event click
34445         * Fires when a node is clicked
34446         * @param {Node} node The node
34447         * @param {Roo.EventObject} e The event object
34448         */
34449         "click":true,
34450         /**
34451         * @event dblclick
34452         * Fires when a node is double clicked
34453         * @param {Node} node The node
34454         * @param {Roo.EventObject} e The event object
34455         */
34456         "dblclick":true,
34457         /**
34458         * @event contextmenu
34459         * Fires when a node is right clicked
34460         * @param {Node} node The node
34461         * @param {Roo.EventObject} e The event object
34462         */
34463         "contextmenu":true,
34464         /**
34465         * @event beforechildrenrendered
34466         * Fires right before the child nodes for a node are rendered
34467         * @param {Node} node The node
34468         */
34469         "beforechildrenrendered":true,
34470         /**
34471         * @event startdrag
34472         * Fires when a node starts being dragged
34473         * @param {Roo.tree.TreePanel} this
34474         * @param {Roo.tree.TreeNode} node
34475         * @param {event} e The raw browser event
34476         */ 
34477        "startdrag" : true,
34478        /**
34479         * @event enddrag
34480         * Fires when a drag operation is complete
34481         * @param {Roo.tree.TreePanel} this
34482         * @param {Roo.tree.TreeNode} node
34483         * @param {event} e The raw browser event
34484         */
34485        "enddrag" : true,
34486        /**
34487         * @event dragdrop
34488         * Fires when a dragged node is dropped on a valid DD target
34489         * @param {Roo.tree.TreePanel} this
34490         * @param {Roo.tree.TreeNode} node
34491         * @param {DD} dd The dd it was dropped on
34492         * @param {event} e The raw browser event
34493         */
34494        "dragdrop" : true,
34495        /**
34496         * @event beforenodedrop
34497         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34498         * passed to handlers has the following properties:<br />
34499         * <ul style="padding:5px;padding-left:16px;">
34500         * <li>tree - The TreePanel</li>
34501         * <li>target - The node being targeted for the drop</li>
34502         * <li>data - The drag data from the drag source</li>
34503         * <li>point - The point of the drop - append, above or below</li>
34504         * <li>source - The drag source</li>
34505         * <li>rawEvent - Raw mouse event</li>
34506         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34507         * to be inserted by setting them on this object.</li>
34508         * <li>cancel - Set this to true to cancel the drop.</li>
34509         * </ul>
34510         * @param {Object} dropEvent
34511         */
34512        "beforenodedrop" : true,
34513        /**
34514         * @event nodedrop
34515         * Fires after a DD object is dropped on a node in this tree. 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 - Dropped node(s).</li>
34525         * </ul>
34526         * @param {Object} dropEvent
34527         */
34528        "nodedrop" : true,
34529         /**
34530         * @event nodedragover
34531         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34532         * passed to handlers has the following properties:<br />
34533         * <ul style="padding:5px;padding-left:16px;">
34534         * <li>tree - The TreePanel</li>
34535         * <li>target - The node being targeted for the drop</li>
34536         * <li>data - The drag data from the drag source</li>
34537         * <li>point - The point of the drop - append, above or below</li>
34538         * <li>source - The drag source</li>
34539         * <li>rawEvent - Raw mouse event</li>
34540         * <li>dropNode - Drop node(s) provided by the source.</li>
34541         * <li>cancel - Set this to true to signal drop not allowed.</li>
34542         * </ul>
34543         * @param {Object} dragOverEvent
34544         */
34545        "nodedragover" : true,
34546        /**
34547         * @event appendnode
34548         * Fires when append node to the tree
34549         * @param {Roo.tree.TreePanel} this
34550         * @param {Roo.tree.TreeNode} node
34551         * @param {Number} index The index of the newly appended node
34552         */
34553        "appendnode" : true
34554         
34555     });
34556     if(this.singleExpand){
34557        this.on("beforeexpand", this.restrictExpand, this);
34558     }
34559     if (this.editor) {
34560         this.editor.tree = this;
34561         this.editor = Roo.factory(this.editor, Roo.tree);
34562     }
34563     
34564     if (this.selModel) {
34565         this.selModel = Roo.factory(this.selModel, Roo.tree);
34566     }
34567    
34568 };
34569 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34570     rootVisible : true,
34571     animate: Roo.enableFx,
34572     lines : true,
34573     enableDD : false,
34574     hlDrop : Roo.enableFx,
34575   
34576     renderer: false,
34577     
34578     rendererTip: false,
34579     // private
34580     restrictExpand : function(node){
34581         var p = node.parentNode;
34582         if(p){
34583             if(p.expandedChild && p.expandedChild.parentNode == p){
34584                 p.expandedChild.collapse();
34585             }
34586             p.expandedChild = node;
34587         }
34588     },
34589
34590     // private override
34591     setRootNode : function(node){
34592         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34593         if(!this.rootVisible){
34594             node.ui = new Roo.tree.RootTreeNodeUI(node);
34595         }
34596         return node;
34597     },
34598
34599     /**
34600      * Returns the container element for this TreePanel
34601      */
34602     getEl : function(){
34603         return this.el;
34604     },
34605
34606     /**
34607      * Returns the default TreeLoader for this TreePanel
34608      */
34609     getLoader : function(){
34610         return this.loader;
34611     },
34612
34613     /**
34614      * Expand all nodes
34615      */
34616     expandAll : function(){
34617         this.root.expand(true);
34618     },
34619
34620     /**
34621      * Collapse all nodes
34622      */
34623     collapseAll : function(){
34624         this.root.collapse(true);
34625     },
34626
34627     /**
34628      * Returns the selection model used by this TreePanel
34629      */
34630     getSelectionModel : function(){
34631         if(!this.selModel){
34632             this.selModel = new Roo.tree.DefaultSelectionModel();
34633         }
34634         return this.selModel;
34635     },
34636
34637     /**
34638      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34639      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34640      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34641      * @return {Array}
34642      */
34643     getChecked : function(a, startNode){
34644         startNode = startNode || this.root;
34645         var r = [];
34646         var f = function(){
34647             if(this.attributes.checked){
34648                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34649             }
34650         }
34651         startNode.cascade(f);
34652         return r;
34653     },
34654
34655     /**
34656      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34657      * @param {String} path
34658      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34659      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34660      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34661      */
34662     expandPath : function(path, attr, callback){
34663         attr = attr || "id";
34664         var keys = path.split(this.pathSeparator);
34665         var curNode = this.root;
34666         if(curNode.attributes[attr] != keys[1]){ // invalid root
34667             if(callback){
34668                 callback(false, null);
34669             }
34670             return;
34671         }
34672         var index = 1;
34673         var f = function(){
34674             if(++index == keys.length){
34675                 if(callback){
34676                     callback(true, curNode);
34677                 }
34678                 return;
34679             }
34680             var c = curNode.findChild(attr, keys[index]);
34681             if(!c){
34682                 if(callback){
34683                     callback(false, curNode);
34684                 }
34685                 return;
34686             }
34687             curNode = c;
34688             c.expand(false, false, f);
34689         };
34690         curNode.expand(false, false, f);
34691     },
34692
34693     /**
34694      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34695      * @param {String} path
34696      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34697      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34698      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34699      */
34700     selectPath : function(path, attr, callback){
34701         attr = attr || "id";
34702         var keys = path.split(this.pathSeparator);
34703         var v = keys.pop();
34704         if(keys.length > 0){
34705             var f = function(success, node){
34706                 if(success && node){
34707                     var n = node.findChild(attr, v);
34708                     if(n){
34709                         n.select();
34710                         if(callback){
34711                             callback(true, n);
34712                         }
34713                     }else if(callback){
34714                         callback(false, n);
34715                     }
34716                 }else{
34717                     if(callback){
34718                         callback(false, n);
34719                     }
34720                 }
34721             };
34722             this.expandPath(keys.join(this.pathSeparator), attr, f);
34723         }else{
34724             this.root.select();
34725             if(callback){
34726                 callback(true, this.root);
34727             }
34728         }
34729     },
34730
34731     getTreeEl : function(){
34732         return this.el;
34733     },
34734
34735     /**
34736      * Trigger rendering of this TreePanel
34737      */
34738     render : function(){
34739         if (this.innerCt) {
34740             return this; // stop it rendering more than once!!
34741         }
34742         
34743         this.innerCt = this.el.createChild({tag:"ul",
34744                cls:"x-tree-root-ct " +
34745                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34746
34747         if(this.containerScroll){
34748             Roo.dd.ScrollManager.register(this.el);
34749         }
34750         if((this.enableDD || this.enableDrop) && !this.dropZone){
34751            /**
34752             * The dropZone used by this tree if drop is enabled
34753             * @type Roo.tree.TreeDropZone
34754             */
34755              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34756                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34757            });
34758         }
34759         if((this.enableDD || this.enableDrag) && !this.dragZone){
34760            /**
34761             * The dragZone used by this tree if drag is enabled
34762             * @type Roo.tree.TreeDragZone
34763             */
34764             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34765                ddGroup: this.ddGroup || "TreeDD",
34766                scroll: this.ddScroll
34767            });
34768         }
34769         this.getSelectionModel().init(this);
34770         if (!this.root) {
34771             Roo.log("ROOT not set in tree");
34772             return this;
34773         }
34774         this.root.render();
34775         if(!this.rootVisible){
34776             this.root.renderChildren();
34777         }
34778         return this;
34779     }
34780 });/*
34781  * Based on:
34782  * Ext JS Library 1.1.1
34783  * Copyright(c) 2006-2007, Ext JS, LLC.
34784  *
34785  * Originally Released Under LGPL - original licence link has changed is not relivant.
34786  *
34787  * Fork - LGPL
34788  * <script type="text/javascript">
34789  */
34790  
34791
34792 /**
34793  * @class Roo.tree.DefaultSelectionModel
34794  * @extends Roo.util.Observable
34795  * The default single selection for a TreePanel.
34796  * @param {Object} cfg Configuration
34797  */
34798 Roo.tree.DefaultSelectionModel = function(cfg){
34799    this.selNode = null;
34800    
34801    
34802    
34803    this.addEvents({
34804        /**
34805         * @event selectionchange
34806         * Fires when the selected node changes
34807         * @param {DefaultSelectionModel} this
34808         * @param {TreeNode} node the new selection
34809         */
34810        "selectionchange" : true,
34811
34812        /**
34813         * @event beforeselect
34814         * Fires before the selected node changes, return false to cancel the change
34815         * @param {DefaultSelectionModel} this
34816         * @param {TreeNode} node the new selection
34817         * @param {TreeNode} node the old selection
34818         */
34819        "beforeselect" : true
34820    });
34821    
34822     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34823 };
34824
34825 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34826     init : function(tree){
34827         this.tree = tree;
34828         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34829         tree.on("click", this.onNodeClick, this);
34830     },
34831     
34832     onNodeClick : function(node, e){
34833         if (e.ctrlKey && this.selNode == node)  {
34834             this.unselect(node);
34835             return;
34836         }
34837         this.select(node);
34838     },
34839     
34840     /**
34841      * Select a node.
34842      * @param {TreeNode} node The node to select
34843      * @return {TreeNode} The selected node
34844      */
34845     select : function(node){
34846         var last = this.selNode;
34847         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34848             if(last){
34849                 last.ui.onSelectedChange(false);
34850             }
34851             this.selNode = node;
34852             node.ui.onSelectedChange(true);
34853             this.fireEvent("selectionchange", this, node, last);
34854         }
34855         return node;
34856     },
34857     
34858     /**
34859      * Deselect a node.
34860      * @param {TreeNode} node The node to unselect
34861      */
34862     unselect : function(node){
34863         if(this.selNode == node){
34864             this.clearSelections();
34865         }    
34866     },
34867     
34868     /**
34869      * Clear all selections
34870      */
34871     clearSelections : function(){
34872         var n = this.selNode;
34873         if(n){
34874             n.ui.onSelectedChange(false);
34875             this.selNode = null;
34876             this.fireEvent("selectionchange", this, null);
34877         }
34878         return n;
34879     },
34880     
34881     /**
34882      * Get the selected node
34883      * @return {TreeNode} The selected node
34884      */
34885     getSelectedNode : function(){
34886         return this.selNode;    
34887     },
34888     
34889     /**
34890      * Returns true if the node is selected
34891      * @param {TreeNode} node The node to check
34892      * @return {Boolean}
34893      */
34894     isSelected : function(node){
34895         return this.selNode == node;  
34896     },
34897
34898     /**
34899      * Selects the node above the selected node in the tree, intelligently walking the nodes
34900      * @return TreeNode The new selection
34901      */
34902     selectPrevious : function(){
34903         var s = this.selNode || this.lastSelNode;
34904         if(!s){
34905             return null;
34906         }
34907         var ps = s.previousSibling;
34908         if(ps){
34909             if(!ps.isExpanded() || ps.childNodes.length < 1){
34910                 return this.select(ps);
34911             } else{
34912                 var lc = ps.lastChild;
34913                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34914                     lc = lc.lastChild;
34915                 }
34916                 return this.select(lc);
34917             }
34918         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34919             return this.select(s.parentNode);
34920         }
34921         return null;
34922     },
34923
34924     /**
34925      * Selects the node above the selected node in the tree, intelligently walking the nodes
34926      * @return TreeNode The new selection
34927      */
34928     selectNext : function(){
34929         var s = this.selNode || this.lastSelNode;
34930         if(!s){
34931             return null;
34932         }
34933         if(s.firstChild && s.isExpanded()){
34934              return this.select(s.firstChild);
34935          }else if(s.nextSibling){
34936              return this.select(s.nextSibling);
34937          }else if(s.parentNode){
34938             var newS = null;
34939             s.parentNode.bubble(function(){
34940                 if(this.nextSibling){
34941                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34942                     return false;
34943                 }
34944             });
34945             return newS;
34946          }
34947         return null;
34948     },
34949
34950     onKeyDown : function(e){
34951         var s = this.selNode || this.lastSelNode;
34952         // undesirable, but required
34953         var sm = this;
34954         if(!s){
34955             return;
34956         }
34957         var k = e.getKey();
34958         switch(k){
34959              case e.DOWN:
34960                  e.stopEvent();
34961                  this.selectNext();
34962              break;
34963              case e.UP:
34964                  e.stopEvent();
34965                  this.selectPrevious();
34966              break;
34967              case e.RIGHT:
34968                  e.preventDefault();
34969                  if(s.hasChildNodes()){
34970                      if(!s.isExpanded()){
34971                          s.expand();
34972                      }else if(s.firstChild){
34973                          this.select(s.firstChild, e);
34974                      }
34975                  }
34976              break;
34977              case e.LEFT:
34978                  e.preventDefault();
34979                  if(s.hasChildNodes() && s.isExpanded()){
34980                      s.collapse();
34981                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34982                      this.select(s.parentNode, e);
34983                  }
34984              break;
34985         };
34986     }
34987 });
34988
34989 /**
34990  * @class Roo.tree.MultiSelectionModel
34991  * @extends Roo.util.Observable
34992  * Multi selection for a TreePanel.
34993  * @param {Object} cfg Configuration
34994  */
34995 Roo.tree.MultiSelectionModel = function(){
34996    this.selNodes = [];
34997    this.selMap = {};
34998    this.addEvents({
34999        /**
35000         * @event selectionchange
35001         * Fires when the selected nodes change
35002         * @param {MultiSelectionModel} this
35003         * @param {Array} nodes Array of the selected nodes
35004         */
35005        "selectionchange" : true
35006    });
35007    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35008    
35009 };
35010
35011 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35012     init : function(tree){
35013         this.tree = tree;
35014         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35015         tree.on("click", this.onNodeClick, this);
35016     },
35017     
35018     onNodeClick : function(node, e){
35019         this.select(node, e, e.ctrlKey);
35020     },
35021     
35022     /**
35023      * Select a node.
35024      * @param {TreeNode} node The node to select
35025      * @param {EventObject} e (optional) An event associated with the selection
35026      * @param {Boolean} keepExisting True to retain existing selections
35027      * @return {TreeNode} The selected node
35028      */
35029     select : function(node, e, keepExisting){
35030         if(keepExisting !== true){
35031             this.clearSelections(true);
35032         }
35033         if(this.isSelected(node)){
35034             this.lastSelNode = node;
35035             return node;
35036         }
35037         this.selNodes.push(node);
35038         this.selMap[node.id] = node;
35039         this.lastSelNode = node;
35040         node.ui.onSelectedChange(true);
35041         this.fireEvent("selectionchange", this, this.selNodes);
35042         return node;
35043     },
35044     
35045     /**
35046      * Deselect a node.
35047      * @param {TreeNode} node The node to unselect
35048      */
35049     unselect : function(node){
35050         if(this.selMap[node.id]){
35051             node.ui.onSelectedChange(false);
35052             var sn = this.selNodes;
35053             var index = -1;
35054             if(sn.indexOf){
35055                 index = sn.indexOf(node);
35056             }else{
35057                 for(var i = 0, len = sn.length; i < len; i++){
35058                     if(sn[i] == node){
35059                         index = i;
35060                         break;
35061                     }
35062                 }
35063             }
35064             if(index != -1){
35065                 this.selNodes.splice(index, 1);
35066             }
35067             delete this.selMap[node.id];
35068             this.fireEvent("selectionchange", this, this.selNodes);
35069         }
35070     },
35071     
35072     /**
35073      * Clear all selections
35074      */
35075     clearSelections : function(suppressEvent){
35076         var sn = this.selNodes;
35077         if(sn.length > 0){
35078             for(var i = 0, len = sn.length; i < len; i++){
35079                 sn[i].ui.onSelectedChange(false);
35080             }
35081             this.selNodes = [];
35082             this.selMap = {};
35083             if(suppressEvent !== true){
35084                 this.fireEvent("selectionchange", this, this.selNodes);
35085             }
35086         }
35087     },
35088     
35089     /**
35090      * Returns true if the node is selected
35091      * @param {TreeNode} node The node to check
35092      * @return {Boolean}
35093      */
35094     isSelected : function(node){
35095         return this.selMap[node.id] ? true : false;  
35096     },
35097     
35098     /**
35099      * Returns an array of the selected nodes
35100      * @return {Array}
35101      */
35102     getSelectedNodes : function(){
35103         return this.selNodes;    
35104     },
35105
35106     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35107
35108     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35109
35110     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35111 });/*
35112  * Based on:
35113  * Ext JS Library 1.1.1
35114  * Copyright(c) 2006-2007, Ext JS, LLC.
35115  *
35116  * Originally Released Under LGPL - original licence link has changed is not relivant.
35117  *
35118  * Fork - LGPL
35119  * <script type="text/javascript">
35120  */
35121  
35122 /**
35123  * @class Roo.tree.TreeNode
35124  * @extends Roo.data.Node
35125  * @cfg {String} text The text for this node
35126  * @cfg {Boolean} expanded true to start the node expanded
35127  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35128  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35129  * @cfg {Boolean} disabled true to start the node disabled
35130  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35131  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35132  * @cfg {String} cls A css class to be added to the node
35133  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35134  * @cfg {String} href URL of the link used for the node (defaults to #)
35135  * @cfg {String} hrefTarget target frame for the link
35136  * @cfg {String} qtip An Ext QuickTip for the node
35137  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35138  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35139  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35140  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35141  * (defaults to undefined with no checkbox rendered)
35142  * @constructor
35143  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35144  */
35145 Roo.tree.TreeNode = function(attributes){
35146     attributes = attributes || {};
35147     if(typeof attributes == "string"){
35148         attributes = {text: attributes};
35149     }
35150     this.childrenRendered = false;
35151     this.rendered = false;
35152     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35153     this.expanded = attributes.expanded === true;
35154     this.isTarget = attributes.isTarget !== false;
35155     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35156     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35157
35158     /**
35159      * Read-only. The text for this node. To change it use setText().
35160      * @type String
35161      */
35162     this.text = attributes.text;
35163     /**
35164      * True if this node is disabled.
35165      * @type Boolean
35166      */
35167     this.disabled = attributes.disabled === true;
35168
35169     this.addEvents({
35170         /**
35171         * @event textchange
35172         * Fires when the text for this node is changed
35173         * @param {Node} this This node
35174         * @param {String} text The new text
35175         * @param {String} oldText The old text
35176         */
35177         "textchange" : true,
35178         /**
35179         * @event beforeexpand
35180         * Fires before this node is expanded, return false to cancel.
35181         * @param {Node} this This node
35182         * @param {Boolean} deep
35183         * @param {Boolean} anim
35184         */
35185         "beforeexpand" : true,
35186         /**
35187         * @event beforecollapse
35188         * Fires before this node is collapsed, return false to cancel.
35189         * @param {Node} this This node
35190         * @param {Boolean} deep
35191         * @param {Boolean} anim
35192         */
35193         "beforecollapse" : true,
35194         /**
35195         * @event expand
35196         * Fires when this node is expanded
35197         * @param {Node} this This node
35198         */
35199         "expand" : true,
35200         /**
35201         * @event disabledchange
35202         * Fires when the disabled status of this node changes
35203         * @param {Node} this This node
35204         * @param {Boolean} disabled
35205         */
35206         "disabledchange" : true,
35207         /**
35208         * @event collapse
35209         * Fires when this node is collapsed
35210         * @param {Node} this This node
35211         */
35212         "collapse" : true,
35213         /**
35214         * @event beforeclick
35215         * Fires before click processing. Return false to cancel the default action.
35216         * @param {Node} this This node
35217         * @param {Roo.EventObject} e The event object
35218         */
35219         "beforeclick":true,
35220         /**
35221         * @event checkchange
35222         * Fires when a node with a checkbox's checked property changes
35223         * @param {Node} this This node
35224         * @param {Boolean} checked
35225         */
35226         "checkchange":true,
35227         /**
35228         * @event click
35229         * Fires when this node is clicked
35230         * @param {Node} this This node
35231         * @param {Roo.EventObject} e The event object
35232         */
35233         "click":true,
35234         /**
35235         * @event dblclick
35236         * Fires when this node is double clicked
35237         * @param {Node} this This node
35238         * @param {Roo.EventObject} e The event object
35239         */
35240         "dblclick":true,
35241         /**
35242         * @event contextmenu
35243         * Fires when this node is right clicked
35244         * @param {Node} this This node
35245         * @param {Roo.EventObject} e The event object
35246         */
35247         "contextmenu":true,
35248         /**
35249         * @event beforechildrenrendered
35250         * Fires right before the child nodes for this node are rendered
35251         * @param {Node} this This node
35252         */
35253         "beforechildrenrendered":true
35254     });
35255
35256     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35257
35258     /**
35259      * Read-only. The UI for this node
35260      * @type TreeNodeUI
35261      */
35262     this.ui = new uiClass(this);
35263     
35264     // finally support items[]
35265     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35266         return;
35267     }
35268     
35269     
35270     Roo.each(this.attributes.items, function(c) {
35271         this.appendChild(Roo.factory(c,Roo.Tree));
35272     }, this);
35273     delete this.attributes.items;
35274     
35275     
35276     
35277 };
35278 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35279     preventHScroll: true,
35280     /**
35281      * Returns true if this node is expanded
35282      * @return {Boolean}
35283      */
35284     isExpanded : function(){
35285         return this.expanded;
35286     },
35287
35288     /**
35289      * Returns the UI object for this node
35290      * @return {TreeNodeUI}
35291      */
35292     getUI : function(){
35293         return this.ui;
35294     },
35295
35296     // private override
35297     setFirstChild : function(node){
35298         var of = this.firstChild;
35299         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35300         if(this.childrenRendered && of && node != of){
35301             of.renderIndent(true, true);
35302         }
35303         if(this.rendered){
35304             this.renderIndent(true, true);
35305         }
35306     },
35307
35308     // private override
35309     setLastChild : function(node){
35310         var ol = this.lastChild;
35311         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35312         if(this.childrenRendered && ol && node != ol){
35313             ol.renderIndent(true, true);
35314         }
35315         if(this.rendered){
35316             this.renderIndent(true, true);
35317         }
35318     },
35319
35320     // these methods are overridden to provide lazy rendering support
35321     // private override
35322     appendChild : function()
35323     {
35324         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35325         if(node && this.childrenRendered){
35326             node.render();
35327         }
35328         this.ui.updateExpandIcon();
35329         return node;
35330     },
35331
35332     // private override
35333     removeChild : function(node){
35334         this.ownerTree.getSelectionModel().unselect(node);
35335         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35336         // if it's been rendered remove dom node
35337         if(this.childrenRendered){
35338             node.ui.remove();
35339         }
35340         if(this.childNodes.length < 1){
35341             this.collapse(false, false);
35342         }else{
35343             this.ui.updateExpandIcon();
35344         }
35345         if(!this.firstChild) {
35346             this.childrenRendered = false;
35347         }
35348         return node;
35349     },
35350
35351     // private override
35352     insertBefore : function(node, refNode){
35353         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35354         if(newNode && refNode && this.childrenRendered){
35355             node.render();
35356         }
35357         this.ui.updateExpandIcon();
35358         return newNode;
35359     },
35360
35361     /**
35362      * Sets the text for this node
35363      * @param {String} text
35364      */
35365     setText : function(text){
35366         var oldText = this.text;
35367         this.text = text;
35368         this.attributes.text = text;
35369         if(this.rendered){ // event without subscribing
35370             this.ui.onTextChange(this, text, oldText);
35371         }
35372         this.fireEvent("textchange", this, text, oldText);
35373     },
35374
35375     /**
35376      * Triggers selection of this node
35377      */
35378     select : function(){
35379         this.getOwnerTree().getSelectionModel().select(this);
35380     },
35381
35382     /**
35383      * Triggers deselection of this node
35384      */
35385     unselect : function(){
35386         this.getOwnerTree().getSelectionModel().unselect(this);
35387     },
35388
35389     /**
35390      * Returns true if this node is selected
35391      * @return {Boolean}
35392      */
35393     isSelected : function(){
35394         return this.getOwnerTree().getSelectionModel().isSelected(this);
35395     },
35396
35397     /**
35398      * Expand this node.
35399      * @param {Boolean} deep (optional) True to expand all children as well
35400      * @param {Boolean} anim (optional) false to cancel the default animation
35401      * @param {Function} callback (optional) A callback to be called when
35402      * expanding this node completes (does not wait for deep expand to complete).
35403      * Called with 1 parameter, this node.
35404      */
35405     expand : function(deep, anim, callback){
35406         if(!this.expanded){
35407             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35408                 return;
35409             }
35410             if(!this.childrenRendered){
35411                 this.renderChildren();
35412             }
35413             this.expanded = true;
35414             
35415             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35416                 this.ui.animExpand(function(){
35417                     this.fireEvent("expand", this);
35418                     if(typeof callback == "function"){
35419                         callback(this);
35420                     }
35421                     if(deep === true){
35422                         this.expandChildNodes(true);
35423                     }
35424                 }.createDelegate(this));
35425                 return;
35426             }else{
35427                 this.ui.expand();
35428                 this.fireEvent("expand", this);
35429                 if(typeof callback == "function"){
35430                     callback(this);
35431                 }
35432             }
35433         }else{
35434            if(typeof callback == "function"){
35435                callback(this);
35436            }
35437         }
35438         if(deep === true){
35439             this.expandChildNodes(true);
35440         }
35441     },
35442
35443     isHiddenRoot : function(){
35444         return this.isRoot && !this.getOwnerTree().rootVisible;
35445     },
35446
35447     /**
35448      * Collapse this node.
35449      * @param {Boolean} deep (optional) True to collapse all children as well
35450      * @param {Boolean} anim (optional) false to cancel the default animation
35451      */
35452     collapse : function(deep, anim){
35453         if(this.expanded && !this.isHiddenRoot()){
35454             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35455                 return;
35456             }
35457             this.expanded = false;
35458             if((this.getOwnerTree().animate && anim !== false) || anim){
35459                 this.ui.animCollapse(function(){
35460                     this.fireEvent("collapse", this);
35461                     if(deep === true){
35462                         this.collapseChildNodes(true);
35463                     }
35464                 }.createDelegate(this));
35465                 return;
35466             }else{
35467                 this.ui.collapse();
35468                 this.fireEvent("collapse", this);
35469             }
35470         }
35471         if(deep === true){
35472             var cs = this.childNodes;
35473             for(var i = 0, len = cs.length; i < len; i++) {
35474                 cs[i].collapse(true, false);
35475             }
35476         }
35477     },
35478
35479     // private
35480     delayedExpand : function(delay){
35481         if(!this.expandProcId){
35482             this.expandProcId = this.expand.defer(delay, this);
35483         }
35484     },
35485
35486     // private
35487     cancelExpand : function(){
35488         if(this.expandProcId){
35489             clearTimeout(this.expandProcId);
35490         }
35491         this.expandProcId = false;
35492     },
35493
35494     /**
35495      * Toggles expanded/collapsed state of the node
35496      */
35497     toggle : function(){
35498         if(this.expanded){
35499             this.collapse();
35500         }else{
35501             this.expand();
35502         }
35503     },
35504
35505     /**
35506      * Ensures all parent nodes are expanded
35507      */
35508     ensureVisible : function(callback){
35509         var tree = this.getOwnerTree();
35510         tree.expandPath(this.parentNode.getPath(), false, function(){
35511             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35512             Roo.callback(callback);
35513         }.createDelegate(this));
35514     },
35515
35516     /**
35517      * Expand all child nodes
35518      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35519      */
35520     expandChildNodes : function(deep){
35521         var cs = this.childNodes;
35522         for(var i = 0, len = cs.length; i < len; i++) {
35523                 cs[i].expand(deep);
35524         }
35525     },
35526
35527     /**
35528      * Collapse all child nodes
35529      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35530      */
35531     collapseChildNodes : function(deep){
35532         var cs = this.childNodes;
35533         for(var i = 0, len = cs.length; i < len; i++) {
35534                 cs[i].collapse(deep);
35535         }
35536     },
35537
35538     /**
35539      * Disables this node
35540      */
35541     disable : function(){
35542         this.disabled = true;
35543         this.unselect();
35544         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35545             this.ui.onDisableChange(this, true);
35546         }
35547         this.fireEvent("disabledchange", this, true);
35548     },
35549
35550     /**
35551      * Enables this node
35552      */
35553     enable : function(){
35554         this.disabled = false;
35555         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35556             this.ui.onDisableChange(this, false);
35557         }
35558         this.fireEvent("disabledchange", this, false);
35559     },
35560
35561     // private
35562     renderChildren : function(suppressEvent){
35563         if(suppressEvent !== false){
35564             this.fireEvent("beforechildrenrendered", this);
35565         }
35566         var cs = this.childNodes;
35567         for(var i = 0, len = cs.length; i < len; i++){
35568             cs[i].render(true);
35569         }
35570         this.childrenRendered = true;
35571     },
35572
35573     // private
35574     sort : function(fn, scope){
35575         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35576         if(this.childrenRendered){
35577             var cs = this.childNodes;
35578             for(var i = 0, len = cs.length; i < len; i++){
35579                 cs[i].render(true);
35580             }
35581         }
35582     },
35583
35584     // private
35585     render : function(bulkRender){
35586         this.ui.render(bulkRender);
35587         if(!this.rendered){
35588             this.rendered = true;
35589             if(this.expanded){
35590                 this.expanded = false;
35591                 this.expand(false, false);
35592             }
35593         }
35594     },
35595
35596     // private
35597     renderIndent : function(deep, refresh){
35598         if(refresh){
35599             this.ui.childIndent = null;
35600         }
35601         this.ui.renderIndent();
35602         if(deep === true && this.childrenRendered){
35603             var cs = this.childNodes;
35604             for(var i = 0, len = cs.length; i < len; i++){
35605                 cs[i].renderIndent(true, refresh);
35606             }
35607         }
35608     }
35609 });/*
35610  * Based on:
35611  * Ext JS Library 1.1.1
35612  * Copyright(c) 2006-2007, Ext JS, LLC.
35613  *
35614  * Originally Released Under LGPL - original licence link has changed is not relivant.
35615  *
35616  * Fork - LGPL
35617  * <script type="text/javascript">
35618  */
35619  
35620 /**
35621  * @class Roo.tree.AsyncTreeNode
35622  * @extends Roo.tree.TreeNode
35623  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35624  * @constructor
35625  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35626  */
35627  Roo.tree.AsyncTreeNode = function(config){
35628     this.loaded = false;
35629     this.loading = false;
35630     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35631     /**
35632     * @event beforeload
35633     * Fires before this node is loaded, return false to cancel
35634     * @param {Node} this This node
35635     */
35636     this.addEvents({'beforeload':true, 'load': true});
35637     /**
35638     * @event load
35639     * Fires when this node is loaded
35640     * @param {Node} this This node
35641     */
35642     /**
35643      * The loader used by this node (defaults to using the tree's defined loader)
35644      * @type TreeLoader
35645      * @property loader
35646      */
35647 };
35648 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35649     expand : function(deep, anim, callback){
35650         if(this.loading){ // if an async load is already running, waiting til it's done
35651             var timer;
35652             var f = function(){
35653                 if(!this.loading){ // done loading
35654                     clearInterval(timer);
35655                     this.expand(deep, anim, callback);
35656                 }
35657             }.createDelegate(this);
35658             timer = setInterval(f, 200);
35659             return;
35660         }
35661         if(!this.loaded){
35662             if(this.fireEvent("beforeload", this) === false){
35663                 return;
35664             }
35665             this.loading = true;
35666             this.ui.beforeLoad(this);
35667             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35668             if(loader){
35669                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35670                 return;
35671             }
35672         }
35673         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35674     },
35675     
35676     /**
35677      * Returns true if this node is currently loading
35678      * @return {Boolean}
35679      */
35680     isLoading : function(){
35681         return this.loading;  
35682     },
35683     
35684     loadComplete : function(deep, anim, callback){
35685         this.loading = false;
35686         this.loaded = true;
35687         this.ui.afterLoad(this);
35688         this.fireEvent("load", this);
35689         this.expand(deep, anim, callback);
35690     },
35691     
35692     /**
35693      * Returns true if this node has been loaded
35694      * @return {Boolean}
35695      */
35696     isLoaded : function(){
35697         return this.loaded;
35698     },
35699     
35700     hasChildNodes : function(){
35701         if(!this.isLeaf() && !this.loaded){
35702             return true;
35703         }else{
35704             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35705         }
35706     },
35707
35708     /**
35709      * Trigger a reload for this node
35710      * @param {Function} callback
35711      */
35712     reload : function(callback){
35713         this.collapse(false, false);
35714         while(this.firstChild){
35715             this.removeChild(this.firstChild);
35716         }
35717         this.childrenRendered = false;
35718         this.loaded = false;
35719         if(this.isHiddenRoot()){
35720             this.expanded = false;
35721         }
35722         this.expand(false, false, callback);
35723     }
35724 });/*
35725  * Based on:
35726  * Ext JS Library 1.1.1
35727  * Copyright(c) 2006-2007, Ext JS, LLC.
35728  *
35729  * Originally Released Under LGPL - original licence link has changed is not relivant.
35730  *
35731  * Fork - LGPL
35732  * <script type="text/javascript">
35733  */
35734  
35735 /**
35736  * @class Roo.tree.TreeNodeUI
35737  * @constructor
35738  * @param {Object} node The node to render
35739  * The TreeNode UI implementation is separate from the
35740  * tree implementation. Unless you are customizing the tree UI,
35741  * you should never have to use this directly.
35742  */
35743 Roo.tree.TreeNodeUI = function(node){
35744     this.node = node;
35745     this.rendered = false;
35746     this.animating = false;
35747     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35748 };
35749
35750 Roo.tree.TreeNodeUI.prototype = {
35751     removeChild : function(node){
35752         if(this.rendered){
35753             this.ctNode.removeChild(node.ui.getEl());
35754         }
35755     },
35756
35757     beforeLoad : function(){
35758          this.addClass("x-tree-node-loading");
35759     },
35760
35761     afterLoad : function(){
35762          this.removeClass("x-tree-node-loading");
35763     },
35764
35765     onTextChange : function(node, text, oldText){
35766         if(this.rendered){
35767             this.textNode.innerHTML = text;
35768         }
35769     },
35770
35771     onDisableChange : function(node, state){
35772         this.disabled = state;
35773         if(state){
35774             this.addClass("x-tree-node-disabled");
35775         }else{
35776             this.removeClass("x-tree-node-disabled");
35777         }
35778     },
35779
35780     onSelectedChange : function(state){
35781         if(state){
35782             this.focus();
35783             this.addClass("x-tree-selected");
35784         }else{
35785             //this.blur();
35786             this.removeClass("x-tree-selected");
35787         }
35788     },
35789
35790     onMove : function(tree, node, oldParent, newParent, index, refNode){
35791         this.childIndent = null;
35792         if(this.rendered){
35793             var targetNode = newParent.ui.getContainer();
35794             if(!targetNode){//target not rendered
35795                 this.holder = document.createElement("div");
35796                 this.holder.appendChild(this.wrap);
35797                 return;
35798             }
35799             var insertBefore = refNode ? refNode.ui.getEl() : null;
35800             if(insertBefore){
35801                 targetNode.insertBefore(this.wrap, insertBefore);
35802             }else{
35803                 targetNode.appendChild(this.wrap);
35804             }
35805             this.node.renderIndent(true);
35806         }
35807     },
35808
35809     addClass : function(cls){
35810         if(this.elNode){
35811             Roo.fly(this.elNode).addClass(cls);
35812         }
35813     },
35814
35815     removeClass : function(cls){
35816         if(this.elNode){
35817             Roo.fly(this.elNode).removeClass(cls);
35818         }
35819     },
35820
35821     remove : function(){
35822         if(this.rendered){
35823             this.holder = document.createElement("div");
35824             this.holder.appendChild(this.wrap);
35825         }
35826     },
35827
35828     fireEvent : function(){
35829         return this.node.fireEvent.apply(this.node, arguments);
35830     },
35831
35832     initEvents : function(){
35833         this.node.on("move", this.onMove, this);
35834         var E = Roo.EventManager;
35835         var a = this.anchor;
35836
35837         var el = Roo.fly(a, '_treeui');
35838
35839         if(Roo.isOpera){ // opera render bug ignores the CSS
35840             el.setStyle("text-decoration", "none");
35841         }
35842
35843         el.on("click", this.onClick, this);
35844         el.on("dblclick", this.onDblClick, this);
35845
35846         if(this.checkbox){
35847             Roo.EventManager.on(this.checkbox,
35848                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35849         }
35850
35851         el.on("contextmenu", this.onContextMenu, this);
35852
35853         var icon = Roo.fly(this.iconNode);
35854         icon.on("click", this.onClick, this);
35855         icon.on("dblclick", this.onDblClick, this);
35856         icon.on("contextmenu", this.onContextMenu, this);
35857         E.on(this.ecNode, "click", this.ecClick, this, true);
35858
35859         if(this.node.disabled){
35860             this.addClass("x-tree-node-disabled");
35861         }
35862         if(this.node.hidden){
35863             this.addClass("x-tree-node-disabled");
35864         }
35865         var ot = this.node.getOwnerTree();
35866         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35867         if(dd && (!this.node.isRoot || ot.rootVisible)){
35868             Roo.dd.Registry.register(this.elNode, {
35869                 node: this.node,
35870                 handles: this.getDDHandles(),
35871                 isHandle: false
35872             });
35873         }
35874     },
35875
35876     getDDHandles : function(){
35877         return [this.iconNode, this.textNode];
35878     },
35879
35880     hide : function(){
35881         if(this.rendered){
35882             this.wrap.style.display = "none";
35883         }
35884     },
35885
35886     show : function(){
35887         if(this.rendered){
35888             this.wrap.style.display = "";
35889         }
35890     },
35891
35892     onContextMenu : function(e){
35893         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35894             e.preventDefault();
35895             this.focus();
35896             this.fireEvent("contextmenu", this.node, e);
35897         }
35898     },
35899
35900     onClick : function(e){
35901         if(this.dropping){
35902             e.stopEvent();
35903             return;
35904         }
35905         if(this.fireEvent("beforeclick", this.node, e) !== false){
35906             if(!this.disabled && this.node.attributes.href){
35907                 this.fireEvent("click", this.node, e);
35908                 return;
35909             }
35910             e.preventDefault();
35911             if(this.disabled){
35912                 return;
35913             }
35914
35915             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35916                 this.node.toggle();
35917             }
35918
35919             this.fireEvent("click", this.node, e);
35920         }else{
35921             e.stopEvent();
35922         }
35923     },
35924
35925     onDblClick : function(e){
35926         e.preventDefault();
35927         if(this.disabled){
35928             return;
35929         }
35930         if(this.checkbox){
35931             this.toggleCheck();
35932         }
35933         if(!this.animating && this.node.hasChildNodes()){
35934             this.node.toggle();
35935         }
35936         this.fireEvent("dblclick", this.node, e);
35937     },
35938
35939     onCheckChange : function(){
35940         var checked = this.checkbox.checked;
35941         this.node.attributes.checked = checked;
35942         this.fireEvent('checkchange', this.node, checked);
35943     },
35944
35945     ecClick : function(e){
35946         if(!this.animating && this.node.hasChildNodes()){
35947             this.node.toggle();
35948         }
35949     },
35950
35951     startDrop : function(){
35952         this.dropping = true;
35953     },
35954
35955     // delayed drop so the click event doesn't get fired on a drop
35956     endDrop : function(){
35957        setTimeout(function(){
35958            this.dropping = false;
35959        }.createDelegate(this), 50);
35960     },
35961
35962     expand : function(){
35963         this.updateExpandIcon();
35964         this.ctNode.style.display = "";
35965     },
35966
35967     focus : function(){
35968         if(!this.node.preventHScroll){
35969             try{this.anchor.focus();
35970             }catch(e){}
35971         }else if(!Roo.isIE){
35972             try{
35973                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35974                 var l = noscroll.scrollLeft;
35975                 this.anchor.focus();
35976                 noscroll.scrollLeft = l;
35977             }catch(e){}
35978         }
35979     },
35980
35981     toggleCheck : function(value){
35982         var cb = this.checkbox;
35983         if(cb){
35984             cb.checked = (value === undefined ? !cb.checked : value);
35985         }
35986     },
35987
35988     blur : function(){
35989         try{
35990             this.anchor.blur();
35991         }catch(e){}
35992     },
35993
35994     animExpand : function(callback){
35995         var ct = Roo.get(this.ctNode);
35996         ct.stopFx();
35997         if(!this.node.hasChildNodes()){
35998             this.updateExpandIcon();
35999             this.ctNode.style.display = "";
36000             Roo.callback(callback);
36001             return;
36002         }
36003         this.animating = true;
36004         this.updateExpandIcon();
36005
36006         ct.slideIn('t', {
36007            callback : function(){
36008                this.animating = false;
36009                Roo.callback(callback);
36010             },
36011             scope: this,
36012             duration: this.node.ownerTree.duration || .25
36013         });
36014     },
36015
36016     highlight : function(){
36017         var tree = this.node.getOwnerTree();
36018         Roo.fly(this.wrap).highlight(
36019             tree.hlColor || "C3DAF9",
36020             {endColor: tree.hlBaseColor}
36021         );
36022     },
36023
36024     collapse : function(){
36025         this.updateExpandIcon();
36026         this.ctNode.style.display = "none";
36027     },
36028
36029     animCollapse : function(callback){
36030         var ct = Roo.get(this.ctNode);
36031         ct.enableDisplayMode('block');
36032         ct.stopFx();
36033
36034         this.animating = true;
36035         this.updateExpandIcon();
36036
36037         ct.slideOut('t', {
36038             callback : function(){
36039                this.animating = false;
36040                Roo.callback(callback);
36041             },
36042             scope: this,
36043             duration: this.node.ownerTree.duration || .25
36044         });
36045     },
36046
36047     getContainer : function(){
36048         return this.ctNode;
36049     },
36050
36051     getEl : function(){
36052         return this.wrap;
36053     },
36054
36055     appendDDGhost : function(ghostNode){
36056         ghostNode.appendChild(this.elNode.cloneNode(true));
36057     },
36058
36059     getDDRepairXY : function(){
36060         return Roo.lib.Dom.getXY(this.iconNode);
36061     },
36062
36063     onRender : function(){
36064         this.render();
36065     },
36066
36067     render : function(bulkRender){
36068         var n = this.node, a = n.attributes;
36069         var targetNode = n.parentNode ?
36070               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36071
36072         if(!this.rendered){
36073             this.rendered = true;
36074
36075             this.renderElements(n, a, targetNode, bulkRender);
36076
36077             if(a.qtip){
36078                if(this.textNode.setAttributeNS){
36079                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36080                    if(a.qtipTitle){
36081                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36082                    }
36083                }else{
36084                    this.textNode.setAttribute("ext:qtip", a.qtip);
36085                    if(a.qtipTitle){
36086                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36087                    }
36088                }
36089             }else if(a.qtipCfg){
36090                 a.qtipCfg.target = Roo.id(this.textNode);
36091                 Roo.QuickTips.register(a.qtipCfg);
36092             }
36093             this.initEvents();
36094             if(!this.node.expanded){
36095                 this.updateExpandIcon();
36096             }
36097         }else{
36098             if(bulkRender === true) {
36099                 targetNode.appendChild(this.wrap);
36100             }
36101         }
36102     },
36103
36104     renderElements : function(n, a, targetNode, bulkRender)
36105     {
36106         // add some indent caching, this helps performance when rendering a large tree
36107         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36108         var t = n.getOwnerTree();
36109         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36110         if (typeof(n.attributes.html) != 'undefined') {
36111             txt = n.attributes.html;
36112         }
36113         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36114         var cb = typeof a.checked == 'boolean';
36115         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36116         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36117             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36118             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36119             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36120             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36121             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36122              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36123                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36124             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36125             "</li>"];
36126
36127         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36128             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36129                                 n.nextSibling.ui.getEl(), buf.join(""));
36130         }else{
36131             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36132         }
36133
36134         this.elNode = this.wrap.childNodes[0];
36135         this.ctNode = this.wrap.childNodes[1];
36136         var cs = this.elNode.childNodes;
36137         this.indentNode = cs[0];
36138         this.ecNode = cs[1];
36139         this.iconNode = cs[2];
36140         var index = 3;
36141         if(cb){
36142             this.checkbox = cs[3];
36143             index++;
36144         }
36145         this.anchor = cs[index];
36146         this.textNode = cs[index].firstChild;
36147     },
36148
36149     getAnchor : function(){
36150         return this.anchor;
36151     },
36152
36153     getTextEl : function(){
36154         return this.textNode;
36155     },
36156
36157     getIconEl : function(){
36158         return this.iconNode;
36159     },
36160
36161     isChecked : function(){
36162         return this.checkbox ? this.checkbox.checked : false;
36163     },
36164
36165     updateExpandIcon : function(){
36166         if(this.rendered){
36167             var n = this.node, c1, c2;
36168             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36169             var hasChild = n.hasChildNodes();
36170             if(hasChild){
36171                 if(n.expanded){
36172                     cls += "-minus";
36173                     c1 = "x-tree-node-collapsed";
36174                     c2 = "x-tree-node-expanded";
36175                 }else{
36176                     cls += "-plus";
36177                     c1 = "x-tree-node-expanded";
36178                     c2 = "x-tree-node-collapsed";
36179                 }
36180                 if(this.wasLeaf){
36181                     this.removeClass("x-tree-node-leaf");
36182                     this.wasLeaf = false;
36183                 }
36184                 if(this.c1 != c1 || this.c2 != c2){
36185                     Roo.fly(this.elNode).replaceClass(c1, c2);
36186                     this.c1 = c1; this.c2 = c2;
36187                 }
36188             }else{
36189                 // this changes non-leafs into leafs if they have no children.
36190                 // it's not very rational behaviour..
36191                 
36192                 if(!this.wasLeaf && this.node.leaf){
36193                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36194                     delete this.c1;
36195                     delete this.c2;
36196                     this.wasLeaf = true;
36197                 }
36198             }
36199             var ecc = "x-tree-ec-icon "+cls;
36200             if(this.ecc != ecc){
36201                 this.ecNode.className = ecc;
36202                 this.ecc = ecc;
36203             }
36204         }
36205     },
36206
36207     getChildIndent : function(){
36208         if(!this.childIndent){
36209             var buf = [];
36210             var p = this.node;
36211             while(p){
36212                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36213                     if(!p.isLast()) {
36214                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36215                     } else {
36216                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36217                     }
36218                 }
36219                 p = p.parentNode;
36220             }
36221             this.childIndent = buf.join("");
36222         }
36223         return this.childIndent;
36224     },
36225
36226     renderIndent : function(){
36227         if(this.rendered){
36228             var indent = "";
36229             var p = this.node.parentNode;
36230             if(p){
36231                 indent = p.ui.getChildIndent();
36232             }
36233             if(this.indentMarkup != indent){ // don't rerender if not required
36234                 this.indentNode.innerHTML = indent;
36235                 this.indentMarkup = indent;
36236             }
36237             this.updateExpandIcon();
36238         }
36239     }
36240 };
36241
36242 Roo.tree.RootTreeNodeUI = function(){
36243     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36244 };
36245 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36246     render : function(){
36247         if(!this.rendered){
36248             var targetNode = this.node.ownerTree.innerCt.dom;
36249             this.node.expanded = true;
36250             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36251             this.wrap = this.ctNode = targetNode.firstChild;
36252         }
36253     },
36254     collapse : function(){
36255     },
36256     expand : function(){
36257     }
36258 });/*
36259  * Based on:
36260  * Ext JS Library 1.1.1
36261  * Copyright(c) 2006-2007, Ext JS, LLC.
36262  *
36263  * Originally Released Under LGPL - original licence link has changed is not relivant.
36264  *
36265  * Fork - LGPL
36266  * <script type="text/javascript">
36267  */
36268 /**
36269  * @class Roo.tree.TreeLoader
36270  * @extends Roo.util.Observable
36271  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36272  * nodes from a specified URL. The response must be a javascript Array definition
36273  * who's elements are node definition objects. eg:
36274  * <pre><code>
36275 {  success : true,
36276    data :      [
36277    
36278     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36279     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36280     ]
36281 }
36282
36283
36284 </code></pre>
36285  * <br><br>
36286  * The old style respose with just an array is still supported, but not recommended.
36287  * <br><br>
36288  *
36289  * A server request is sent, and child nodes are loaded only when a node is expanded.
36290  * The loading node's id is passed to the server under the parameter name "node" to
36291  * enable the server to produce the correct child nodes.
36292  * <br><br>
36293  * To pass extra parameters, an event handler may be attached to the "beforeload"
36294  * event, and the parameters specified in the TreeLoader's baseParams property:
36295  * <pre><code>
36296     myTreeLoader.on("beforeload", function(treeLoader, node) {
36297         this.baseParams.category = node.attributes.category;
36298     }, this);
36299     
36300 </code></pre>
36301  *
36302  * This would pass an HTTP parameter called "category" to the server containing
36303  * the value of the Node's "category" attribute.
36304  * @constructor
36305  * Creates a new Treeloader.
36306  * @param {Object} config A config object containing config properties.
36307  */
36308 Roo.tree.TreeLoader = function(config){
36309     this.baseParams = {};
36310     this.requestMethod = "POST";
36311     Roo.apply(this, config);
36312
36313     this.addEvents({
36314     
36315         /**
36316          * @event beforeload
36317          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36318          * @param {Object} This TreeLoader object.
36319          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36320          * @param {Object} callback The callback function specified in the {@link #load} call.
36321          */
36322         beforeload : true,
36323         /**
36324          * @event load
36325          * Fires when the node has been successfuly loaded.
36326          * @param {Object} This TreeLoader object.
36327          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36328          * @param {Object} response The response object containing the data from the server.
36329          */
36330         load : true,
36331         /**
36332          * @event loadexception
36333          * Fires if the network request failed.
36334          * @param {Object} This TreeLoader object.
36335          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36336          * @param {Object} response The response object containing the data from the server.
36337          */
36338         loadexception : true,
36339         /**
36340          * @event create
36341          * Fires before a node is created, enabling you to return custom Node types 
36342          * @param {Object} This TreeLoader object.
36343          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36344          */
36345         create : true
36346     });
36347
36348     Roo.tree.TreeLoader.superclass.constructor.call(this);
36349 };
36350
36351 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36352     /**
36353     * @cfg {String} dataUrl The URL from which to request a Json string which
36354     * specifies an array of node definition object representing the child nodes
36355     * to be loaded.
36356     */
36357     /**
36358     * @cfg {String} requestMethod either GET or POST
36359     * defaults to POST (due to BC)
36360     * to be loaded.
36361     */
36362     /**
36363     * @cfg {Object} baseParams (optional) An object containing properties which
36364     * specify HTTP parameters to be passed to each request for child nodes.
36365     */
36366     /**
36367     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36368     * created by this loader. If the attributes sent by the server have an attribute in this object,
36369     * they take priority.
36370     */
36371     /**
36372     * @cfg {Object} uiProviders (optional) An object containing properties which
36373     * 
36374     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36375     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36376     * <i>uiProvider</i> attribute of a returned child node is a string rather
36377     * than a reference to a TreeNodeUI implementation, this that string value
36378     * is used as a property name in the uiProviders object. You can define the provider named
36379     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36380     */
36381     uiProviders : {},
36382
36383     /**
36384     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36385     * child nodes before loading.
36386     */
36387     clearOnLoad : true,
36388
36389     /**
36390     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36391     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36392     * Grid query { data : [ .....] }
36393     */
36394     
36395     root : false,
36396      /**
36397     * @cfg {String} queryParam (optional) 
36398     * Name of the query as it will be passed on the querystring (defaults to 'node')
36399     * eg. the request will be ?node=[id]
36400     */
36401     
36402     
36403     queryParam: false,
36404     
36405     /**
36406      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36407      * This is called automatically when a node is expanded, but may be used to reload
36408      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36409      * @param {Roo.tree.TreeNode} node
36410      * @param {Function} callback
36411      */
36412     load : function(node, callback){
36413         if(this.clearOnLoad){
36414             while(node.firstChild){
36415                 node.removeChild(node.firstChild);
36416             }
36417         }
36418         if(node.attributes.children){ // preloaded json children
36419             var cs = node.attributes.children;
36420             for(var i = 0, len = cs.length; i < len; i++){
36421                 node.appendChild(this.createNode(cs[i]));
36422             }
36423             if(typeof callback == "function"){
36424                 callback();
36425             }
36426         }else if(this.dataUrl){
36427             this.requestData(node, callback);
36428         }
36429     },
36430
36431     getParams: function(node){
36432         var buf = [], bp = this.baseParams;
36433         for(var key in bp){
36434             if(typeof bp[key] != "function"){
36435                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36436             }
36437         }
36438         var n = this.queryParam === false ? 'node' : this.queryParam;
36439         buf.push(n + "=", encodeURIComponent(node.id));
36440         return buf.join("");
36441     },
36442
36443     requestData : function(node, callback){
36444         if(this.fireEvent("beforeload", this, node, callback) !== false){
36445             this.transId = Roo.Ajax.request({
36446                 method:this.requestMethod,
36447                 url: this.dataUrl||this.url,
36448                 success: this.handleResponse,
36449                 failure: this.handleFailure,
36450                 scope: this,
36451                 argument: {callback: callback, node: node},
36452                 params: this.getParams(node)
36453             });
36454         }else{
36455             // if the load is cancelled, make sure we notify
36456             // the node that we are done
36457             if(typeof callback == "function"){
36458                 callback();
36459             }
36460         }
36461     },
36462
36463     isLoading : function(){
36464         return this.transId ? true : false;
36465     },
36466
36467     abort : function(){
36468         if(this.isLoading()){
36469             Roo.Ajax.abort(this.transId);
36470         }
36471     },
36472
36473     // private
36474     createNode : function(attr)
36475     {
36476         // apply baseAttrs, nice idea Corey!
36477         if(this.baseAttrs){
36478             Roo.applyIf(attr, this.baseAttrs);
36479         }
36480         if(this.applyLoader !== false){
36481             attr.loader = this;
36482         }
36483         // uiProvider = depreciated..
36484         
36485         if(typeof(attr.uiProvider) == 'string'){
36486            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36487                 /**  eval:var:attr */ eval(attr.uiProvider);
36488         }
36489         if(typeof(this.uiProviders['default']) != 'undefined') {
36490             attr.uiProvider = this.uiProviders['default'];
36491         }
36492         
36493         this.fireEvent('create', this, attr);
36494         
36495         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36496         return(attr.leaf ?
36497                         new Roo.tree.TreeNode(attr) :
36498                         new Roo.tree.AsyncTreeNode(attr));
36499     },
36500
36501     processResponse : function(response, node, callback)
36502     {
36503         var json = response.responseText;
36504         try {
36505             
36506             var o = Roo.decode(json);
36507             
36508             if (this.root === false && typeof(o.success) != undefined) {
36509                 this.root = 'data'; // the default behaviour for list like data..
36510                 }
36511                 
36512             if (this.root !== false &&  !o.success) {
36513                 // it's a failure condition.
36514                 var a = response.argument;
36515                 this.fireEvent("loadexception", this, a.node, response);
36516                 Roo.log("Load failed - should have a handler really");
36517                 return;
36518             }
36519             
36520             
36521             
36522             if (this.root !== false) {
36523                  o = o[this.root];
36524             }
36525             
36526             for(var i = 0, len = o.length; i < len; i++){
36527                 var n = this.createNode(o[i]);
36528                 if(n){
36529                     node.appendChild(n);
36530                 }
36531             }
36532             if(typeof callback == "function"){
36533                 callback(this, node);
36534             }
36535         }catch(e){
36536             this.handleFailure(response);
36537         }
36538     },
36539
36540     handleResponse : function(response){
36541         this.transId = false;
36542         var a = response.argument;
36543         this.processResponse(response, a.node, a.callback);
36544         this.fireEvent("load", this, a.node, response);
36545     },
36546
36547     handleFailure : function(response)
36548     {
36549         // should handle failure better..
36550         this.transId = false;
36551         var a = response.argument;
36552         this.fireEvent("loadexception", this, a.node, response);
36553         if(typeof a.callback == "function"){
36554             a.callback(this, a.node);
36555         }
36556     }
36557 });/*
36558  * Based on:
36559  * Ext JS Library 1.1.1
36560  * Copyright(c) 2006-2007, Ext JS, LLC.
36561  *
36562  * Originally Released Under LGPL - original licence link has changed is not relivant.
36563  *
36564  * Fork - LGPL
36565  * <script type="text/javascript">
36566  */
36567
36568 /**
36569 * @class Roo.tree.TreeFilter
36570 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36571 * @param {TreePanel} tree
36572 * @param {Object} config (optional)
36573  */
36574 Roo.tree.TreeFilter = function(tree, config){
36575     this.tree = tree;
36576     this.filtered = {};
36577     Roo.apply(this, config);
36578 };
36579
36580 Roo.tree.TreeFilter.prototype = {
36581     clearBlank:false,
36582     reverse:false,
36583     autoClear:false,
36584     remove:false,
36585
36586      /**
36587      * Filter the data by a specific attribute.
36588      * @param {String/RegExp} value Either string that the attribute value
36589      * should start with or a RegExp to test against the attribute
36590      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36591      * @param {TreeNode} startNode (optional) The node to start the filter at.
36592      */
36593     filter : function(value, attr, startNode){
36594         attr = attr || "text";
36595         var f;
36596         if(typeof value == "string"){
36597             var vlen = value.length;
36598             // auto clear empty filter
36599             if(vlen == 0 && this.clearBlank){
36600                 this.clear();
36601                 return;
36602             }
36603             value = value.toLowerCase();
36604             f = function(n){
36605                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36606             };
36607         }else if(value.exec){ // regex?
36608             f = function(n){
36609                 return value.test(n.attributes[attr]);
36610             };
36611         }else{
36612             throw 'Illegal filter type, must be string or regex';
36613         }
36614         this.filterBy(f, null, startNode);
36615         },
36616
36617     /**
36618      * Filter by a function. The passed function will be called with each
36619      * node in the tree (or from the startNode). If the function returns true, the node is kept
36620      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36621      * @param {Function} fn The filter function
36622      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36623      */
36624     filterBy : function(fn, scope, startNode){
36625         startNode = startNode || this.tree.root;
36626         if(this.autoClear){
36627             this.clear();
36628         }
36629         var af = this.filtered, rv = this.reverse;
36630         var f = function(n){
36631             if(n == startNode){
36632                 return true;
36633             }
36634             if(af[n.id]){
36635                 return false;
36636             }
36637             var m = fn.call(scope || n, n);
36638             if(!m || rv){
36639                 af[n.id] = n;
36640                 n.ui.hide();
36641                 return false;
36642             }
36643             return true;
36644         };
36645         startNode.cascade(f);
36646         if(this.remove){
36647            for(var id in af){
36648                if(typeof id != "function"){
36649                    var n = af[id];
36650                    if(n && n.parentNode){
36651                        n.parentNode.removeChild(n);
36652                    }
36653                }
36654            }
36655         }
36656     },
36657
36658     /**
36659      * Clears the current filter. Note: with the "remove" option
36660      * set a filter cannot be cleared.
36661      */
36662     clear : function(){
36663         var t = this.tree;
36664         var af = this.filtered;
36665         for(var id in af){
36666             if(typeof id != "function"){
36667                 var n = af[id];
36668                 if(n){
36669                     n.ui.show();
36670                 }
36671             }
36672         }
36673         this.filtered = {};
36674     }
36675 };
36676 /*
36677  * Based on:
36678  * Ext JS Library 1.1.1
36679  * Copyright(c) 2006-2007, Ext JS, LLC.
36680  *
36681  * Originally Released Under LGPL - original licence link has changed is not relivant.
36682  *
36683  * Fork - LGPL
36684  * <script type="text/javascript">
36685  */
36686  
36687
36688 /**
36689  * @class Roo.tree.TreeSorter
36690  * Provides sorting of nodes in a TreePanel
36691  * 
36692  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36693  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36694  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36695  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36696  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36697  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36698  * @constructor
36699  * @param {TreePanel} tree
36700  * @param {Object} config
36701  */
36702 Roo.tree.TreeSorter = function(tree, config){
36703     Roo.apply(this, config);
36704     tree.on("beforechildrenrendered", this.doSort, this);
36705     tree.on("append", this.updateSort, this);
36706     tree.on("insert", this.updateSort, this);
36707     
36708     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36709     var p = this.property || "text";
36710     var sortType = this.sortType;
36711     var fs = this.folderSort;
36712     var cs = this.caseSensitive === true;
36713     var leafAttr = this.leafAttr || 'leaf';
36714
36715     this.sortFn = function(n1, n2){
36716         if(fs){
36717             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36718                 return 1;
36719             }
36720             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36721                 return -1;
36722             }
36723         }
36724         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36725         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36726         if(v1 < v2){
36727                         return dsc ? +1 : -1;
36728                 }else if(v1 > v2){
36729                         return dsc ? -1 : +1;
36730         }else{
36731                 return 0;
36732         }
36733     };
36734 };
36735
36736 Roo.tree.TreeSorter.prototype = {
36737     doSort : function(node){
36738         node.sort(this.sortFn);
36739     },
36740     
36741     compareNodes : function(n1, n2){
36742         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36743     },
36744     
36745     updateSort : function(tree, node){
36746         if(node.childrenRendered){
36747             this.doSort.defer(1, this, [node]);
36748         }
36749     }
36750 };/*
36751  * Based on:
36752  * Ext JS Library 1.1.1
36753  * Copyright(c) 2006-2007, Ext JS, LLC.
36754  *
36755  * Originally Released Under LGPL - original licence link has changed is not relivant.
36756  *
36757  * Fork - LGPL
36758  * <script type="text/javascript">
36759  */
36760
36761 if(Roo.dd.DropZone){
36762     
36763 Roo.tree.TreeDropZone = function(tree, config){
36764     this.allowParentInsert = false;
36765     this.allowContainerDrop = false;
36766     this.appendOnly = false;
36767     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36768     this.tree = tree;
36769     this.lastInsertClass = "x-tree-no-status";
36770     this.dragOverData = {};
36771 };
36772
36773 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36774     ddGroup : "TreeDD",
36775     scroll:  true,
36776     
36777     expandDelay : 1000,
36778     
36779     expandNode : function(node){
36780         if(node.hasChildNodes() && !node.isExpanded()){
36781             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36782         }
36783     },
36784     
36785     queueExpand : function(node){
36786         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36787     },
36788     
36789     cancelExpand : function(){
36790         if(this.expandProcId){
36791             clearTimeout(this.expandProcId);
36792             this.expandProcId = false;
36793         }
36794     },
36795     
36796     isValidDropPoint : function(n, pt, dd, e, data){
36797         if(!n || !data){ return false; }
36798         var targetNode = n.node;
36799         var dropNode = data.node;
36800         // default drop rules
36801         if(!(targetNode && targetNode.isTarget && pt)){
36802             return false;
36803         }
36804         if(pt == "append" && targetNode.allowChildren === false){
36805             return false;
36806         }
36807         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36808             return false;
36809         }
36810         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36811             return false;
36812         }
36813         // reuse the object
36814         var overEvent = this.dragOverData;
36815         overEvent.tree = this.tree;
36816         overEvent.target = targetNode;
36817         overEvent.data = data;
36818         overEvent.point = pt;
36819         overEvent.source = dd;
36820         overEvent.rawEvent = e;
36821         overEvent.dropNode = dropNode;
36822         overEvent.cancel = false;  
36823         var result = this.tree.fireEvent("nodedragover", overEvent);
36824         return overEvent.cancel === false && result !== false;
36825     },
36826     
36827     getDropPoint : function(e, n, dd)
36828     {
36829         var tn = n.node;
36830         if(tn.isRoot){
36831             return tn.allowChildren !== false ? "append" : false; // always append for root
36832         }
36833         var dragEl = n.ddel;
36834         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36835         var y = Roo.lib.Event.getPageY(e);
36836         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36837         
36838         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36839         var noAppend = tn.allowChildren === false;
36840         if(this.appendOnly || tn.parentNode.allowChildren === false){
36841             return noAppend ? false : "append";
36842         }
36843         var noBelow = false;
36844         if(!this.allowParentInsert){
36845             noBelow = tn.hasChildNodes() && tn.isExpanded();
36846         }
36847         var q = (b - t) / (noAppend ? 2 : 3);
36848         if(y >= t && y < (t + q)){
36849             return "above";
36850         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36851             return "below";
36852         }else{
36853             return "append";
36854         }
36855     },
36856     
36857     onNodeEnter : function(n, dd, e, data)
36858     {
36859         this.cancelExpand();
36860     },
36861     
36862     onNodeOver : function(n, dd, e, data)
36863     {
36864        
36865         var pt = this.getDropPoint(e, n, dd);
36866         var node = n.node;
36867         
36868         // auto node expand check
36869         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36870             this.queueExpand(node);
36871         }else if(pt != "append"){
36872             this.cancelExpand();
36873         }
36874         
36875         // set the insert point style on the target node
36876         var returnCls = this.dropNotAllowed;
36877         if(this.isValidDropPoint(n, pt, dd, e, data)){
36878            if(pt){
36879                var el = n.ddel;
36880                var cls;
36881                if(pt == "above"){
36882                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36883                    cls = "x-tree-drag-insert-above";
36884                }else if(pt == "below"){
36885                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36886                    cls = "x-tree-drag-insert-below";
36887                }else{
36888                    returnCls = "x-tree-drop-ok-append";
36889                    cls = "x-tree-drag-append";
36890                }
36891                if(this.lastInsertClass != cls){
36892                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36893                    this.lastInsertClass = cls;
36894                }
36895            }
36896        }
36897        return returnCls;
36898     },
36899     
36900     onNodeOut : function(n, dd, e, data){
36901         
36902         this.cancelExpand();
36903         this.removeDropIndicators(n);
36904     },
36905     
36906     onNodeDrop : function(n, dd, e, data){
36907         var point = this.getDropPoint(e, n, dd);
36908         var targetNode = n.node;
36909         targetNode.ui.startDrop();
36910         if(!this.isValidDropPoint(n, point, dd, e, data)){
36911             targetNode.ui.endDrop();
36912             return false;
36913         }
36914         // first try to find the drop node
36915         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36916         var dropEvent = {
36917             tree : this.tree,
36918             target: targetNode,
36919             data: data,
36920             point: point,
36921             source: dd,
36922             rawEvent: e,
36923             dropNode: dropNode,
36924             cancel: !dropNode   
36925         };
36926         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36927         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36928             targetNode.ui.endDrop();
36929             return false;
36930         }
36931         // allow target changing
36932         targetNode = dropEvent.target;
36933         if(point == "append" && !targetNode.isExpanded()){
36934             targetNode.expand(false, null, function(){
36935                 this.completeDrop(dropEvent);
36936             }.createDelegate(this));
36937         }else{
36938             this.completeDrop(dropEvent);
36939         }
36940         return true;
36941     },
36942     
36943     completeDrop : function(de){
36944         var ns = de.dropNode, p = de.point, t = de.target;
36945         if(!(ns instanceof Array)){
36946             ns = [ns];
36947         }
36948         var n;
36949         for(var i = 0, len = ns.length; i < len; i++){
36950             n = ns[i];
36951             if(p == "above"){
36952                 t.parentNode.insertBefore(n, t);
36953             }else if(p == "below"){
36954                 t.parentNode.insertBefore(n, t.nextSibling);
36955             }else{
36956                 t.appendChild(n);
36957             }
36958         }
36959         n.ui.focus();
36960         if(this.tree.hlDrop){
36961             n.ui.highlight();
36962         }
36963         t.ui.endDrop();
36964         this.tree.fireEvent("nodedrop", de);
36965     },
36966     
36967     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36968         if(this.tree.hlDrop){
36969             dropNode.ui.focus();
36970             dropNode.ui.highlight();
36971         }
36972         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36973     },
36974     
36975     getTree : function(){
36976         return this.tree;
36977     },
36978     
36979     removeDropIndicators : function(n){
36980         if(n && n.ddel){
36981             var el = n.ddel;
36982             Roo.fly(el).removeClass([
36983                     "x-tree-drag-insert-above",
36984                     "x-tree-drag-insert-below",
36985                     "x-tree-drag-append"]);
36986             this.lastInsertClass = "_noclass";
36987         }
36988     },
36989     
36990     beforeDragDrop : function(target, e, id){
36991         this.cancelExpand();
36992         return true;
36993     },
36994     
36995     afterRepair : function(data){
36996         if(data && Roo.enableFx){
36997             data.node.ui.highlight();
36998         }
36999         this.hideProxy();
37000     } 
37001     
37002 });
37003
37004 }
37005 /*
37006  * Based on:
37007  * Ext JS Library 1.1.1
37008  * Copyright(c) 2006-2007, Ext JS, LLC.
37009  *
37010  * Originally Released Under LGPL - original licence link has changed is not relivant.
37011  *
37012  * Fork - LGPL
37013  * <script type="text/javascript">
37014  */
37015  
37016
37017 if(Roo.dd.DragZone){
37018 Roo.tree.TreeDragZone = function(tree, config){
37019     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37020     this.tree = tree;
37021 };
37022
37023 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37024     ddGroup : "TreeDD",
37025    
37026     onBeforeDrag : function(data, e){
37027         var n = data.node;
37028         return n && n.draggable && !n.disabled;
37029     },
37030      
37031     
37032     onInitDrag : function(e){
37033         var data = this.dragData;
37034         this.tree.getSelectionModel().select(data.node);
37035         this.proxy.update("");
37036         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37037         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37038     },
37039     
37040     getRepairXY : function(e, data){
37041         return data.node.ui.getDDRepairXY();
37042     },
37043     
37044     onEndDrag : function(data, e){
37045         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37046         
37047         
37048     },
37049     
37050     onValidDrop : function(dd, e, id){
37051         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37052         this.hideProxy();
37053     },
37054     
37055     beforeInvalidDrop : function(e, id){
37056         // this scrolls the original position back into view
37057         var sm = this.tree.getSelectionModel();
37058         sm.clearSelections();
37059         sm.select(this.dragData.node);
37060     }
37061 });
37062 }/*
37063  * Based on:
37064  * Ext JS Library 1.1.1
37065  * Copyright(c) 2006-2007, Ext JS, LLC.
37066  *
37067  * Originally Released Under LGPL - original licence link has changed is not relivant.
37068  *
37069  * Fork - LGPL
37070  * <script type="text/javascript">
37071  */
37072 /**
37073  * @class Roo.tree.TreeEditor
37074  * @extends Roo.Editor
37075  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37076  * as the editor field.
37077  * @constructor
37078  * @param {Object} config (used to be the tree panel.)
37079  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37080  * 
37081  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37082  * @cfg {Roo.form.TextField|Object} field The field configuration
37083  *
37084  * 
37085  */
37086 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37087     var tree = config;
37088     var field;
37089     if (oldconfig) { // old style..
37090         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37091     } else {
37092         // new style..
37093         tree = config.tree;
37094         config.field = config.field  || {};
37095         config.field.xtype = 'TextField';
37096         field = Roo.factory(config.field, Roo.form);
37097     }
37098     config = config || {};
37099     
37100     
37101     this.addEvents({
37102         /**
37103          * @event beforenodeedit
37104          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37105          * false from the handler of this event.
37106          * @param {Editor} this
37107          * @param {Roo.tree.Node} node 
37108          */
37109         "beforenodeedit" : true
37110     });
37111     
37112     //Roo.log(config);
37113     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37114
37115     this.tree = tree;
37116
37117     tree.on('beforeclick', this.beforeNodeClick, this);
37118     tree.getTreeEl().on('mousedown', this.hide, this);
37119     this.on('complete', this.updateNode, this);
37120     this.on('beforestartedit', this.fitToTree, this);
37121     this.on('startedit', this.bindScroll, this, {delay:10});
37122     this.on('specialkey', this.onSpecialKey, this);
37123 };
37124
37125 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37126     /**
37127      * @cfg {String} alignment
37128      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37129      */
37130     alignment: "l-l",
37131     // inherit
37132     autoSize: false,
37133     /**
37134      * @cfg {Boolean} hideEl
37135      * True to hide the bound element while the editor is displayed (defaults to false)
37136      */
37137     hideEl : false,
37138     /**
37139      * @cfg {String} cls
37140      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37141      */
37142     cls: "x-small-editor x-tree-editor",
37143     /**
37144      * @cfg {Boolean} shim
37145      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37146      */
37147     shim:false,
37148     // inherit
37149     shadow:"frame",
37150     /**
37151      * @cfg {Number} maxWidth
37152      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37153      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37154      * scroll and client offsets into account prior to each edit.
37155      */
37156     maxWidth: 250,
37157
37158     editDelay : 350,
37159
37160     // private
37161     fitToTree : function(ed, el){
37162         var td = this.tree.getTreeEl().dom, nd = el.dom;
37163         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37164             td.scrollLeft = nd.offsetLeft;
37165         }
37166         var w = Math.min(
37167                 this.maxWidth,
37168                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37169         this.setSize(w, '');
37170         
37171         return this.fireEvent('beforenodeedit', this, this.editNode);
37172         
37173     },
37174
37175     // private
37176     triggerEdit : function(node){
37177         this.completeEdit();
37178         this.editNode = node;
37179         this.startEdit(node.ui.textNode, node.text);
37180     },
37181
37182     // private
37183     bindScroll : function(){
37184         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37185     },
37186
37187     // private
37188     beforeNodeClick : function(node, e){
37189         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37190         this.lastClick = new Date();
37191         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37192             e.stopEvent();
37193             this.triggerEdit(node);
37194             return false;
37195         }
37196         return true;
37197     },
37198
37199     // private
37200     updateNode : function(ed, value){
37201         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37202         this.editNode.setText(value);
37203     },
37204
37205     // private
37206     onHide : function(){
37207         Roo.tree.TreeEditor.superclass.onHide.call(this);
37208         if(this.editNode){
37209             this.editNode.ui.focus();
37210         }
37211     },
37212
37213     // private
37214     onSpecialKey : function(field, e){
37215         var k = e.getKey();
37216         if(k == e.ESC){
37217             e.stopEvent();
37218             this.cancelEdit();
37219         }else if(k == e.ENTER && !e.hasModifier()){
37220             e.stopEvent();
37221             this.completeEdit();
37222         }
37223     }
37224 });//<Script type="text/javascript">
37225 /*
37226  * Based on:
37227  * Ext JS Library 1.1.1
37228  * Copyright(c) 2006-2007, Ext JS, LLC.
37229  *
37230  * Originally Released Under LGPL - original licence link has changed is not relivant.
37231  *
37232  * Fork - LGPL
37233  * <script type="text/javascript">
37234  */
37235  
37236 /**
37237  * Not documented??? - probably should be...
37238  */
37239
37240 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37241     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37242     
37243     renderElements : function(n, a, targetNode, bulkRender){
37244         //consel.log("renderElements?");
37245         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37246
37247         var t = n.getOwnerTree();
37248         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37249         
37250         var cols = t.columns;
37251         var bw = t.borderWidth;
37252         var c = cols[0];
37253         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37254          var cb = typeof a.checked == "boolean";
37255         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37256         var colcls = 'x-t-' + tid + '-c0';
37257         var buf = [
37258             '<li class="x-tree-node">',
37259             
37260                 
37261                 '<div class="x-tree-node-el ', a.cls,'">',
37262                     // extran...
37263                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37264                 
37265                 
37266                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37267                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37268                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37269                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37270                            (a.iconCls ? ' '+a.iconCls : ''),
37271                            '" unselectable="on" />',
37272                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37273                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37274                              
37275                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37276                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37277                             '<span unselectable="on" qtip="' + tx + '">',
37278                              tx,
37279                              '</span></a>' ,
37280                     '</div>',
37281                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37282                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37283                  ];
37284         for(var i = 1, len = cols.length; i < len; i++){
37285             c = cols[i];
37286             colcls = 'x-t-' + tid + '-c' +i;
37287             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37288             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37289                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37290                       "</div>");
37291          }
37292          
37293          buf.push(
37294             '</a>',
37295             '<div class="x-clear"></div></div>',
37296             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37297             "</li>");
37298         
37299         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37300             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37301                                 n.nextSibling.ui.getEl(), buf.join(""));
37302         }else{
37303             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37304         }
37305         var el = this.wrap.firstChild;
37306         this.elRow = el;
37307         this.elNode = el.firstChild;
37308         this.ranchor = el.childNodes[1];
37309         this.ctNode = this.wrap.childNodes[1];
37310         var cs = el.firstChild.childNodes;
37311         this.indentNode = cs[0];
37312         this.ecNode = cs[1];
37313         this.iconNode = cs[2];
37314         var index = 3;
37315         if(cb){
37316             this.checkbox = cs[3];
37317             index++;
37318         }
37319         this.anchor = cs[index];
37320         
37321         this.textNode = cs[index].firstChild;
37322         
37323         //el.on("click", this.onClick, this);
37324         //el.on("dblclick", this.onDblClick, this);
37325         
37326         
37327        // console.log(this);
37328     },
37329     initEvents : function(){
37330         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37331         
37332             
37333         var a = this.ranchor;
37334
37335         var el = Roo.get(a);
37336
37337         if(Roo.isOpera){ // opera render bug ignores the CSS
37338             el.setStyle("text-decoration", "none");
37339         }
37340
37341         el.on("click", this.onClick, this);
37342         el.on("dblclick", this.onDblClick, this);
37343         el.on("contextmenu", this.onContextMenu, this);
37344         
37345     },
37346     
37347     /*onSelectedChange : function(state){
37348         if(state){
37349             this.focus();
37350             this.addClass("x-tree-selected");
37351         }else{
37352             //this.blur();
37353             this.removeClass("x-tree-selected");
37354         }
37355     },*/
37356     addClass : function(cls){
37357         if(this.elRow){
37358             Roo.fly(this.elRow).addClass(cls);
37359         }
37360         
37361     },
37362     
37363     
37364     removeClass : function(cls){
37365         if(this.elRow){
37366             Roo.fly(this.elRow).removeClass(cls);
37367         }
37368     }
37369
37370     
37371     
37372 });//<Script type="text/javascript">
37373
37374 /*
37375  * Based on:
37376  * Ext JS Library 1.1.1
37377  * Copyright(c) 2006-2007, Ext JS, LLC.
37378  *
37379  * Originally Released Under LGPL - original licence link has changed is not relivant.
37380  *
37381  * Fork - LGPL
37382  * <script type="text/javascript">
37383  */
37384  
37385
37386 /**
37387  * @class Roo.tree.ColumnTree
37388  * @extends Roo.data.TreePanel
37389  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37390  * @cfg {int} borderWidth  compined right/left border allowance
37391  * @constructor
37392  * @param {String/HTMLElement/Element} el The container element
37393  * @param {Object} config
37394  */
37395 Roo.tree.ColumnTree =  function(el, config)
37396 {
37397    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37398    this.addEvents({
37399         /**
37400         * @event resize
37401         * Fire this event on a container when it resizes
37402         * @param {int} w Width
37403         * @param {int} h Height
37404         */
37405        "resize" : true
37406     });
37407     this.on('resize', this.onResize, this);
37408 };
37409
37410 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37411     //lines:false,
37412     
37413     
37414     borderWidth: Roo.isBorderBox ? 0 : 2, 
37415     headEls : false,
37416     
37417     render : function(){
37418         // add the header.....
37419        
37420         Roo.tree.ColumnTree.superclass.render.apply(this);
37421         
37422         this.el.addClass('x-column-tree');
37423         
37424         this.headers = this.el.createChild(
37425             {cls:'x-tree-headers'},this.innerCt.dom);
37426    
37427         var cols = this.columns, c;
37428         var totalWidth = 0;
37429         this.headEls = [];
37430         var  len = cols.length;
37431         for(var i = 0; i < len; i++){
37432              c = cols[i];
37433              totalWidth += c.width;
37434             this.headEls.push(this.headers.createChild({
37435                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37436                  cn: {
37437                      cls:'x-tree-hd-text',
37438                      html: c.header
37439                  },
37440                  style:'width:'+(c.width-this.borderWidth)+'px;'
37441              }));
37442         }
37443         this.headers.createChild({cls:'x-clear'});
37444         // prevent floats from wrapping when clipped
37445         this.headers.setWidth(totalWidth);
37446         //this.innerCt.setWidth(totalWidth);
37447         this.innerCt.setStyle({ overflow: 'auto' });
37448         this.onResize(this.width, this.height);
37449              
37450         
37451     },
37452     onResize : function(w,h)
37453     {
37454         this.height = h;
37455         this.width = w;
37456         // resize cols..
37457         this.innerCt.setWidth(this.width);
37458         this.innerCt.setHeight(this.height-20);
37459         
37460         // headers...
37461         var cols = this.columns, c;
37462         var totalWidth = 0;
37463         var expEl = false;
37464         var len = cols.length;
37465         for(var i = 0; i < len; i++){
37466             c = cols[i];
37467             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37468                 // it's the expander..
37469                 expEl  = this.headEls[i];
37470                 continue;
37471             }
37472             totalWidth += c.width;
37473             
37474         }
37475         if (expEl) {
37476             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37477         }
37478         this.headers.setWidth(w-20);
37479
37480         
37481         
37482         
37483     }
37484 });
37485 /*
37486  * Based on:
37487  * Ext JS Library 1.1.1
37488  * Copyright(c) 2006-2007, Ext JS, LLC.
37489  *
37490  * Originally Released Under LGPL - original licence link has changed is not relivant.
37491  *
37492  * Fork - LGPL
37493  * <script type="text/javascript">
37494  */
37495  
37496 /**
37497  * @class Roo.menu.Menu
37498  * @extends Roo.util.Observable
37499  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37500  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37501  * @constructor
37502  * Creates a new Menu
37503  * @param {Object} config Configuration options
37504  */
37505 Roo.menu.Menu = function(config){
37506     
37507     Roo.menu.Menu.superclass.constructor.call(this, config);
37508     
37509     this.id = this.id || Roo.id();
37510     this.addEvents({
37511         /**
37512          * @event beforeshow
37513          * Fires before this menu is displayed
37514          * @param {Roo.menu.Menu} this
37515          */
37516         beforeshow : true,
37517         /**
37518          * @event beforehide
37519          * Fires before this menu is hidden
37520          * @param {Roo.menu.Menu} this
37521          */
37522         beforehide : true,
37523         /**
37524          * @event show
37525          * Fires after this menu is displayed
37526          * @param {Roo.menu.Menu} this
37527          */
37528         show : true,
37529         /**
37530          * @event hide
37531          * Fires after this menu is hidden
37532          * @param {Roo.menu.Menu} this
37533          */
37534         hide : true,
37535         /**
37536          * @event click
37537          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37538          * @param {Roo.menu.Menu} this
37539          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37540          * @param {Roo.EventObject} e
37541          */
37542         click : true,
37543         /**
37544          * @event mouseover
37545          * Fires when the mouse is hovering over this menu
37546          * @param {Roo.menu.Menu} this
37547          * @param {Roo.EventObject} e
37548          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37549          */
37550         mouseover : true,
37551         /**
37552          * @event mouseout
37553          * Fires when the mouse exits this menu
37554          * @param {Roo.menu.Menu} this
37555          * @param {Roo.EventObject} e
37556          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37557          */
37558         mouseout : true,
37559         /**
37560          * @event itemclick
37561          * Fires when a menu item contained in this menu is clicked
37562          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37563          * @param {Roo.EventObject} e
37564          */
37565         itemclick: true
37566     });
37567     if (this.registerMenu) {
37568         Roo.menu.MenuMgr.register(this);
37569     }
37570     
37571     var mis = this.items;
37572     this.items = new Roo.util.MixedCollection();
37573     if(mis){
37574         this.add.apply(this, mis);
37575     }
37576 };
37577
37578 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37579     /**
37580      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37581      */
37582     minWidth : 120,
37583     /**
37584      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37585      * for bottom-right shadow (defaults to "sides")
37586      */
37587     shadow : "sides",
37588     /**
37589      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37590      * this menu (defaults to "tl-tr?")
37591      */
37592     subMenuAlign : "tl-tr?",
37593     /**
37594      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37595      * relative to its element of origin (defaults to "tl-bl?")
37596      */
37597     defaultAlign : "tl-bl?",
37598     /**
37599      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37600      */
37601     allowOtherMenus : false,
37602     /**
37603      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37604      */
37605     registerMenu : true,
37606
37607     hidden:true,
37608
37609     // private
37610     render : function(){
37611         if(this.el){
37612             return;
37613         }
37614         var el = this.el = new Roo.Layer({
37615             cls: "x-menu",
37616             shadow:this.shadow,
37617             constrain: false,
37618             parentEl: this.parentEl || document.body,
37619             zindex:15000
37620         });
37621
37622         this.keyNav = new Roo.menu.MenuNav(this);
37623
37624         if(this.plain){
37625             el.addClass("x-menu-plain");
37626         }
37627         if(this.cls){
37628             el.addClass(this.cls);
37629         }
37630         // generic focus element
37631         this.focusEl = el.createChild({
37632             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37633         });
37634         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37635         //disabling touch- as it's causing issues ..
37636         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37637         ul.on('click'   , this.onClick, this);
37638         
37639         
37640         ul.on("mouseover", this.onMouseOver, this);
37641         ul.on("mouseout", this.onMouseOut, this);
37642         this.items.each(function(item){
37643             if (item.hidden) {
37644                 return;
37645             }
37646             
37647             var li = document.createElement("li");
37648             li.className = "x-menu-list-item";
37649             ul.dom.appendChild(li);
37650             item.render(li, this);
37651         }, this);
37652         this.ul = ul;
37653         this.autoWidth();
37654     },
37655
37656     // private
37657     autoWidth : function(){
37658         var el = this.el, ul = this.ul;
37659         if(!el){
37660             return;
37661         }
37662         var w = this.width;
37663         if(w){
37664             el.setWidth(w);
37665         }else if(Roo.isIE){
37666             el.setWidth(this.minWidth);
37667             var t = el.dom.offsetWidth; // force recalc
37668             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37669         }
37670     },
37671
37672     // private
37673     delayAutoWidth : function(){
37674         if(this.rendered){
37675             if(!this.awTask){
37676                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37677             }
37678             this.awTask.delay(20);
37679         }
37680     },
37681
37682     // private
37683     findTargetItem : function(e){
37684         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37685         if(t && t.menuItemId){
37686             return this.items.get(t.menuItemId);
37687         }
37688     },
37689
37690     // private
37691     onClick : function(e){
37692         Roo.log("menu.onClick");
37693         var t = this.findTargetItem(e);
37694         if(!t){
37695             return;
37696         }
37697         Roo.log(e);
37698         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37699             if(t == this.activeItem && t.shouldDeactivate(e)){
37700                 this.activeItem.deactivate();
37701                 delete this.activeItem;
37702                 return;
37703             }
37704             if(t.canActivate){
37705                 this.setActiveItem(t, true);
37706             }
37707             return;
37708             
37709             
37710         }
37711         
37712         t.onClick(e);
37713         this.fireEvent("click", this, t, e);
37714     },
37715
37716     // private
37717     setActiveItem : function(item, autoExpand){
37718         if(item != this.activeItem){
37719             if(this.activeItem){
37720                 this.activeItem.deactivate();
37721             }
37722             this.activeItem = item;
37723             item.activate(autoExpand);
37724         }else if(autoExpand){
37725             item.expandMenu();
37726         }
37727     },
37728
37729     // private
37730     tryActivate : function(start, step){
37731         var items = this.items;
37732         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37733             var item = items.get(i);
37734             if(!item.disabled && item.canActivate){
37735                 this.setActiveItem(item, false);
37736                 return item;
37737             }
37738         }
37739         return false;
37740     },
37741
37742     // private
37743     onMouseOver : function(e){
37744         var t;
37745         if(t = this.findTargetItem(e)){
37746             if(t.canActivate && !t.disabled){
37747                 this.setActiveItem(t, true);
37748             }
37749         }
37750         this.fireEvent("mouseover", this, e, t);
37751     },
37752
37753     // private
37754     onMouseOut : function(e){
37755         var t;
37756         if(t = this.findTargetItem(e)){
37757             if(t == this.activeItem && t.shouldDeactivate(e)){
37758                 this.activeItem.deactivate();
37759                 delete this.activeItem;
37760             }
37761         }
37762         this.fireEvent("mouseout", this, e, t);
37763     },
37764
37765     /**
37766      * Read-only.  Returns true if the menu is currently displayed, else false.
37767      * @type Boolean
37768      */
37769     isVisible : function(){
37770         return this.el && !this.hidden;
37771     },
37772
37773     /**
37774      * Displays this menu relative to another element
37775      * @param {String/HTMLElement/Roo.Element} element The element to align to
37776      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37777      * the element (defaults to this.defaultAlign)
37778      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37779      */
37780     show : function(el, pos, parentMenu){
37781         this.parentMenu = parentMenu;
37782         if(!this.el){
37783             this.render();
37784         }
37785         this.fireEvent("beforeshow", this);
37786         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37787     },
37788
37789     /**
37790      * Displays this menu at a specific xy position
37791      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37792      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37793      */
37794     showAt : function(xy, parentMenu, /* private: */_e){
37795         this.parentMenu = parentMenu;
37796         if(!this.el){
37797             this.render();
37798         }
37799         if(_e !== false){
37800             this.fireEvent("beforeshow", this);
37801             xy = this.el.adjustForConstraints(xy);
37802         }
37803         this.el.setXY(xy);
37804         this.el.show();
37805         this.hidden = false;
37806         this.focus();
37807         this.fireEvent("show", this);
37808     },
37809
37810     focus : function(){
37811         if(!this.hidden){
37812             this.doFocus.defer(50, this);
37813         }
37814     },
37815
37816     doFocus : function(){
37817         if(!this.hidden){
37818             this.focusEl.focus();
37819         }
37820     },
37821
37822     /**
37823      * Hides this menu and optionally all parent menus
37824      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37825      */
37826     hide : function(deep){
37827         if(this.el && this.isVisible()){
37828             this.fireEvent("beforehide", this);
37829             if(this.activeItem){
37830                 this.activeItem.deactivate();
37831                 this.activeItem = null;
37832             }
37833             this.el.hide();
37834             this.hidden = true;
37835             this.fireEvent("hide", this);
37836         }
37837         if(deep === true && this.parentMenu){
37838             this.parentMenu.hide(true);
37839         }
37840     },
37841
37842     /**
37843      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37844      * Any of the following are valid:
37845      * <ul>
37846      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37847      * <li>An HTMLElement object which will be converted to a menu item</li>
37848      * <li>A menu item config object that will be created as a new menu item</li>
37849      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37850      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37851      * </ul>
37852      * Usage:
37853      * <pre><code>
37854 // Create the menu
37855 var menu = new Roo.menu.Menu();
37856
37857 // Create a menu item to add by reference
37858 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37859
37860 // Add a bunch of items at once using different methods.
37861 // Only the last item added will be returned.
37862 var item = menu.add(
37863     menuItem,                // add existing item by ref
37864     'Dynamic Item',          // new TextItem
37865     '-',                     // new separator
37866     { text: 'Config Item' }  // new item by config
37867 );
37868 </code></pre>
37869      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37870      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37871      */
37872     add : function(){
37873         var a = arguments, l = a.length, item;
37874         for(var i = 0; i < l; i++){
37875             var el = a[i];
37876             if ((typeof(el) == "object") && el.xtype && el.xns) {
37877                 el = Roo.factory(el, Roo.menu);
37878             }
37879             
37880             if(el.render){ // some kind of Item
37881                 item = this.addItem(el);
37882             }else if(typeof el == "string"){ // string
37883                 if(el == "separator" || el == "-"){
37884                     item = this.addSeparator();
37885                 }else{
37886                     item = this.addText(el);
37887                 }
37888             }else if(el.tagName || el.el){ // element
37889                 item = this.addElement(el);
37890             }else if(typeof el == "object"){ // must be menu item config?
37891                 item = this.addMenuItem(el);
37892             }
37893         }
37894         return item;
37895     },
37896
37897     /**
37898      * Returns this menu's underlying {@link Roo.Element} object
37899      * @return {Roo.Element} The element
37900      */
37901     getEl : function(){
37902         if(!this.el){
37903             this.render();
37904         }
37905         return this.el;
37906     },
37907
37908     /**
37909      * Adds a separator bar to the menu
37910      * @return {Roo.menu.Item} The menu item that was added
37911      */
37912     addSeparator : function(){
37913         return this.addItem(new Roo.menu.Separator());
37914     },
37915
37916     /**
37917      * Adds an {@link Roo.Element} object to the menu
37918      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37919      * @return {Roo.menu.Item} The menu item that was added
37920      */
37921     addElement : function(el){
37922         return this.addItem(new Roo.menu.BaseItem(el));
37923     },
37924
37925     /**
37926      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37927      * @param {Roo.menu.Item} item The menu item to add
37928      * @return {Roo.menu.Item} The menu item that was added
37929      */
37930     addItem : function(item){
37931         this.items.add(item);
37932         if(this.ul){
37933             var li = document.createElement("li");
37934             li.className = "x-menu-list-item";
37935             this.ul.dom.appendChild(li);
37936             item.render(li, this);
37937             this.delayAutoWidth();
37938         }
37939         return item;
37940     },
37941
37942     /**
37943      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37944      * @param {Object} config A MenuItem config object
37945      * @return {Roo.menu.Item} The menu item that was added
37946      */
37947     addMenuItem : function(config){
37948         if(!(config instanceof Roo.menu.Item)){
37949             if(typeof config.checked == "boolean"){ // must be check menu item config?
37950                 config = new Roo.menu.CheckItem(config);
37951             }else{
37952                 config = new Roo.menu.Item(config);
37953             }
37954         }
37955         return this.addItem(config);
37956     },
37957
37958     /**
37959      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37960      * @param {String} text The text to display in the menu item
37961      * @return {Roo.menu.Item} The menu item that was added
37962      */
37963     addText : function(text){
37964         return this.addItem(new Roo.menu.TextItem({ text : text }));
37965     },
37966
37967     /**
37968      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37969      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37970      * @param {Roo.menu.Item} item The menu item to add
37971      * @return {Roo.menu.Item} The menu item that was added
37972      */
37973     insert : function(index, item){
37974         this.items.insert(index, item);
37975         if(this.ul){
37976             var li = document.createElement("li");
37977             li.className = "x-menu-list-item";
37978             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37979             item.render(li, this);
37980             this.delayAutoWidth();
37981         }
37982         return item;
37983     },
37984
37985     /**
37986      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37987      * @param {Roo.menu.Item} item The menu item to remove
37988      */
37989     remove : function(item){
37990         this.items.removeKey(item.id);
37991         item.destroy();
37992     },
37993
37994     /**
37995      * Removes and destroys all items in the menu
37996      */
37997     removeAll : function(){
37998         var f;
37999         while(f = this.items.first()){
38000             this.remove(f);
38001         }
38002     }
38003 });
38004
38005 // MenuNav is a private utility class used internally by the Menu
38006 Roo.menu.MenuNav = function(menu){
38007     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38008     this.scope = this.menu = menu;
38009 };
38010
38011 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38012     doRelay : function(e, h){
38013         var k = e.getKey();
38014         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38015             this.menu.tryActivate(0, 1);
38016             return false;
38017         }
38018         return h.call(this.scope || this, e, this.menu);
38019     },
38020
38021     up : function(e, m){
38022         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38023             m.tryActivate(m.items.length-1, -1);
38024         }
38025     },
38026
38027     down : function(e, m){
38028         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38029             m.tryActivate(0, 1);
38030         }
38031     },
38032
38033     right : function(e, m){
38034         if(m.activeItem){
38035             m.activeItem.expandMenu(true);
38036         }
38037     },
38038
38039     left : function(e, m){
38040         m.hide();
38041         if(m.parentMenu && m.parentMenu.activeItem){
38042             m.parentMenu.activeItem.activate();
38043         }
38044     },
38045
38046     enter : function(e, m){
38047         if(m.activeItem){
38048             e.stopPropagation();
38049             m.activeItem.onClick(e);
38050             m.fireEvent("click", this, m.activeItem);
38051             return true;
38052         }
38053     }
38054 });/*
38055  * Based on:
38056  * Ext JS Library 1.1.1
38057  * Copyright(c) 2006-2007, Ext JS, LLC.
38058  *
38059  * Originally Released Under LGPL - original licence link has changed is not relivant.
38060  *
38061  * Fork - LGPL
38062  * <script type="text/javascript">
38063  */
38064  
38065 /**
38066  * @class Roo.menu.MenuMgr
38067  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38068  * @singleton
38069  */
38070 Roo.menu.MenuMgr = function(){
38071    var menus, active, groups = {}, attached = false, lastShow = new Date();
38072
38073    // private - called when first menu is created
38074    function init(){
38075        menus = {};
38076        active = new Roo.util.MixedCollection();
38077        Roo.get(document).addKeyListener(27, function(){
38078            if(active.length > 0){
38079                hideAll();
38080            }
38081        });
38082    }
38083
38084    // private
38085    function hideAll(){
38086        if(active && active.length > 0){
38087            var c = active.clone();
38088            c.each(function(m){
38089                m.hide();
38090            });
38091        }
38092    }
38093
38094    // private
38095    function onHide(m){
38096        active.remove(m);
38097        if(active.length < 1){
38098            Roo.get(document).un("mousedown", onMouseDown);
38099            attached = false;
38100        }
38101    }
38102
38103    // private
38104    function onShow(m){
38105        var last = active.last();
38106        lastShow = new Date();
38107        active.add(m);
38108        if(!attached){
38109            Roo.get(document).on("mousedown", onMouseDown);
38110            attached = true;
38111        }
38112        if(m.parentMenu){
38113           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38114           m.parentMenu.activeChild = m;
38115        }else if(last && last.isVisible()){
38116           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38117        }
38118    }
38119
38120    // private
38121    function onBeforeHide(m){
38122        if(m.activeChild){
38123            m.activeChild.hide();
38124        }
38125        if(m.autoHideTimer){
38126            clearTimeout(m.autoHideTimer);
38127            delete m.autoHideTimer;
38128        }
38129    }
38130
38131    // private
38132    function onBeforeShow(m){
38133        var pm = m.parentMenu;
38134        if(!pm && !m.allowOtherMenus){
38135            hideAll();
38136        }else if(pm && pm.activeChild && active != m){
38137            pm.activeChild.hide();
38138        }
38139    }
38140
38141    // private
38142    function onMouseDown(e){
38143        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38144            hideAll();
38145        }
38146    }
38147
38148    // private
38149    function onBeforeCheck(mi, state){
38150        if(state){
38151            var g = groups[mi.group];
38152            for(var i = 0, l = g.length; i < l; i++){
38153                if(g[i] != mi){
38154                    g[i].setChecked(false);
38155                }
38156            }
38157        }
38158    }
38159
38160    return {
38161
38162        /**
38163         * Hides all menus that are currently visible
38164         */
38165        hideAll : function(){
38166             hideAll();  
38167        },
38168
38169        // private
38170        register : function(menu){
38171            if(!menus){
38172                init();
38173            }
38174            menus[menu.id] = menu;
38175            menu.on("beforehide", onBeforeHide);
38176            menu.on("hide", onHide);
38177            menu.on("beforeshow", onBeforeShow);
38178            menu.on("show", onShow);
38179            var g = menu.group;
38180            if(g && menu.events["checkchange"]){
38181                if(!groups[g]){
38182                    groups[g] = [];
38183                }
38184                groups[g].push(menu);
38185                menu.on("checkchange", onCheck);
38186            }
38187        },
38188
38189         /**
38190          * Returns a {@link Roo.menu.Menu} object
38191          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38192          * be used to generate and return a new Menu instance.
38193          */
38194        get : function(menu){
38195            if(typeof menu == "string"){ // menu id
38196                return menus[menu];
38197            }else if(menu.events){  // menu instance
38198                return menu;
38199            }else if(typeof menu.length == 'number'){ // array of menu items?
38200                return new Roo.menu.Menu({items:menu});
38201            }else{ // otherwise, must be a config
38202                return new Roo.menu.Menu(menu);
38203            }
38204        },
38205
38206        // private
38207        unregister : function(menu){
38208            delete menus[menu.id];
38209            menu.un("beforehide", onBeforeHide);
38210            menu.un("hide", onHide);
38211            menu.un("beforeshow", onBeforeShow);
38212            menu.un("show", onShow);
38213            var g = menu.group;
38214            if(g && menu.events["checkchange"]){
38215                groups[g].remove(menu);
38216                menu.un("checkchange", onCheck);
38217            }
38218        },
38219
38220        // private
38221        registerCheckable : function(menuItem){
38222            var g = menuItem.group;
38223            if(g){
38224                if(!groups[g]){
38225                    groups[g] = [];
38226                }
38227                groups[g].push(menuItem);
38228                menuItem.on("beforecheckchange", onBeforeCheck);
38229            }
38230        },
38231
38232        // private
38233        unregisterCheckable : function(menuItem){
38234            var g = menuItem.group;
38235            if(g){
38236                groups[g].remove(menuItem);
38237                menuItem.un("beforecheckchange", onBeforeCheck);
38238            }
38239        }
38240    };
38241 }();/*
38242  * Based on:
38243  * Ext JS Library 1.1.1
38244  * Copyright(c) 2006-2007, Ext JS, LLC.
38245  *
38246  * Originally Released Under LGPL - original licence link has changed is not relivant.
38247  *
38248  * Fork - LGPL
38249  * <script type="text/javascript">
38250  */
38251  
38252
38253 /**
38254  * @class Roo.menu.BaseItem
38255  * @extends Roo.Component
38256  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38257  * management and base configuration options shared by all menu components.
38258  * @constructor
38259  * Creates a new BaseItem
38260  * @param {Object} config Configuration options
38261  */
38262 Roo.menu.BaseItem = function(config){
38263     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38264
38265     this.addEvents({
38266         /**
38267          * @event click
38268          * Fires when this item is clicked
38269          * @param {Roo.menu.BaseItem} this
38270          * @param {Roo.EventObject} e
38271          */
38272         click: true,
38273         /**
38274          * @event activate
38275          * Fires when this item is activated
38276          * @param {Roo.menu.BaseItem} this
38277          */
38278         activate : true,
38279         /**
38280          * @event deactivate
38281          * Fires when this item is deactivated
38282          * @param {Roo.menu.BaseItem} this
38283          */
38284         deactivate : true
38285     });
38286
38287     if(this.handler){
38288         this.on("click", this.handler, this.scope, true);
38289     }
38290 };
38291
38292 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38293     /**
38294      * @cfg {Function} handler
38295      * A function that will handle the click event of this menu item (defaults to undefined)
38296      */
38297     /**
38298      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38299      */
38300     canActivate : false,
38301     
38302      /**
38303      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38304      */
38305     hidden: false,
38306     
38307     /**
38308      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38309      */
38310     activeClass : "x-menu-item-active",
38311     /**
38312      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38313      */
38314     hideOnClick : true,
38315     /**
38316      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38317      */
38318     hideDelay : 100,
38319
38320     // private
38321     ctype: "Roo.menu.BaseItem",
38322
38323     // private
38324     actionMode : "container",
38325
38326     // private
38327     render : function(container, parentMenu){
38328         this.parentMenu = parentMenu;
38329         Roo.menu.BaseItem.superclass.render.call(this, container);
38330         this.container.menuItemId = this.id;
38331     },
38332
38333     // private
38334     onRender : function(container, position){
38335         this.el = Roo.get(this.el);
38336         container.dom.appendChild(this.el.dom);
38337     },
38338
38339     // private
38340     onClick : function(e){
38341         if(!this.disabled && this.fireEvent("click", this, e) !== false
38342                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38343             this.handleClick(e);
38344         }else{
38345             e.stopEvent();
38346         }
38347     },
38348
38349     // private
38350     activate : function(){
38351         if(this.disabled){
38352             return false;
38353         }
38354         var li = this.container;
38355         li.addClass(this.activeClass);
38356         this.region = li.getRegion().adjust(2, 2, -2, -2);
38357         this.fireEvent("activate", this);
38358         return true;
38359     },
38360
38361     // private
38362     deactivate : function(){
38363         this.container.removeClass(this.activeClass);
38364         this.fireEvent("deactivate", this);
38365     },
38366
38367     // private
38368     shouldDeactivate : function(e){
38369         return !this.region || !this.region.contains(e.getPoint());
38370     },
38371
38372     // private
38373     handleClick : function(e){
38374         if(this.hideOnClick){
38375             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38376         }
38377     },
38378
38379     // private
38380     expandMenu : function(autoActivate){
38381         // do nothing
38382     },
38383
38384     // private
38385     hideMenu : function(){
38386         // do nothing
38387     }
38388 });/*
38389  * Based on:
38390  * Ext JS Library 1.1.1
38391  * Copyright(c) 2006-2007, Ext JS, LLC.
38392  *
38393  * Originally Released Under LGPL - original licence link has changed is not relivant.
38394  *
38395  * Fork - LGPL
38396  * <script type="text/javascript">
38397  */
38398  
38399 /**
38400  * @class Roo.menu.Adapter
38401  * @extends Roo.menu.BaseItem
38402  * 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.
38403  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38404  * @constructor
38405  * Creates a new Adapter
38406  * @param {Object} config Configuration options
38407  */
38408 Roo.menu.Adapter = function(component, config){
38409     Roo.menu.Adapter.superclass.constructor.call(this, config);
38410     this.component = component;
38411 };
38412 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38413     // private
38414     canActivate : true,
38415
38416     // private
38417     onRender : function(container, position){
38418         this.component.render(container);
38419         this.el = this.component.getEl();
38420     },
38421
38422     // private
38423     activate : function(){
38424         if(this.disabled){
38425             return false;
38426         }
38427         this.component.focus();
38428         this.fireEvent("activate", this);
38429         return true;
38430     },
38431
38432     // private
38433     deactivate : function(){
38434         this.fireEvent("deactivate", this);
38435     },
38436
38437     // private
38438     disable : function(){
38439         this.component.disable();
38440         Roo.menu.Adapter.superclass.disable.call(this);
38441     },
38442
38443     // private
38444     enable : function(){
38445         this.component.enable();
38446         Roo.menu.Adapter.superclass.enable.call(this);
38447     }
38448 });/*
38449  * Based on:
38450  * Ext JS Library 1.1.1
38451  * Copyright(c) 2006-2007, Ext JS, LLC.
38452  *
38453  * Originally Released Under LGPL - original licence link has changed is not relivant.
38454  *
38455  * Fork - LGPL
38456  * <script type="text/javascript">
38457  */
38458
38459 /**
38460  * @class Roo.menu.TextItem
38461  * @extends Roo.menu.BaseItem
38462  * Adds a static text string to a menu, usually used as either a heading or group separator.
38463  * Note: old style constructor with text is still supported.
38464  * 
38465  * @constructor
38466  * Creates a new TextItem
38467  * @param {Object} cfg Configuration
38468  */
38469 Roo.menu.TextItem = function(cfg){
38470     if (typeof(cfg) == 'string') {
38471         this.text = cfg;
38472     } else {
38473         Roo.apply(this,cfg);
38474     }
38475     
38476     Roo.menu.TextItem.superclass.constructor.call(this);
38477 };
38478
38479 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38480     /**
38481      * @cfg {String} text Text to show on item.
38482      */
38483     text : '',
38484     
38485     /**
38486      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38487      */
38488     hideOnClick : false,
38489     /**
38490      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38491      */
38492     itemCls : "x-menu-text",
38493
38494     // private
38495     onRender : function(){
38496         var s = document.createElement("span");
38497         s.className = this.itemCls;
38498         s.innerHTML = this.text;
38499         this.el = s;
38500         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38501     }
38502 });/*
38503  * Based on:
38504  * Ext JS Library 1.1.1
38505  * Copyright(c) 2006-2007, Ext JS, LLC.
38506  *
38507  * Originally Released Under LGPL - original licence link has changed is not relivant.
38508  *
38509  * Fork - LGPL
38510  * <script type="text/javascript">
38511  */
38512
38513 /**
38514  * @class Roo.menu.Separator
38515  * @extends Roo.menu.BaseItem
38516  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38517  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38518  * @constructor
38519  * @param {Object} config Configuration options
38520  */
38521 Roo.menu.Separator = function(config){
38522     Roo.menu.Separator.superclass.constructor.call(this, config);
38523 };
38524
38525 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38526     /**
38527      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38528      */
38529     itemCls : "x-menu-sep",
38530     /**
38531      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38532      */
38533     hideOnClick : false,
38534
38535     // private
38536     onRender : function(li){
38537         var s = document.createElement("span");
38538         s.className = this.itemCls;
38539         s.innerHTML = "&#160;";
38540         this.el = s;
38541         li.addClass("x-menu-sep-li");
38542         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38543     }
38544 });/*
38545  * Based on:
38546  * Ext JS Library 1.1.1
38547  * Copyright(c) 2006-2007, Ext JS, LLC.
38548  *
38549  * Originally Released Under LGPL - original licence link has changed is not relivant.
38550  *
38551  * Fork - LGPL
38552  * <script type="text/javascript">
38553  */
38554 /**
38555  * @class Roo.menu.Item
38556  * @extends Roo.menu.BaseItem
38557  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38558  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38559  * activation and click handling.
38560  * @constructor
38561  * Creates a new Item
38562  * @param {Object} config Configuration options
38563  */
38564 Roo.menu.Item = function(config){
38565     Roo.menu.Item.superclass.constructor.call(this, config);
38566     if(this.menu){
38567         this.menu = Roo.menu.MenuMgr.get(this.menu);
38568     }
38569 };
38570 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38571     
38572     /**
38573      * @cfg {String} text
38574      * The text to show on the menu item.
38575      */
38576     text: '',
38577      /**
38578      * @cfg {String} HTML to render in menu
38579      * The text to show on the menu item (HTML version).
38580      */
38581     html: '',
38582     /**
38583      * @cfg {String} icon
38584      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38585      */
38586     icon: undefined,
38587     /**
38588      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38589      */
38590     itemCls : "x-menu-item",
38591     /**
38592      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38593      */
38594     canActivate : true,
38595     /**
38596      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38597      */
38598     showDelay: 200,
38599     // doc'd in BaseItem
38600     hideDelay: 200,
38601
38602     // private
38603     ctype: "Roo.menu.Item",
38604     
38605     // private
38606     onRender : function(container, position){
38607         var el = document.createElement("a");
38608         el.hideFocus = true;
38609         el.unselectable = "on";
38610         el.href = this.href || "#";
38611         if(this.hrefTarget){
38612             el.target = this.hrefTarget;
38613         }
38614         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38615         
38616         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38617         
38618         el.innerHTML = String.format(
38619                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38620                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38621         this.el = el;
38622         Roo.menu.Item.superclass.onRender.call(this, container, position);
38623     },
38624
38625     /**
38626      * Sets the text to display in this menu item
38627      * @param {String} text The text to display
38628      * @param {Boolean} isHTML true to indicate text is pure html.
38629      */
38630     setText : function(text, isHTML){
38631         if (isHTML) {
38632             this.html = text;
38633         } else {
38634             this.text = text;
38635             this.html = '';
38636         }
38637         if(this.rendered){
38638             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38639      
38640             this.el.update(String.format(
38641                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38642                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38643             this.parentMenu.autoWidth();
38644         }
38645     },
38646
38647     // private
38648     handleClick : function(e){
38649         if(!this.href){ // if no link defined, stop the event automatically
38650             e.stopEvent();
38651         }
38652         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38653     },
38654
38655     // private
38656     activate : function(autoExpand){
38657         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38658             this.focus();
38659             if(autoExpand){
38660                 this.expandMenu();
38661             }
38662         }
38663         return true;
38664     },
38665
38666     // private
38667     shouldDeactivate : function(e){
38668         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38669             if(this.menu && this.menu.isVisible()){
38670                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38671             }
38672             return true;
38673         }
38674         return false;
38675     },
38676
38677     // private
38678     deactivate : function(){
38679         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38680         this.hideMenu();
38681     },
38682
38683     // private
38684     expandMenu : function(autoActivate){
38685         if(!this.disabled && this.menu){
38686             clearTimeout(this.hideTimer);
38687             delete this.hideTimer;
38688             if(!this.menu.isVisible() && !this.showTimer){
38689                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38690             }else if (this.menu.isVisible() && autoActivate){
38691                 this.menu.tryActivate(0, 1);
38692             }
38693         }
38694     },
38695
38696     // private
38697     deferExpand : function(autoActivate){
38698         delete this.showTimer;
38699         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38700         if(autoActivate){
38701             this.menu.tryActivate(0, 1);
38702         }
38703     },
38704
38705     // private
38706     hideMenu : function(){
38707         clearTimeout(this.showTimer);
38708         delete this.showTimer;
38709         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38710             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38711         }
38712     },
38713
38714     // private
38715     deferHide : function(){
38716         delete this.hideTimer;
38717         this.menu.hide();
38718     }
38719 });/*
38720  * Based on:
38721  * Ext JS Library 1.1.1
38722  * Copyright(c) 2006-2007, Ext JS, LLC.
38723  *
38724  * Originally Released Under LGPL - original licence link has changed is not relivant.
38725  *
38726  * Fork - LGPL
38727  * <script type="text/javascript">
38728  */
38729  
38730 /**
38731  * @class Roo.menu.CheckItem
38732  * @extends Roo.menu.Item
38733  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38734  * @constructor
38735  * Creates a new CheckItem
38736  * @param {Object} config Configuration options
38737  */
38738 Roo.menu.CheckItem = function(config){
38739     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38740     this.addEvents({
38741         /**
38742          * @event beforecheckchange
38743          * Fires before the checked value is set, providing an opportunity to cancel if needed
38744          * @param {Roo.menu.CheckItem} this
38745          * @param {Boolean} checked The new checked value that will be set
38746          */
38747         "beforecheckchange" : true,
38748         /**
38749          * @event checkchange
38750          * Fires after the checked value has been set
38751          * @param {Roo.menu.CheckItem} this
38752          * @param {Boolean} checked The checked value that was set
38753          */
38754         "checkchange" : true
38755     });
38756     if(this.checkHandler){
38757         this.on('checkchange', this.checkHandler, this.scope);
38758     }
38759 };
38760 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38761     /**
38762      * @cfg {String} group
38763      * All check items with the same group name will automatically be grouped into a single-select
38764      * radio button group (defaults to '')
38765      */
38766     /**
38767      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38768      */
38769     itemCls : "x-menu-item x-menu-check-item",
38770     /**
38771      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38772      */
38773     groupClass : "x-menu-group-item",
38774
38775     /**
38776      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38777      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38778      * initialized with checked = true will be rendered as checked.
38779      */
38780     checked: false,
38781
38782     // private
38783     ctype: "Roo.menu.CheckItem",
38784
38785     // private
38786     onRender : function(c){
38787         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38788         if(this.group){
38789             this.el.addClass(this.groupClass);
38790         }
38791         Roo.menu.MenuMgr.registerCheckable(this);
38792         if(this.checked){
38793             this.checked = false;
38794             this.setChecked(true, true);
38795         }
38796     },
38797
38798     // private
38799     destroy : function(){
38800         if(this.rendered){
38801             Roo.menu.MenuMgr.unregisterCheckable(this);
38802         }
38803         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38804     },
38805
38806     /**
38807      * Set the checked state of this item
38808      * @param {Boolean} checked The new checked value
38809      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38810      */
38811     setChecked : function(state, suppressEvent){
38812         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38813             if(this.container){
38814                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38815             }
38816             this.checked = state;
38817             if(suppressEvent !== true){
38818                 this.fireEvent("checkchange", this, state);
38819             }
38820         }
38821     },
38822
38823     // private
38824     handleClick : function(e){
38825        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38826            this.setChecked(!this.checked);
38827        }
38828        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38829     }
38830 });/*
38831  * Based on:
38832  * Ext JS Library 1.1.1
38833  * Copyright(c) 2006-2007, Ext JS, LLC.
38834  *
38835  * Originally Released Under LGPL - original licence link has changed is not relivant.
38836  *
38837  * Fork - LGPL
38838  * <script type="text/javascript">
38839  */
38840  
38841 /**
38842  * @class Roo.menu.DateItem
38843  * @extends Roo.menu.Adapter
38844  * A menu item that wraps the {@link Roo.DatPicker} component.
38845  * @constructor
38846  * Creates a new DateItem
38847  * @param {Object} config Configuration options
38848  */
38849 Roo.menu.DateItem = function(config){
38850     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38851     /** The Roo.DatePicker object @type Roo.DatePicker */
38852     this.picker = this.component;
38853     this.addEvents({select: true});
38854     
38855     this.picker.on("render", function(picker){
38856         picker.getEl().swallowEvent("click");
38857         picker.container.addClass("x-menu-date-item");
38858     });
38859
38860     this.picker.on("select", this.onSelect, this);
38861 };
38862
38863 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38864     // private
38865     onSelect : function(picker, date){
38866         this.fireEvent("select", this, date, picker);
38867         Roo.menu.DateItem.superclass.handleClick.call(this);
38868     }
38869 });/*
38870  * Based on:
38871  * Ext JS Library 1.1.1
38872  * Copyright(c) 2006-2007, Ext JS, LLC.
38873  *
38874  * Originally Released Under LGPL - original licence link has changed is not relivant.
38875  *
38876  * Fork - LGPL
38877  * <script type="text/javascript">
38878  */
38879  
38880 /**
38881  * @class Roo.menu.ColorItem
38882  * @extends Roo.menu.Adapter
38883  * A menu item that wraps the {@link Roo.ColorPalette} component.
38884  * @constructor
38885  * Creates a new ColorItem
38886  * @param {Object} config Configuration options
38887  */
38888 Roo.menu.ColorItem = function(config){
38889     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38890     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38891     this.palette = this.component;
38892     this.relayEvents(this.palette, ["select"]);
38893     if(this.selectHandler){
38894         this.on('select', this.selectHandler, this.scope);
38895     }
38896 };
38897 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38898  * Based on:
38899  * Ext JS Library 1.1.1
38900  * Copyright(c) 2006-2007, Ext JS, LLC.
38901  *
38902  * Originally Released Under LGPL - original licence link has changed is not relivant.
38903  *
38904  * Fork - LGPL
38905  * <script type="text/javascript">
38906  */
38907  
38908
38909 /**
38910  * @class Roo.menu.DateMenu
38911  * @extends Roo.menu.Menu
38912  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38913  * @constructor
38914  * Creates a new DateMenu
38915  * @param {Object} config Configuration options
38916  */
38917 Roo.menu.DateMenu = function(config){
38918     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38919     this.plain = true;
38920     var di = new Roo.menu.DateItem(config);
38921     this.add(di);
38922     /**
38923      * The {@link Roo.DatePicker} instance for this DateMenu
38924      * @type DatePicker
38925      */
38926     this.picker = di.picker;
38927     /**
38928      * @event select
38929      * @param {DatePicker} picker
38930      * @param {Date} date
38931      */
38932     this.relayEvents(di, ["select"]);
38933     this.on('beforeshow', function(){
38934         if(this.picker){
38935             this.picker.hideMonthPicker(false);
38936         }
38937     }, this);
38938 };
38939 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38940     cls:'x-date-menu'
38941 });/*
38942  * Based on:
38943  * Ext JS Library 1.1.1
38944  * Copyright(c) 2006-2007, Ext JS, LLC.
38945  *
38946  * Originally Released Under LGPL - original licence link has changed is not relivant.
38947  *
38948  * Fork - LGPL
38949  * <script type="text/javascript">
38950  */
38951  
38952
38953 /**
38954  * @class Roo.menu.ColorMenu
38955  * @extends Roo.menu.Menu
38956  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38957  * @constructor
38958  * Creates a new ColorMenu
38959  * @param {Object} config Configuration options
38960  */
38961 Roo.menu.ColorMenu = function(config){
38962     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38963     this.plain = true;
38964     var ci = new Roo.menu.ColorItem(config);
38965     this.add(ci);
38966     /**
38967      * The {@link Roo.ColorPalette} instance for this ColorMenu
38968      * @type ColorPalette
38969      */
38970     this.palette = ci.palette;
38971     /**
38972      * @event select
38973      * @param {ColorPalette} palette
38974      * @param {String} color
38975      */
38976     this.relayEvents(ci, ["select"]);
38977 };
38978 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38979  * Based on:
38980  * Ext JS Library 1.1.1
38981  * Copyright(c) 2006-2007, Ext JS, LLC.
38982  *
38983  * Originally Released Under LGPL - original licence link has changed is not relivant.
38984  *
38985  * Fork - LGPL
38986  * <script type="text/javascript">
38987  */
38988  
38989 /**
38990  * @class Roo.form.TextItem
38991  * @extends Roo.BoxComponent
38992  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38993  * @constructor
38994  * Creates a new TextItem
38995  * @param {Object} config Configuration options
38996  */
38997 Roo.form.TextItem = function(config){
38998     Roo.form.TextItem.superclass.constructor.call(this, config);
38999 };
39000
39001 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39002     
39003     /**
39004      * @cfg {String} tag the tag for this item (default div)
39005      */
39006     tag : 'div',
39007     /**
39008      * @cfg {String} html the content for this item
39009      */
39010     html : '',
39011     
39012     getAutoCreate : function()
39013     {
39014         var cfg = {
39015             id: this.id,
39016             tag: this.tag,
39017             html: this.html,
39018             cls: 'x-form-item'
39019         };
39020         
39021         return cfg;
39022         
39023     },
39024     
39025     onRender : function(ct, position)
39026     {
39027         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39028         
39029         if(!this.el){
39030             var cfg = this.getAutoCreate();
39031             if(!cfg.name){
39032                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39033             }
39034             if (!cfg.name.length) {
39035                 delete cfg.name;
39036             }
39037             this.el = ct.createChild(cfg, position);
39038         }
39039     },
39040     /*
39041      * setHTML
39042      * @param {String} html update the Contents of the element.
39043      */
39044     setHTML : function(html)
39045     {
39046         this.fieldEl.dom.innerHTML = html;
39047     }
39048     
39049 });/*
39050  * Based on:
39051  * Ext JS Library 1.1.1
39052  * Copyright(c) 2006-2007, Ext JS, LLC.
39053  *
39054  * Originally Released Under LGPL - original licence link has changed is not relivant.
39055  *
39056  * Fork - LGPL
39057  * <script type="text/javascript">
39058  */
39059  
39060 /**
39061  * @class Roo.form.Field
39062  * @extends Roo.BoxComponent
39063  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39064  * @constructor
39065  * Creates a new Field
39066  * @param {Object} config Configuration options
39067  */
39068 Roo.form.Field = function(config){
39069     Roo.form.Field.superclass.constructor.call(this, config);
39070 };
39071
39072 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39073     /**
39074      * @cfg {String} fieldLabel Label to use when rendering a form.
39075      */
39076        /**
39077      * @cfg {String} qtip Mouse over tip
39078      */
39079      
39080     /**
39081      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39082      */
39083     invalidClass : "x-form-invalid",
39084     /**
39085      * @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")
39086      */
39087     invalidText : "The value in this field is invalid",
39088     /**
39089      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39090      */
39091     focusClass : "x-form-focus",
39092     /**
39093      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39094       automatic validation (defaults to "keyup").
39095      */
39096     validationEvent : "keyup",
39097     /**
39098      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39099      */
39100     validateOnBlur : true,
39101     /**
39102      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39103      */
39104     validationDelay : 250,
39105     /**
39106      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39107      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39108      */
39109     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39110     /**
39111      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39112      */
39113     fieldClass : "x-form-field",
39114     /**
39115      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39116      *<pre>
39117 Value         Description
39118 -----------   ----------------------------------------------------------------------
39119 qtip          Display a quick tip when the user hovers over the field
39120 title         Display a default browser title attribute popup
39121 under         Add a block div beneath the field containing the error text
39122 side          Add an error icon to the right of the field with a popup on hover
39123 [element id]  Add the error text directly to the innerHTML of the specified element
39124 </pre>
39125      */
39126     msgTarget : 'qtip',
39127     /**
39128      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39129      */
39130     msgFx : 'normal',
39131
39132     /**
39133      * @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.
39134      */
39135     readOnly : false,
39136
39137     /**
39138      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39139      */
39140     disabled : false,
39141
39142     /**
39143      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39144      */
39145     inputType : undefined,
39146     
39147     /**
39148      * @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).
39149          */
39150         tabIndex : undefined,
39151         
39152     // private
39153     isFormField : true,
39154
39155     // private
39156     hasFocus : false,
39157     /**
39158      * @property {Roo.Element} fieldEl
39159      * Element Containing the rendered Field (with label etc.)
39160      */
39161     /**
39162      * @cfg {Mixed} value A value to initialize this field with.
39163      */
39164     value : undefined,
39165
39166     /**
39167      * @cfg {String} name The field's HTML name attribute.
39168      */
39169     /**
39170      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39171      */
39172     // private
39173     loadedValue : false,
39174      
39175      
39176         // private ??
39177         initComponent : function(){
39178         Roo.form.Field.superclass.initComponent.call(this);
39179         this.addEvents({
39180             /**
39181              * @event focus
39182              * Fires when this field receives input focus.
39183              * @param {Roo.form.Field} this
39184              */
39185             focus : true,
39186             /**
39187              * @event blur
39188              * Fires when this field loses input focus.
39189              * @param {Roo.form.Field} this
39190              */
39191             blur : true,
39192             /**
39193              * @event specialkey
39194              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39195              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39196              * @param {Roo.form.Field} this
39197              * @param {Roo.EventObject} e The event object
39198              */
39199             specialkey : true,
39200             /**
39201              * @event change
39202              * Fires just before the field blurs if the field value has changed.
39203              * @param {Roo.form.Field} this
39204              * @param {Mixed} newValue The new value
39205              * @param {Mixed} oldValue The original value
39206              */
39207             change : true,
39208             /**
39209              * @event invalid
39210              * Fires after the field has been marked as invalid.
39211              * @param {Roo.form.Field} this
39212              * @param {String} msg The validation message
39213              */
39214             invalid : true,
39215             /**
39216              * @event valid
39217              * Fires after the field has been validated with no errors.
39218              * @param {Roo.form.Field} this
39219              */
39220             valid : true,
39221              /**
39222              * @event keyup
39223              * Fires after the key up
39224              * @param {Roo.form.Field} this
39225              * @param {Roo.EventObject}  e The event Object
39226              */
39227             keyup : true
39228         });
39229     },
39230
39231     /**
39232      * Returns the name attribute of the field if available
39233      * @return {String} name The field name
39234      */
39235     getName: function(){
39236          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39237     },
39238
39239     // private
39240     onRender : function(ct, position){
39241         Roo.form.Field.superclass.onRender.call(this, ct, position);
39242         if(!this.el){
39243             var cfg = this.getAutoCreate();
39244             if(!cfg.name){
39245                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39246             }
39247             if (!cfg.name.length) {
39248                 delete cfg.name;
39249             }
39250             if(this.inputType){
39251                 cfg.type = this.inputType;
39252             }
39253             this.el = ct.createChild(cfg, position);
39254         }
39255         var type = this.el.dom.type;
39256         if(type){
39257             if(type == 'password'){
39258                 type = 'text';
39259             }
39260             this.el.addClass('x-form-'+type);
39261         }
39262         if(this.readOnly){
39263             this.el.dom.readOnly = true;
39264         }
39265         if(this.tabIndex !== undefined){
39266             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39267         }
39268
39269         this.el.addClass([this.fieldClass, this.cls]);
39270         this.initValue();
39271     },
39272
39273     /**
39274      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39275      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39276      * @return {Roo.form.Field} this
39277      */
39278     applyTo : function(target){
39279         this.allowDomMove = false;
39280         this.el = Roo.get(target);
39281         this.render(this.el.dom.parentNode);
39282         return this;
39283     },
39284
39285     // private
39286     initValue : function(){
39287         if(this.value !== undefined){
39288             this.setValue(this.value);
39289         }else if(this.el.dom.value.length > 0){
39290             this.setValue(this.el.dom.value);
39291         }
39292     },
39293
39294     /**
39295      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39296      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39297      */
39298     isDirty : function() {
39299         if(this.disabled) {
39300             return false;
39301         }
39302         return String(this.getValue()) !== String(this.originalValue);
39303     },
39304
39305     /**
39306      * stores the current value in loadedValue
39307      */
39308     resetHasChanged : function()
39309     {
39310         this.loadedValue = String(this.getValue());
39311     },
39312     /**
39313      * checks the current value against the 'loaded' value.
39314      * Note - will return false if 'resetHasChanged' has not been called first.
39315      */
39316     hasChanged : function()
39317     {
39318         if(this.disabled || this.readOnly) {
39319             return false;
39320         }
39321         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39322     },
39323     
39324     
39325     
39326     // private
39327     afterRender : function(){
39328         Roo.form.Field.superclass.afterRender.call(this);
39329         this.initEvents();
39330     },
39331
39332     // private
39333     fireKey : function(e){
39334         //Roo.log('field ' + e.getKey());
39335         if(e.isNavKeyPress()){
39336             this.fireEvent("specialkey", this, e);
39337         }
39338     },
39339
39340     /**
39341      * Resets the current field value to the originally loaded value and clears any validation messages
39342      */
39343     reset : function(){
39344         this.setValue(this.resetValue);
39345         this.originalValue = this.getValue();
39346         this.clearInvalid();
39347     },
39348
39349     // private
39350     initEvents : function(){
39351         // safari killled keypress - so keydown is now used..
39352         this.el.on("keydown" , this.fireKey,  this);
39353         this.el.on("focus", this.onFocus,  this);
39354         this.el.on("blur", this.onBlur,  this);
39355         this.el.relayEvent('keyup', this);
39356
39357         // reference to original value for reset
39358         this.originalValue = this.getValue();
39359         this.resetValue =  this.getValue();
39360     },
39361
39362     // private
39363     onFocus : function(){
39364         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39365             this.el.addClass(this.focusClass);
39366         }
39367         if(!this.hasFocus){
39368             this.hasFocus = true;
39369             this.startValue = this.getValue();
39370             this.fireEvent("focus", this);
39371         }
39372     },
39373
39374     beforeBlur : Roo.emptyFn,
39375
39376     // private
39377     onBlur : function(){
39378         this.beforeBlur();
39379         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39380             this.el.removeClass(this.focusClass);
39381         }
39382         this.hasFocus = false;
39383         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39384             this.validate();
39385         }
39386         var v = this.getValue();
39387         if(String(v) !== String(this.startValue)){
39388             this.fireEvent('change', this, v, this.startValue);
39389         }
39390         this.fireEvent("blur", this);
39391     },
39392
39393     /**
39394      * Returns whether or not the field value is currently valid
39395      * @param {Boolean} preventMark True to disable marking the field invalid
39396      * @return {Boolean} True if the value is valid, else false
39397      */
39398     isValid : function(preventMark){
39399         if(this.disabled){
39400             return true;
39401         }
39402         var restore = this.preventMark;
39403         this.preventMark = preventMark === true;
39404         var v = this.validateValue(this.processValue(this.getRawValue()));
39405         this.preventMark = restore;
39406         return v;
39407     },
39408
39409     /**
39410      * Validates the field value
39411      * @return {Boolean} True if the value is valid, else false
39412      */
39413     validate : function(){
39414         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39415             this.clearInvalid();
39416             return true;
39417         }
39418         return false;
39419     },
39420
39421     processValue : function(value){
39422         return value;
39423     },
39424
39425     // private
39426     // Subclasses should provide the validation implementation by overriding this
39427     validateValue : function(value){
39428         return true;
39429     },
39430
39431     /**
39432      * Mark this field as invalid
39433      * @param {String} msg The validation message
39434      */
39435     markInvalid : function(msg){
39436         if(!this.rendered || this.preventMark){ // not rendered
39437             return;
39438         }
39439         
39440         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39441         
39442         obj.el.addClass(this.invalidClass);
39443         msg = msg || this.invalidText;
39444         switch(this.msgTarget){
39445             case 'qtip':
39446                 obj.el.dom.qtip = msg;
39447                 obj.el.dom.qclass = 'x-form-invalid-tip';
39448                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39449                     Roo.QuickTips.enable();
39450                 }
39451                 break;
39452             case 'title':
39453                 this.el.dom.title = msg;
39454                 break;
39455             case 'under':
39456                 if(!this.errorEl){
39457                     var elp = this.el.findParent('.x-form-element', 5, true);
39458                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39459                     this.errorEl.setWidth(elp.getWidth(true)-20);
39460                 }
39461                 this.errorEl.update(msg);
39462                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39463                 break;
39464             case 'side':
39465                 if(!this.errorIcon){
39466                     var elp = this.el.findParent('.x-form-element', 5, true);
39467                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39468                 }
39469                 this.alignErrorIcon();
39470                 this.errorIcon.dom.qtip = msg;
39471                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39472                 this.errorIcon.show();
39473                 this.on('resize', this.alignErrorIcon, this);
39474                 break;
39475             default:
39476                 var t = Roo.getDom(this.msgTarget);
39477                 t.innerHTML = msg;
39478                 t.style.display = this.msgDisplay;
39479                 break;
39480         }
39481         this.fireEvent('invalid', this, msg);
39482     },
39483
39484     // private
39485     alignErrorIcon : function(){
39486         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39487     },
39488
39489     /**
39490      * Clear any invalid styles/messages for this field
39491      */
39492     clearInvalid : function(){
39493         if(!this.rendered || this.preventMark){ // not rendered
39494             return;
39495         }
39496         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39497         
39498         obj.el.removeClass(this.invalidClass);
39499         switch(this.msgTarget){
39500             case 'qtip':
39501                 obj.el.dom.qtip = '';
39502                 break;
39503             case 'title':
39504                 this.el.dom.title = '';
39505                 break;
39506             case 'under':
39507                 if(this.errorEl){
39508                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39509                 }
39510                 break;
39511             case 'side':
39512                 if(this.errorIcon){
39513                     this.errorIcon.dom.qtip = '';
39514                     this.errorIcon.hide();
39515                     this.un('resize', this.alignErrorIcon, this);
39516                 }
39517                 break;
39518             default:
39519                 var t = Roo.getDom(this.msgTarget);
39520                 t.innerHTML = '';
39521                 t.style.display = 'none';
39522                 break;
39523         }
39524         this.fireEvent('valid', this);
39525     },
39526
39527     /**
39528      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39529      * @return {Mixed} value The field value
39530      */
39531     getRawValue : function(){
39532         var v = this.el.getValue();
39533         
39534         return v;
39535     },
39536
39537     /**
39538      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39539      * @return {Mixed} value The field value
39540      */
39541     getValue : function(){
39542         var v = this.el.getValue();
39543          
39544         return v;
39545     },
39546
39547     /**
39548      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39549      * @param {Mixed} value The value to set
39550      */
39551     setRawValue : function(v){
39552         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39553     },
39554
39555     /**
39556      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39557      * @param {Mixed} value The value to set
39558      */
39559     setValue : function(v){
39560         this.value = v;
39561         if(this.rendered){
39562             this.el.dom.value = (v === null || v === undefined ? '' : v);
39563              this.validate();
39564         }
39565     },
39566
39567     adjustSize : function(w, h){
39568         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39569         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39570         return s;
39571     },
39572
39573     adjustWidth : function(tag, w){
39574         tag = tag.toLowerCase();
39575         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39576             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39577                 if(tag == 'input'){
39578                     return w + 2;
39579                 }
39580                 if(tag == 'textarea'){
39581                     return w-2;
39582                 }
39583             }else if(Roo.isOpera){
39584                 if(tag == 'input'){
39585                     return w + 2;
39586                 }
39587                 if(tag == 'textarea'){
39588                     return w-2;
39589                 }
39590             }
39591         }
39592         return w;
39593     }
39594 });
39595
39596
39597 // anything other than normal should be considered experimental
39598 Roo.form.Field.msgFx = {
39599     normal : {
39600         show: function(msgEl, f){
39601             msgEl.setDisplayed('block');
39602         },
39603
39604         hide : function(msgEl, f){
39605             msgEl.setDisplayed(false).update('');
39606         }
39607     },
39608
39609     slide : {
39610         show: function(msgEl, f){
39611             msgEl.slideIn('t', {stopFx:true});
39612         },
39613
39614         hide : function(msgEl, f){
39615             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39616         }
39617     },
39618
39619     slideRight : {
39620         show: function(msgEl, f){
39621             msgEl.fixDisplay();
39622             msgEl.alignTo(f.el, 'tl-tr');
39623             msgEl.slideIn('l', {stopFx:true});
39624         },
39625
39626         hide : function(msgEl, f){
39627             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39628         }
39629     }
39630 };/*
39631  * Based on:
39632  * Ext JS Library 1.1.1
39633  * Copyright(c) 2006-2007, Ext JS, LLC.
39634  *
39635  * Originally Released Under LGPL - original licence link has changed is not relivant.
39636  *
39637  * Fork - LGPL
39638  * <script type="text/javascript">
39639  */
39640  
39641
39642 /**
39643  * @class Roo.form.TextField
39644  * @extends Roo.form.Field
39645  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39646  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39647  * @constructor
39648  * Creates a new TextField
39649  * @param {Object} config Configuration options
39650  */
39651 Roo.form.TextField = function(config){
39652     Roo.form.TextField.superclass.constructor.call(this, config);
39653     this.addEvents({
39654         /**
39655          * @event autosize
39656          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39657          * according to the default logic, but this event provides a hook for the developer to apply additional
39658          * logic at runtime to resize the field if needed.
39659              * @param {Roo.form.Field} this This text field
39660              * @param {Number} width The new field width
39661              */
39662         autosize : true
39663     });
39664 };
39665
39666 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39667     /**
39668      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39669      */
39670     grow : false,
39671     /**
39672      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39673      */
39674     growMin : 30,
39675     /**
39676      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39677      */
39678     growMax : 800,
39679     /**
39680      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39681      */
39682     vtype : null,
39683     /**
39684      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39685      */
39686     maskRe : null,
39687     /**
39688      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39689      */
39690     disableKeyFilter : false,
39691     /**
39692      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39693      */
39694     allowBlank : true,
39695     /**
39696      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39697      */
39698     minLength : 0,
39699     /**
39700      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39701      */
39702     maxLength : Number.MAX_VALUE,
39703     /**
39704      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39705      */
39706     minLengthText : "The minimum length for this field is {0}",
39707     /**
39708      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39709      */
39710     maxLengthText : "The maximum length for this field is {0}",
39711     /**
39712      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39713      */
39714     selectOnFocus : false,
39715     /**
39716      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39717      */    
39718     allowLeadingSpace : false,
39719     /**
39720      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39721      */
39722     blankText : "This field is required",
39723     /**
39724      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39725      * If available, this function will be called only after the basic validators all return true, and will be passed the
39726      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39727      */
39728     validator : null,
39729     /**
39730      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39731      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39732      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39733      */
39734     regex : null,
39735     /**
39736      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39737      */
39738     regexText : "",
39739     /**
39740      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39741      */
39742     emptyText : null,
39743    
39744
39745     // private
39746     initEvents : function()
39747     {
39748         if (this.emptyText) {
39749             this.el.attr('placeholder', this.emptyText);
39750         }
39751         
39752         Roo.form.TextField.superclass.initEvents.call(this);
39753         if(this.validationEvent == 'keyup'){
39754             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39755             this.el.on('keyup', this.filterValidation, this);
39756         }
39757         else if(this.validationEvent !== false){
39758             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39759         }
39760         
39761         if(this.selectOnFocus){
39762             this.on("focus", this.preFocus, this);
39763         }
39764         if (!this.allowLeadingSpace) {
39765             this.on('blur', this.cleanLeadingSpace, this);
39766         }
39767         
39768         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39769             this.el.on("keypress", this.filterKeys, this);
39770         }
39771         if(this.grow){
39772             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39773             this.el.on("click", this.autoSize,  this);
39774         }
39775         if(this.el.is('input[type=password]') && Roo.isSafari){
39776             this.el.on('keydown', this.SafariOnKeyDown, this);
39777         }
39778     },
39779
39780     processValue : function(value){
39781         if(this.stripCharsRe){
39782             var newValue = value.replace(this.stripCharsRe, '');
39783             if(newValue !== value){
39784                 this.setRawValue(newValue);
39785                 return newValue;
39786             }
39787         }
39788         return value;
39789     },
39790
39791     filterValidation : function(e){
39792         if(!e.isNavKeyPress()){
39793             this.validationTask.delay(this.validationDelay);
39794         }
39795     },
39796
39797     // private
39798     onKeyUp : function(e){
39799         if(!e.isNavKeyPress()){
39800             this.autoSize();
39801         }
39802     },
39803     // private - clean the leading white space
39804     cleanLeadingSpace : function(e)
39805     {
39806         if ( this.inputType == 'file') {
39807             return;
39808         }
39809         
39810         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39811     },
39812     /**
39813      * Resets the current field value to the originally-loaded value and clears any validation messages.
39814      *  
39815      */
39816     reset : function(){
39817         Roo.form.TextField.superclass.reset.call(this);
39818        
39819     }, 
39820     // private
39821     preFocus : function(){
39822         
39823         if(this.selectOnFocus){
39824             this.el.dom.select();
39825         }
39826     },
39827
39828     
39829     // private
39830     filterKeys : function(e){
39831         var k = e.getKey();
39832         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39833             return;
39834         }
39835         var c = e.getCharCode(), cc = String.fromCharCode(c);
39836         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39837             return;
39838         }
39839         if(!this.maskRe.test(cc)){
39840             e.stopEvent();
39841         }
39842     },
39843
39844     setValue : function(v){
39845         
39846         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39847         
39848         this.autoSize();
39849     },
39850
39851     /**
39852      * Validates a value according to the field's validation rules and marks the field as invalid
39853      * if the validation fails
39854      * @param {Mixed} value The value to validate
39855      * @return {Boolean} True if the value is valid, else false
39856      */
39857     validateValue : function(value){
39858         if(value.length < 1)  { // if it's blank
39859              if(this.allowBlank){
39860                 this.clearInvalid();
39861                 return true;
39862              }else{
39863                 this.markInvalid(this.blankText);
39864                 return false;
39865              }
39866         }
39867         if(value.length < this.minLength){
39868             this.markInvalid(String.format(this.minLengthText, this.minLength));
39869             return false;
39870         }
39871         if(value.length > this.maxLength){
39872             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39873             return false;
39874         }
39875         if(this.vtype){
39876             var vt = Roo.form.VTypes;
39877             if(!vt[this.vtype](value, this)){
39878                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39879                 return false;
39880             }
39881         }
39882         if(typeof this.validator == "function"){
39883             var msg = this.validator(value);
39884             if(msg !== true){
39885                 this.markInvalid(msg);
39886                 return false;
39887             }
39888         }
39889         if(this.regex && !this.regex.test(value)){
39890             this.markInvalid(this.regexText);
39891             return false;
39892         }
39893         return true;
39894     },
39895
39896     /**
39897      * Selects text in this field
39898      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39899      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39900      */
39901     selectText : function(start, end){
39902         var v = this.getRawValue();
39903         if(v.length > 0){
39904             start = start === undefined ? 0 : start;
39905             end = end === undefined ? v.length : end;
39906             var d = this.el.dom;
39907             if(d.setSelectionRange){
39908                 d.setSelectionRange(start, end);
39909             }else if(d.createTextRange){
39910                 var range = d.createTextRange();
39911                 range.moveStart("character", start);
39912                 range.moveEnd("character", v.length-end);
39913                 range.select();
39914             }
39915         }
39916     },
39917
39918     /**
39919      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39920      * This only takes effect if grow = true, and fires the autosize event.
39921      */
39922     autoSize : function(){
39923         if(!this.grow || !this.rendered){
39924             return;
39925         }
39926         if(!this.metrics){
39927             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39928         }
39929         var el = this.el;
39930         var v = el.dom.value;
39931         var d = document.createElement('div');
39932         d.appendChild(document.createTextNode(v));
39933         v = d.innerHTML;
39934         d = null;
39935         v += "&#160;";
39936         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39937         this.el.setWidth(w);
39938         this.fireEvent("autosize", this, w);
39939     },
39940     
39941     // private
39942     SafariOnKeyDown : function(event)
39943     {
39944         // this is a workaround for a password hang bug on chrome/ webkit.
39945         
39946         var isSelectAll = false;
39947         
39948         if(this.el.dom.selectionEnd > 0){
39949             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39950         }
39951         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39952             event.preventDefault();
39953             this.setValue('');
39954             return;
39955         }
39956         
39957         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39958             
39959             event.preventDefault();
39960             // this is very hacky as keydown always get's upper case.
39961             
39962             var cc = String.fromCharCode(event.getCharCode());
39963             
39964             
39965             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39966             
39967         }
39968         
39969         
39970     }
39971 });/*
39972  * Based on:
39973  * Ext JS Library 1.1.1
39974  * Copyright(c) 2006-2007, Ext JS, LLC.
39975  *
39976  * Originally Released Under LGPL - original licence link has changed is not relivant.
39977  *
39978  * Fork - LGPL
39979  * <script type="text/javascript">
39980  */
39981  
39982 /**
39983  * @class Roo.form.Hidden
39984  * @extends Roo.form.TextField
39985  * Simple Hidden element used on forms 
39986  * 
39987  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39988  * 
39989  * @constructor
39990  * Creates a new Hidden form element.
39991  * @param {Object} config Configuration options
39992  */
39993
39994
39995
39996 // easy hidden field...
39997 Roo.form.Hidden = function(config){
39998     Roo.form.Hidden.superclass.constructor.call(this, config);
39999 };
40000   
40001 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40002     fieldLabel:      '',
40003     inputType:      'hidden',
40004     width:          50,
40005     allowBlank:     true,
40006     labelSeparator: '',
40007     hidden:         true,
40008     itemCls :       'x-form-item-display-none'
40009
40010
40011 });
40012
40013
40014 /*
40015  * Based on:
40016  * Ext JS Library 1.1.1
40017  * Copyright(c) 2006-2007, Ext JS, LLC.
40018  *
40019  * Originally Released Under LGPL - original licence link has changed is not relivant.
40020  *
40021  * Fork - LGPL
40022  * <script type="text/javascript">
40023  */
40024  
40025 /**
40026  * @class Roo.form.TriggerField
40027  * @extends Roo.form.TextField
40028  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40029  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40030  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40031  * for which you can provide a custom implementation.  For example:
40032  * <pre><code>
40033 var trigger = new Roo.form.TriggerField();
40034 trigger.onTriggerClick = myTriggerFn;
40035 trigger.applyTo('my-field');
40036 </code></pre>
40037  *
40038  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40039  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40040  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40041  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40042  * @constructor
40043  * Create a new TriggerField.
40044  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40045  * to the base TextField)
40046  */
40047 Roo.form.TriggerField = function(config){
40048     this.mimicing = false;
40049     Roo.form.TriggerField.superclass.constructor.call(this, config);
40050 };
40051
40052 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40053     /**
40054      * @cfg {String} triggerClass A CSS class to apply to the trigger
40055      */
40056     /**
40057      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40058      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40059      */
40060     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40061     /**
40062      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40063      */
40064     hideTrigger:false,
40065
40066     /** @cfg {Boolean} grow @hide */
40067     /** @cfg {Number} growMin @hide */
40068     /** @cfg {Number} growMax @hide */
40069
40070     /**
40071      * @hide 
40072      * @method
40073      */
40074     autoSize: Roo.emptyFn,
40075     // private
40076     monitorTab : true,
40077     // private
40078     deferHeight : true,
40079
40080     
40081     actionMode : 'wrap',
40082     // private
40083     onResize : function(w, h){
40084         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40085         if(typeof w == 'number'){
40086             var x = w - this.trigger.getWidth();
40087             this.el.setWidth(this.adjustWidth('input', x));
40088             this.trigger.setStyle('left', x+'px');
40089         }
40090     },
40091
40092     // private
40093     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40094
40095     // private
40096     getResizeEl : function(){
40097         return this.wrap;
40098     },
40099
40100     // private
40101     getPositionEl : function(){
40102         return this.wrap;
40103     },
40104
40105     // private
40106     alignErrorIcon : function(){
40107         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40108     },
40109
40110     // private
40111     onRender : function(ct, position){
40112         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40113         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40114         this.trigger = this.wrap.createChild(this.triggerConfig ||
40115                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40116         if(this.hideTrigger){
40117             this.trigger.setDisplayed(false);
40118         }
40119         this.initTrigger();
40120         if(!this.width){
40121             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40122         }
40123     },
40124
40125     // private
40126     initTrigger : function(){
40127         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40128         this.trigger.addClassOnOver('x-form-trigger-over');
40129         this.trigger.addClassOnClick('x-form-trigger-click');
40130     },
40131
40132     // private
40133     onDestroy : function(){
40134         if(this.trigger){
40135             this.trigger.removeAllListeners();
40136             this.trigger.remove();
40137         }
40138         if(this.wrap){
40139             this.wrap.remove();
40140         }
40141         Roo.form.TriggerField.superclass.onDestroy.call(this);
40142     },
40143
40144     // private
40145     onFocus : function(){
40146         Roo.form.TriggerField.superclass.onFocus.call(this);
40147         if(!this.mimicing){
40148             this.wrap.addClass('x-trigger-wrap-focus');
40149             this.mimicing = true;
40150             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40151             if(this.monitorTab){
40152                 this.el.on("keydown", this.checkTab, this);
40153             }
40154         }
40155     },
40156
40157     // private
40158     checkTab : function(e){
40159         if(e.getKey() == e.TAB){
40160             this.triggerBlur();
40161         }
40162     },
40163
40164     // private
40165     onBlur : function(){
40166         // do nothing
40167     },
40168
40169     // private
40170     mimicBlur : function(e, t){
40171         if(!this.wrap.contains(t) && this.validateBlur()){
40172             this.triggerBlur();
40173         }
40174     },
40175
40176     // private
40177     triggerBlur : function(){
40178         this.mimicing = false;
40179         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40180         if(this.monitorTab){
40181             this.el.un("keydown", this.checkTab, this);
40182         }
40183         this.wrap.removeClass('x-trigger-wrap-focus');
40184         Roo.form.TriggerField.superclass.onBlur.call(this);
40185     },
40186
40187     // private
40188     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40189     validateBlur : function(e, t){
40190         return true;
40191     },
40192
40193     // private
40194     onDisable : function(){
40195         Roo.form.TriggerField.superclass.onDisable.call(this);
40196         if(this.wrap){
40197             this.wrap.addClass('x-item-disabled');
40198         }
40199     },
40200
40201     // private
40202     onEnable : function(){
40203         Roo.form.TriggerField.superclass.onEnable.call(this);
40204         if(this.wrap){
40205             this.wrap.removeClass('x-item-disabled');
40206         }
40207     },
40208
40209     // private
40210     onShow : function(){
40211         var ae = this.getActionEl();
40212         
40213         if(ae){
40214             ae.dom.style.display = '';
40215             ae.dom.style.visibility = 'visible';
40216         }
40217     },
40218
40219     // private
40220     
40221     onHide : function(){
40222         var ae = this.getActionEl();
40223         ae.dom.style.display = 'none';
40224     },
40225
40226     /**
40227      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40228      * by an implementing function.
40229      * @method
40230      * @param {EventObject} e
40231      */
40232     onTriggerClick : Roo.emptyFn
40233 });
40234
40235 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40236 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40237 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40238 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40239     initComponent : function(){
40240         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40241
40242         this.triggerConfig = {
40243             tag:'span', cls:'x-form-twin-triggers', cn:[
40244             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40245             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40246         ]};
40247     },
40248
40249     getTrigger : function(index){
40250         return this.triggers[index];
40251     },
40252
40253     initTrigger : function(){
40254         var ts = this.trigger.select('.x-form-trigger', true);
40255         this.wrap.setStyle('overflow', 'hidden');
40256         var triggerField = this;
40257         ts.each(function(t, all, index){
40258             t.hide = function(){
40259                 var w = triggerField.wrap.getWidth();
40260                 this.dom.style.display = 'none';
40261                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40262             };
40263             t.show = function(){
40264                 var w = triggerField.wrap.getWidth();
40265                 this.dom.style.display = '';
40266                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40267             };
40268             var triggerIndex = 'Trigger'+(index+1);
40269
40270             if(this['hide'+triggerIndex]){
40271                 t.dom.style.display = 'none';
40272             }
40273             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40274             t.addClassOnOver('x-form-trigger-over');
40275             t.addClassOnClick('x-form-trigger-click');
40276         }, this);
40277         this.triggers = ts.elements;
40278     },
40279
40280     onTrigger1Click : Roo.emptyFn,
40281     onTrigger2Click : Roo.emptyFn
40282 });/*
40283  * Based on:
40284  * Ext JS Library 1.1.1
40285  * Copyright(c) 2006-2007, Ext JS, LLC.
40286  *
40287  * Originally Released Under LGPL - original licence link has changed is not relivant.
40288  *
40289  * Fork - LGPL
40290  * <script type="text/javascript">
40291  */
40292  
40293 /**
40294  * @class Roo.form.TextArea
40295  * @extends Roo.form.TextField
40296  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40297  * support for auto-sizing.
40298  * @constructor
40299  * Creates a new TextArea
40300  * @param {Object} config Configuration options
40301  */
40302 Roo.form.TextArea = function(config){
40303     Roo.form.TextArea.superclass.constructor.call(this, config);
40304     // these are provided exchanges for backwards compat
40305     // minHeight/maxHeight were replaced by growMin/growMax to be
40306     // compatible with TextField growing config values
40307     if(this.minHeight !== undefined){
40308         this.growMin = this.minHeight;
40309     }
40310     if(this.maxHeight !== undefined){
40311         this.growMax = this.maxHeight;
40312     }
40313 };
40314
40315 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40316     /**
40317      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40318      */
40319     growMin : 60,
40320     /**
40321      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40322      */
40323     growMax: 1000,
40324     /**
40325      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40326      * in the field (equivalent to setting overflow: hidden, defaults to false)
40327      */
40328     preventScrollbars: false,
40329     /**
40330      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40331      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40332      */
40333
40334     // private
40335     onRender : function(ct, position){
40336         if(!this.el){
40337             this.defaultAutoCreate = {
40338                 tag: "textarea",
40339                 style:"width:300px;height:60px;",
40340                 autocomplete: "new-password"
40341             };
40342         }
40343         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40344         if(this.grow){
40345             this.textSizeEl = Roo.DomHelper.append(document.body, {
40346                 tag: "pre", cls: "x-form-grow-sizer"
40347             });
40348             if(this.preventScrollbars){
40349                 this.el.setStyle("overflow", "hidden");
40350             }
40351             this.el.setHeight(this.growMin);
40352         }
40353     },
40354
40355     onDestroy : function(){
40356         if(this.textSizeEl){
40357             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40358         }
40359         Roo.form.TextArea.superclass.onDestroy.call(this);
40360     },
40361
40362     // private
40363     onKeyUp : function(e){
40364         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40365             this.autoSize();
40366         }
40367     },
40368
40369     /**
40370      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40371      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40372      */
40373     autoSize : function(){
40374         if(!this.grow || !this.textSizeEl){
40375             return;
40376         }
40377         var el = this.el;
40378         var v = el.dom.value;
40379         var ts = this.textSizeEl;
40380
40381         ts.innerHTML = '';
40382         ts.appendChild(document.createTextNode(v));
40383         v = ts.innerHTML;
40384
40385         Roo.fly(ts).setWidth(this.el.getWidth());
40386         if(v.length < 1){
40387             v = "&#160;&#160;";
40388         }else{
40389             if(Roo.isIE){
40390                 v = v.replace(/\n/g, '<p>&#160;</p>');
40391             }
40392             v += "&#160;\n&#160;";
40393         }
40394         ts.innerHTML = v;
40395         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40396         if(h != this.lastHeight){
40397             this.lastHeight = h;
40398             this.el.setHeight(h);
40399             this.fireEvent("autosize", this, h);
40400         }
40401     }
40402 });/*
40403  * Based on:
40404  * Ext JS Library 1.1.1
40405  * Copyright(c) 2006-2007, Ext JS, LLC.
40406  *
40407  * Originally Released Under LGPL - original licence link has changed is not relivant.
40408  *
40409  * Fork - LGPL
40410  * <script type="text/javascript">
40411  */
40412  
40413
40414 /**
40415  * @class Roo.form.NumberField
40416  * @extends Roo.form.TextField
40417  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40418  * @constructor
40419  * Creates a new NumberField
40420  * @param {Object} config Configuration options
40421  */
40422 Roo.form.NumberField = function(config){
40423     Roo.form.NumberField.superclass.constructor.call(this, config);
40424 };
40425
40426 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40427     /**
40428      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40429      */
40430     fieldClass: "x-form-field x-form-num-field",
40431     /**
40432      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40433      */
40434     allowDecimals : true,
40435     /**
40436      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40437      */
40438     decimalSeparator : ".",
40439     /**
40440      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40441      */
40442     decimalPrecision : 2,
40443     /**
40444      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40445      */
40446     allowNegative : true,
40447     /**
40448      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40449      */
40450     minValue : Number.NEGATIVE_INFINITY,
40451     /**
40452      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40453      */
40454     maxValue : Number.MAX_VALUE,
40455     /**
40456      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40457      */
40458     minText : "The minimum value for this field is {0}",
40459     /**
40460      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40461      */
40462     maxText : "The maximum value for this field is {0}",
40463     /**
40464      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40465      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40466      */
40467     nanText : "{0} is not a valid number",
40468
40469     // private
40470     initEvents : function(){
40471         Roo.form.NumberField.superclass.initEvents.call(this);
40472         var allowed = "0123456789";
40473         if(this.allowDecimals){
40474             allowed += this.decimalSeparator;
40475         }
40476         if(this.allowNegative){
40477             allowed += "-";
40478         }
40479         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40480         var keyPress = function(e){
40481             var k = e.getKey();
40482             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40483                 return;
40484             }
40485             var c = e.getCharCode();
40486             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40487                 e.stopEvent();
40488             }
40489         };
40490         this.el.on("keypress", keyPress, this);
40491     },
40492
40493     // private
40494     validateValue : function(value){
40495         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40496             return false;
40497         }
40498         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40499              return true;
40500         }
40501         var num = this.parseValue(value);
40502         if(isNaN(num)){
40503             this.markInvalid(String.format(this.nanText, value));
40504             return false;
40505         }
40506         if(num < this.minValue){
40507             this.markInvalid(String.format(this.minText, this.minValue));
40508             return false;
40509         }
40510         if(num > this.maxValue){
40511             this.markInvalid(String.format(this.maxText, this.maxValue));
40512             return false;
40513         }
40514         return true;
40515     },
40516
40517     getValue : function(){
40518         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40519     },
40520
40521     // private
40522     parseValue : function(value){
40523         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40524         return isNaN(value) ? '' : value;
40525     },
40526
40527     // private
40528     fixPrecision : function(value){
40529         var nan = isNaN(value);
40530         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40531             return nan ? '' : value;
40532         }
40533         return parseFloat(value).toFixed(this.decimalPrecision);
40534     },
40535
40536     setValue : function(v){
40537         v = this.fixPrecision(v);
40538         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40539     },
40540
40541     // private
40542     decimalPrecisionFcn : function(v){
40543         return Math.floor(v);
40544     },
40545
40546     beforeBlur : function(){
40547         var v = this.parseValue(this.getRawValue());
40548         if(v){
40549             this.setValue(v);
40550         }
40551     }
40552 });/*
40553  * Based on:
40554  * Ext JS Library 1.1.1
40555  * Copyright(c) 2006-2007, Ext JS, LLC.
40556  *
40557  * Originally Released Under LGPL - original licence link has changed is not relivant.
40558  *
40559  * Fork - LGPL
40560  * <script type="text/javascript">
40561  */
40562  
40563 /**
40564  * @class Roo.form.DateField
40565  * @extends Roo.form.TriggerField
40566  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40567 * @constructor
40568 * Create a new DateField
40569 * @param {Object} config
40570  */
40571 Roo.form.DateField = function(config)
40572 {
40573     Roo.form.DateField.superclass.constructor.call(this, config);
40574     
40575       this.addEvents({
40576          
40577         /**
40578          * @event select
40579          * Fires when a date is selected
40580              * @param {Roo.form.DateField} combo This combo box
40581              * @param {Date} date The date selected
40582              */
40583         'select' : true
40584          
40585     });
40586     
40587     
40588     if(typeof this.minValue == "string") {
40589         this.minValue = this.parseDate(this.minValue);
40590     }
40591     if(typeof this.maxValue == "string") {
40592         this.maxValue = this.parseDate(this.maxValue);
40593     }
40594     this.ddMatch = null;
40595     if(this.disabledDates){
40596         var dd = this.disabledDates;
40597         var re = "(?:";
40598         for(var i = 0; i < dd.length; i++){
40599             re += dd[i];
40600             if(i != dd.length-1) {
40601                 re += "|";
40602             }
40603         }
40604         this.ddMatch = new RegExp(re + ")");
40605     }
40606 };
40607
40608 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40609     /**
40610      * @cfg {String} format
40611      * The default date format string which can be overriden for localization support.  The format must be
40612      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40613      */
40614     format : "m/d/y",
40615     /**
40616      * @cfg {String} altFormats
40617      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40618      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40619      */
40620     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40621     /**
40622      * @cfg {Array} disabledDays
40623      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40624      */
40625     disabledDays : null,
40626     /**
40627      * @cfg {String} disabledDaysText
40628      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40629      */
40630     disabledDaysText : "Disabled",
40631     /**
40632      * @cfg {Array} disabledDates
40633      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40634      * expression so they are very powerful. Some examples:
40635      * <ul>
40636      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40637      * <li>["03/08", "09/16"] would disable those days for every year</li>
40638      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40639      * <li>["03/../2006"] would disable every day in March 2006</li>
40640      * <li>["^03"] would disable every day in every March</li>
40641      * </ul>
40642      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40643      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40644      */
40645     disabledDates : null,
40646     /**
40647      * @cfg {String} disabledDatesText
40648      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40649      */
40650     disabledDatesText : "Disabled",
40651     /**
40652      * @cfg {Date/String} minValue
40653      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40654      * valid format (defaults to null).
40655      */
40656     minValue : null,
40657     /**
40658      * @cfg {Date/String} maxValue
40659      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40660      * valid format (defaults to null).
40661      */
40662     maxValue : null,
40663     /**
40664      * @cfg {String} minText
40665      * The error text to display when the date in the cell is before minValue (defaults to
40666      * 'The date in this field must be after {minValue}').
40667      */
40668     minText : "The date in this field must be equal to or after {0}",
40669     /**
40670      * @cfg {String} maxText
40671      * The error text to display when the date in the cell is after maxValue (defaults to
40672      * 'The date in this field must be before {maxValue}').
40673      */
40674     maxText : "The date in this field must be equal to or before {0}",
40675     /**
40676      * @cfg {String} invalidText
40677      * The error text to display when the date in the field is invalid (defaults to
40678      * '{value} is not a valid date - it must be in the format {format}').
40679      */
40680     invalidText : "{0} is not a valid date - it must be in the format {1}",
40681     /**
40682      * @cfg {String} triggerClass
40683      * An additional CSS class used to style the trigger button.  The trigger will always get the
40684      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40685      * which displays a calendar icon).
40686      */
40687     triggerClass : 'x-form-date-trigger',
40688     
40689
40690     /**
40691      * @cfg {Boolean} useIso
40692      * if enabled, then the date field will use a hidden field to store the 
40693      * real value as iso formated date. default (false)
40694      */ 
40695     useIso : false,
40696     /**
40697      * @cfg {String/Object} autoCreate
40698      * A DomHelper element spec, or true for a default element spec (defaults to
40699      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40700      */ 
40701     // private
40702     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40703     
40704     // private
40705     hiddenField: false,
40706     
40707     onRender : function(ct, position)
40708     {
40709         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40710         if (this.useIso) {
40711             //this.el.dom.removeAttribute('name'); 
40712             Roo.log("Changing name?");
40713             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40714             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40715                     'before', true);
40716             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40717             // prevent input submission
40718             this.hiddenName = this.name;
40719         }
40720             
40721             
40722     },
40723     
40724     // private
40725     validateValue : function(value)
40726     {
40727         value = this.formatDate(value);
40728         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40729             Roo.log('super failed');
40730             return false;
40731         }
40732         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40733              return true;
40734         }
40735         var svalue = value;
40736         value = this.parseDate(value);
40737         if(!value){
40738             Roo.log('parse date failed' + svalue);
40739             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40740             return false;
40741         }
40742         var time = value.getTime();
40743         if(this.minValue && time < this.minValue.getTime()){
40744             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40745             return false;
40746         }
40747         if(this.maxValue && time > this.maxValue.getTime()){
40748             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40749             return false;
40750         }
40751         if(this.disabledDays){
40752             var day = value.getDay();
40753             for(var i = 0; i < this.disabledDays.length; i++) {
40754                 if(day === this.disabledDays[i]){
40755                     this.markInvalid(this.disabledDaysText);
40756                     return false;
40757                 }
40758             }
40759         }
40760         var fvalue = this.formatDate(value);
40761         if(this.ddMatch && this.ddMatch.test(fvalue)){
40762             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40763             return false;
40764         }
40765         return true;
40766     },
40767
40768     // private
40769     // Provides logic to override the default TriggerField.validateBlur which just returns true
40770     validateBlur : function(){
40771         return !this.menu || !this.menu.isVisible();
40772     },
40773     
40774     getName: function()
40775     {
40776         // returns hidden if it's set..
40777         if (!this.rendered) {return ''};
40778         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40779         
40780     },
40781
40782     /**
40783      * Returns the current date value of the date field.
40784      * @return {Date} The date value
40785      */
40786     getValue : function(){
40787         
40788         return  this.hiddenField ?
40789                 this.hiddenField.value :
40790                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40791     },
40792
40793     /**
40794      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40795      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40796      * (the default format used is "m/d/y").
40797      * <br />Usage:
40798      * <pre><code>
40799 //All of these calls set the same date value (May 4, 2006)
40800
40801 //Pass a date object:
40802 var dt = new Date('5/4/06');
40803 dateField.setValue(dt);
40804
40805 //Pass a date string (default format):
40806 dateField.setValue('5/4/06');
40807
40808 //Pass a date string (custom format):
40809 dateField.format = 'Y-m-d';
40810 dateField.setValue('2006-5-4');
40811 </code></pre>
40812      * @param {String/Date} date The date or valid date string
40813      */
40814     setValue : function(date){
40815         if (this.hiddenField) {
40816             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40817         }
40818         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40819         // make sure the value field is always stored as a date..
40820         this.value = this.parseDate(date);
40821         
40822         
40823     },
40824
40825     // private
40826     parseDate : function(value){
40827         if(!value || value instanceof Date){
40828             return value;
40829         }
40830         var v = Date.parseDate(value, this.format);
40831          if (!v && this.useIso) {
40832             v = Date.parseDate(value, 'Y-m-d');
40833         }
40834         if(!v && this.altFormats){
40835             if(!this.altFormatsArray){
40836                 this.altFormatsArray = this.altFormats.split("|");
40837             }
40838             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40839                 v = Date.parseDate(value, this.altFormatsArray[i]);
40840             }
40841         }
40842         return v;
40843     },
40844
40845     // private
40846     formatDate : function(date, fmt){
40847         return (!date || !(date instanceof Date)) ?
40848                date : date.dateFormat(fmt || this.format);
40849     },
40850
40851     // private
40852     menuListeners : {
40853         select: function(m, d){
40854             
40855             this.setValue(d);
40856             this.fireEvent('select', this, d);
40857         },
40858         show : function(){ // retain focus styling
40859             this.onFocus();
40860         },
40861         hide : function(){
40862             this.focus.defer(10, this);
40863             var ml = this.menuListeners;
40864             this.menu.un("select", ml.select,  this);
40865             this.menu.un("show", ml.show,  this);
40866             this.menu.un("hide", ml.hide,  this);
40867         }
40868     },
40869
40870     // private
40871     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40872     onTriggerClick : function(){
40873         if(this.disabled){
40874             return;
40875         }
40876         if(this.menu == null){
40877             this.menu = new Roo.menu.DateMenu();
40878         }
40879         Roo.apply(this.menu.picker,  {
40880             showClear: this.allowBlank,
40881             minDate : this.minValue,
40882             maxDate : this.maxValue,
40883             disabledDatesRE : this.ddMatch,
40884             disabledDatesText : this.disabledDatesText,
40885             disabledDays : this.disabledDays,
40886             disabledDaysText : this.disabledDaysText,
40887             format : this.useIso ? 'Y-m-d' : this.format,
40888             minText : String.format(this.minText, this.formatDate(this.minValue)),
40889             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40890         });
40891         this.menu.on(Roo.apply({}, this.menuListeners, {
40892             scope:this
40893         }));
40894         this.menu.picker.setValue(this.getValue() || new Date());
40895         this.menu.show(this.el, "tl-bl?");
40896     },
40897
40898     beforeBlur : function(){
40899         var v = this.parseDate(this.getRawValue());
40900         if(v){
40901             this.setValue(v);
40902         }
40903     },
40904
40905     /*@
40906      * overide
40907      * 
40908      */
40909     isDirty : function() {
40910         if(this.disabled) {
40911             return false;
40912         }
40913         
40914         if(typeof(this.startValue) === 'undefined'){
40915             return false;
40916         }
40917         
40918         return String(this.getValue()) !== String(this.startValue);
40919         
40920     },
40921     // @overide
40922     cleanLeadingSpace : function(e)
40923     {
40924        return;
40925     }
40926     
40927 });/*
40928  * Based on:
40929  * Ext JS Library 1.1.1
40930  * Copyright(c) 2006-2007, Ext JS, LLC.
40931  *
40932  * Originally Released Under LGPL - original licence link has changed is not relivant.
40933  *
40934  * Fork - LGPL
40935  * <script type="text/javascript">
40936  */
40937  
40938 /**
40939  * @class Roo.form.MonthField
40940  * @extends Roo.form.TriggerField
40941  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40942 * @constructor
40943 * Create a new MonthField
40944 * @param {Object} config
40945  */
40946 Roo.form.MonthField = function(config){
40947     
40948     Roo.form.MonthField.superclass.constructor.call(this, config);
40949     
40950       this.addEvents({
40951          
40952         /**
40953          * @event select
40954          * Fires when a date is selected
40955              * @param {Roo.form.MonthFieeld} combo This combo box
40956              * @param {Date} date The date selected
40957              */
40958         'select' : true
40959          
40960     });
40961     
40962     
40963     if(typeof this.minValue == "string") {
40964         this.minValue = this.parseDate(this.minValue);
40965     }
40966     if(typeof this.maxValue == "string") {
40967         this.maxValue = this.parseDate(this.maxValue);
40968     }
40969     this.ddMatch = null;
40970     if(this.disabledDates){
40971         var dd = this.disabledDates;
40972         var re = "(?:";
40973         for(var i = 0; i < dd.length; i++){
40974             re += dd[i];
40975             if(i != dd.length-1) {
40976                 re += "|";
40977             }
40978         }
40979         this.ddMatch = new RegExp(re + ")");
40980     }
40981 };
40982
40983 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40984     /**
40985      * @cfg {String} format
40986      * The default date format string which can be overriden for localization support.  The format must be
40987      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40988      */
40989     format : "M Y",
40990     /**
40991      * @cfg {String} altFormats
40992      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40993      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40994      */
40995     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40996     /**
40997      * @cfg {Array} disabledDays
40998      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40999      */
41000     disabledDays : [0,1,2,3,4,5,6],
41001     /**
41002      * @cfg {String} disabledDaysText
41003      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41004      */
41005     disabledDaysText : "Disabled",
41006     /**
41007      * @cfg {Array} disabledDates
41008      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41009      * expression so they are very powerful. Some examples:
41010      * <ul>
41011      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41012      * <li>["03/08", "09/16"] would disable those days for every year</li>
41013      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41014      * <li>["03/../2006"] would disable every day in March 2006</li>
41015      * <li>["^03"] would disable every day in every March</li>
41016      * </ul>
41017      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41018      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41019      */
41020     disabledDates : null,
41021     /**
41022      * @cfg {String} disabledDatesText
41023      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41024      */
41025     disabledDatesText : "Disabled",
41026     /**
41027      * @cfg {Date/String} minValue
41028      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41029      * valid format (defaults to null).
41030      */
41031     minValue : null,
41032     /**
41033      * @cfg {Date/String} maxValue
41034      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41035      * valid format (defaults to null).
41036      */
41037     maxValue : null,
41038     /**
41039      * @cfg {String} minText
41040      * The error text to display when the date in the cell is before minValue (defaults to
41041      * 'The date in this field must be after {minValue}').
41042      */
41043     minText : "The date in this field must be equal to or after {0}",
41044     /**
41045      * @cfg {String} maxTextf
41046      * The error text to display when the date in the cell is after maxValue (defaults to
41047      * 'The date in this field must be before {maxValue}').
41048      */
41049     maxText : "The date in this field must be equal to or before {0}",
41050     /**
41051      * @cfg {String} invalidText
41052      * The error text to display when the date in the field is invalid (defaults to
41053      * '{value} is not a valid date - it must be in the format {format}').
41054      */
41055     invalidText : "{0} is not a valid date - it must be in the format {1}",
41056     /**
41057      * @cfg {String} triggerClass
41058      * An additional CSS class used to style the trigger button.  The trigger will always get the
41059      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41060      * which displays a calendar icon).
41061      */
41062     triggerClass : 'x-form-date-trigger',
41063     
41064
41065     /**
41066      * @cfg {Boolean} useIso
41067      * if enabled, then the date field will use a hidden field to store the 
41068      * real value as iso formated date. default (true)
41069      */ 
41070     useIso : true,
41071     /**
41072      * @cfg {String/Object} autoCreate
41073      * A DomHelper element spec, or true for a default element spec (defaults to
41074      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41075      */ 
41076     // private
41077     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41078     
41079     // private
41080     hiddenField: false,
41081     
41082     hideMonthPicker : false,
41083     
41084     onRender : function(ct, position)
41085     {
41086         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41087         if (this.useIso) {
41088             this.el.dom.removeAttribute('name'); 
41089             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41090                     'before', true);
41091             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41092             // prevent input submission
41093             this.hiddenName = this.name;
41094         }
41095             
41096             
41097     },
41098     
41099     // private
41100     validateValue : function(value)
41101     {
41102         value = this.formatDate(value);
41103         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41104             return false;
41105         }
41106         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41107              return true;
41108         }
41109         var svalue = value;
41110         value = this.parseDate(value);
41111         if(!value){
41112             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41113             return false;
41114         }
41115         var time = value.getTime();
41116         if(this.minValue && time < this.minValue.getTime()){
41117             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41118             return false;
41119         }
41120         if(this.maxValue && time > this.maxValue.getTime()){
41121             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41122             return false;
41123         }
41124         /*if(this.disabledDays){
41125             var day = value.getDay();
41126             for(var i = 0; i < this.disabledDays.length; i++) {
41127                 if(day === this.disabledDays[i]){
41128                     this.markInvalid(this.disabledDaysText);
41129                     return false;
41130                 }
41131             }
41132         }
41133         */
41134         var fvalue = this.formatDate(value);
41135         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41136             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41137             return false;
41138         }
41139         */
41140         return true;
41141     },
41142
41143     // private
41144     // Provides logic to override the default TriggerField.validateBlur which just returns true
41145     validateBlur : function(){
41146         return !this.menu || !this.menu.isVisible();
41147     },
41148
41149     /**
41150      * Returns the current date value of the date field.
41151      * @return {Date} The date value
41152      */
41153     getValue : function(){
41154         
41155         
41156         
41157         return  this.hiddenField ?
41158                 this.hiddenField.value :
41159                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41160     },
41161
41162     /**
41163      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41164      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41165      * (the default format used is "m/d/y").
41166      * <br />Usage:
41167      * <pre><code>
41168 //All of these calls set the same date value (May 4, 2006)
41169
41170 //Pass a date object:
41171 var dt = new Date('5/4/06');
41172 monthField.setValue(dt);
41173
41174 //Pass a date string (default format):
41175 monthField.setValue('5/4/06');
41176
41177 //Pass a date string (custom format):
41178 monthField.format = 'Y-m-d';
41179 monthField.setValue('2006-5-4');
41180 </code></pre>
41181      * @param {String/Date} date The date or valid date string
41182      */
41183     setValue : function(date){
41184         Roo.log('month setValue' + date);
41185         // can only be first of month..
41186         
41187         var val = this.parseDate(date);
41188         
41189         if (this.hiddenField) {
41190             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41191         }
41192         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41193         this.value = this.parseDate(date);
41194     },
41195
41196     // private
41197     parseDate : function(value){
41198         if(!value || value instanceof Date){
41199             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41200             return value;
41201         }
41202         var v = Date.parseDate(value, this.format);
41203         if (!v && this.useIso) {
41204             v = Date.parseDate(value, 'Y-m-d');
41205         }
41206         if (v) {
41207             // 
41208             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41209         }
41210         
41211         
41212         if(!v && this.altFormats){
41213             if(!this.altFormatsArray){
41214                 this.altFormatsArray = this.altFormats.split("|");
41215             }
41216             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41217                 v = Date.parseDate(value, this.altFormatsArray[i]);
41218             }
41219         }
41220         return v;
41221     },
41222
41223     // private
41224     formatDate : function(date, fmt){
41225         return (!date || !(date instanceof Date)) ?
41226                date : date.dateFormat(fmt || this.format);
41227     },
41228
41229     // private
41230     menuListeners : {
41231         select: function(m, d){
41232             this.setValue(d);
41233             this.fireEvent('select', this, d);
41234         },
41235         show : function(){ // retain focus styling
41236             this.onFocus();
41237         },
41238         hide : function(){
41239             this.focus.defer(10, this);
41240             var ml = this.menuListeners;
41241             this.menu.un("select", ml.select,  this);
41242             this.menu.un("show", ml.show,  this);
41243             this.menu.un("hide", ml.hide,  this);
41244         }
41245     },
41246     // private
41247     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41248     onTriggerClick : function(){
41249         if(this.disabled){
41250             return;
41251         }
41252         if(this.menu == null){
41253             this.menu = new Roo.menu.DateMenu();
41254            
41255         }
41256         
41257         Roo.apply(this.menu.picker,  {
41258             
41259             showClear: this.allowBlank,
41260             minDate : this.minValue,
41261             maxDate : this.maxValue,
41262             disabledDatesRE : this.ddMatch,
41263             disabledDatesText : this.disabledDatesText,
41264             
41265             format : this.useIso ? 'Y-m-d' : this.format,
41266             minText : String.format(this.minText, this.formatDate(this.minValue)),
41267             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41268             
41269         });
41270          this.menu.on(Roo.apply({}, this.menuListeners, {
41271             scope:this
41272         }));
41273        
41274         
41275         var m = this.menu;
41276         var p = m.picker;
41277         
41278         // hide month picker get's called when we called by 'before hide';
41279         
41280         var ignorehide = true;
41281         p.hideMonthPicker  = function(disableAnim){
41282             if (ignorehide) {
41283                 return;
41284             }
41285              if(this.monthPicker){
41286                 Roo.log("hideMonthPicker called");
41287                 if(disableAnim === true){
41288                     this.monthPicker.hide();
41289                 }else{
41290                     this.monthPicker.slideOut('t', {duration:.2});
41291                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41292                     p.fireEvent("select", this, this.value);
41293                     m.hide();
41294                 }
41295             }
41296         }
41297         
41298         Roo.log('picker set value');
41299         Roo.log(this.getValue());
41300         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41301         m.show(this.el, 'tl-bl?');
41302         ignorehide  = false;
41303         // this will trigger hideMonthPicker..
41304         
41305         
41306         // hidden the day picker
41307         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41308         
41309         
41310         
41311       
41312         
41313         p.showMonthPicker.defer(100, p);
41314     
41315         
41316        
41317     },
41318
41319     beforeBlur : function(){
41320         var v = this.parseDate(this.getRawValue());
41321         if(v){
41322             this.setValue(v);
41323         }
41324     }
41325
41326     /** @cfg {Boolean} grow @hide */
41327     /** @cfg {Number} growMin @hide */
41328     /** @cfg {Number} growMax @hide */
41329     /**
41330      * @hide
41331      * @method autoSize
41332      */
41333 });/*
41334  * Based on:
41335  * Ext JS Library 1.1.1
41336  * Copyright(c) 2006-2007, Ext JS, LLC.
41337  *
41338  * Originally Released Under LGPL - original licence link has changed is not relivant.
41339  *
41340  * Fork - LGPL
41341  * <script type="text/javascript">
41342  */
41343  
41344
41345 /**
41346  * @class Roo.form.ComboBox
41347  * @extends Roo.form.TriggerField
41348  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41349  * @constructor
41350  * Create a new ComboBox.
41351  * @param {Object} config Configuration options
41352  */
41353 Roo.form.ComboBox = function(config){
41354     Roo.form.ComboBox.superclass.constructor.call(this, config);
41355     this.addEvents({
41356         /**
41357          * @event expand
41358          * Fires when the dropdown list is expanded
41359              * @param {Roo.form.ComboBox} combo This combo box
41360              */
41361         'expand' : true,
41362         /**
41363          * @event collapse
41364          * Fires when the dropdown list is collapsed
41365              * @param {Roo.form.ComboBox} combo This combo box
41366              */
41367         'collapse' : true,
41368         /**
41369          * @event beforeselect
41370          * Fires before a list item is selected. Return false to cancel the selection.
41371              * @param {Roo.form.ComboBox} combo This combo box
41372              * @param {Roo.data.Record} record The data record returned from the underlying store
41373              * @param {Number} index The index of the selected item in the dropdown list
41374              */
41375         'beforeselect' : true,
41376         /**
41377          * @event select
41378          * Fires when a list item is selected
41379              * @param {Roo.form.ComboBox} combo This combo box
41380              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41381              * @param {Number} index The index of the selected item in the dropdown list
41382              */
41383         'select' : true,
41384         /**
41385          * @event beforequery
41386          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41387          * The event object passed has these properties:
41388              * @param {Roo.form.ComboBox} combo This combo box
41389              * @param {String} query The query
41390              * @param {Boolean} forceAll true to force "all" query
41391              * @param {Boolean} cancel true to cancel the query
41392              * @param {Object} e The query event object
41393              */
41394         'beforequery': true,
41395          /**
41396          * @event add
41397          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41398              * @param {Roo.form.ComboBox} combo This combo box
41399              */
41400         'add' : true,
41401         /**
41402          * @event edit
41403          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41404              * @param {Roo.form.ComboBox} combo This combo box
41405              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41406              */
41407         'edit' : true
41408         
41409         
41410     });
41411     if(this.transform){
41412         this.allowDomMove = false;
41413         var s = Roo.getDom(this.transform);
41414         if(!this.hiddenName){
41415             this.hiddenName = s.name;
41416         }
41417         if(!this.store){
41418             this.mode = 'local';
41419             var d = [], opts = s.options;
41420             for(var i = 0, len = opts.length;i < len; i++){
41421                 var o = opts[i];
41422                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41423                 if(o.selected) {
41424                     this.value = value;
41425                 }
41426                 d.push([value, o.text]);
41427             }
41428             this.store = new Roo.data.SimpleStore({
41429                 'id': 0,
41430                 fields: ['value', 'text'],
41431                 data : d
41432             });
41433             this.valueField = 'value';
41434             this.displayField = 'text';
41435         }
41436         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41437         if(!this.lazyRender){
41438             this.target = true;
41439             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41440             s.parentNode.removeChild(s); // remove it
41441             this.render(this.el.parentNode);
41442         }else{
41443             s.parentNode.removeChild(s); // remove it
41444         }
41445
41446     }
41447     if (this.store) {
41448         this.store = Roo.factory(this.store, Roo.data);
41449     }
41450     
41451     this.selectedIndex = -1;
41452     if(this.mode == 'local'){
41453         if(config.queryDelay === undefined){
41454             this.queryDelay = 10;
41455         }
41456         if(config.minChars === undefined){
41457             this.minChars = 0;
41458         }
41459     }
41460 };
41461
41462 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41463     /**
41464      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41465      */
41466     /**
41467      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41468      * rendering into an Roo.Editor, defaults to false)
41469      */
41470     /**
41471      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41472      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41473      */
41474     /**
41475      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41476      */
41477     /**
41478      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41479      * the dropdown list (defaults to undefined, with no header element)
41480      */
41481
41482      /**
41483      * @cfg {String/Roo.Template} tpl The template to use to render the output
41484      */
41485      
41486     // private
41487     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41488     /**
41489      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41490      */
41491     listWidth: undefined,
41492     /**
41493      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41494      * mode = 'remote' or 'text' if mode = 'local')
41495      */
41496     displayField: undefined,
41497     /**
41498      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41499      * mode = 'remote' or 'value' if mode = 'local'). 
41500      * Note: use of a valueField requires the user make a selection
41501      * in order for a value to be mapped.
41502      */
41503     valueField: undefined,
41504     
41505     
41506     /**
41507      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41508      * field's data value (defaults to the underlying DOM element's name)
41509      */
41510     hiddenName: undefined,
41511     /**
41512      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41513      */
41514     listClass: '',
41515     /**
41516      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41517      */
41518     selectedClass: 'x-combo-selected',
41519     /**
41520      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41521      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41522      * which displays a downward arrow icon).
41523      */
41524     triggerClass : 'x-form-arrow-trigger',
41525     /**
41526      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41527      */
41528     shadow:'sides',
41529     /**
41530      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41531      * anchor positions (defaults to 'tl-bl')
41532      */
41533     listAlign: 'tl-bl?',
41534     /**
41535      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41536      */
41537     maxHeight: 300,
41538     /**
41539      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41540      * query specified by the allQuery config option (defaults to 'query')
41541      */
41542     triggerAction: 'query',
41543     /**
41544      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41545      * (defaults to 4, does not apply if editable = false)
41546      */
41547     minChars : 4,
41548     /**
41549      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41550      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41551      */
41552     typeAhead: false,
41553     /**
41554      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41555      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41556      */
41557     queryDelay: 500,
41558     /**
41559      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41560      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41561      */
41562     pageSize: 0,
41563     /**
41564      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41565      * when editable = true (defaults to false)
41566      */
41567     selectOnFocus:false,
41568     /**
41569      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41570      */
41571     queryParam: 'query',
41572     /**
41573      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41574      * when mode = 'remote' (defaults to 'Loading...')
41575      */
41576     loadingText: 'Loading...',
41577     /**
41578      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41579      */
41580     resizable: false,
41581     /**
41582      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41583      */
41584     handleHeight : 8,
41585     /**
41586      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41587      * traditional select (defaults to true)
41588      */
41589     editable: true,
41590     /**
41591      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41592      */
41593     allQuery: '',
41594     /**
41595      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41596      */
41597     mode: 'remote',
41598     /**
41599      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41600      * listWidth has a higher value)
41601      */
41602     minListWidth : 70,
41603     /**
41604      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41605      * allow the user to set arbitrary text into the field (defaults to false)
41606      */
41607     forceSelection:false,
41608     /**
41609      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41610      * if typeAhead = true (defaults to 250)
41611      */
41612     typeAheadDelay : 250,
41613     /**
41614      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41615      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41616      */
41617     valueNotFoundText : undefined,
41618     /**
41619      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41620      */
41621     blockFocus : false,
41622     
41623     /**
41624      * @cfg {Boolean} disableClear Disable showing of clear button.
41625      */
41626     disableClear : false,
41627     /**
41628      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41629      */
41630     alwaysQuery : false,
41631     
41632     //private
41633     addicon : false,
41634     editicon: false,
41635     
41636     // element that contains real text value.. (when hidden is used..)
41637      
41638     // private
41639     onRender : function(ct, position)
41640     {
41641         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41642         
41643         if(this.hiddenName){
41644             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41645                     'before', true);
41646             this.hiddenField.value =
41647                 this.hiddenValue !== undefined ? this.hiddenValue :
41648                 this.value !== undefined ? this.value : '';
41649
41650             // prevent input submission
41651             this.el.dom.removeAttribute('name');
41652              
41653              
41654         }
41655         
41656         if(Roo.isGecko){
41657             this.el.dom.setAttribute('autocomplete', 'off');
41658         }
41659
41660         var cls = 'x-combo-list';
41661
41662         this.list = new Roo.Layer({
41663             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41664         });
41665
41666         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41667         this.list.setWidth(lw);
41668         this.list.swallowEvent('mousewheel');
41669         this.assetHeight = 0;
41670
41671         if(this.title){
41672             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41673             this.assetHeight += this.header.getHeight();
41674         }
41675
41676         this.innerList = this.list.createChild({cls:cls+'-inner'});
41677         this.innerList.on('mouseover', this.onViewOver, this);
41678         this.innerList.on('mousemove', this.onViewMove, this);
41679         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41680         
41681         if(this.allowBlank && !this.pageSize && !this.disableClear){
41682             this.footer = this.list.createChild({cls:cls+'-ft'});
41683             this.pageTb = new Roo.Toolbar(this.footer);
41684            
41685         }
41686         if(this.pageSize){
41687             this.footer = this.list.createChild({cls:cls+'-ft'});
41688             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41689                     {pageSize: this.pageSize});
41690             
41691         }
41692         
41693         if (this.pageTb && this.allowBlank && !this.disableClear) {
41694             var _this = this;
41695             this.pageTb.add(new Roo.Toolbar.Fill(), {
41696                 cls: 'x-btn-icon x-btn-clear',
41697                 text: '&#160;',
41698                 handler: function()
41699                 {
41700                     _this.collapse();
41701                     _this.clearValue();
41702                     _this.onSelect(false, -1);
41703                 }
41704             });
41705         }
41706         if (this.footer) {
41707             this.assetHeight += this.footer.getHeight();
41708         }
41709         
41710
41711         if(!this.tpl){
41712             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41713         }
41714
41715         this.view = new Roo.View(this.innerList, this.tpl, {
41716             singleSelect:true,
41717             store: this.store,
41718             selectedClass: this.selectedClass
41719         });
41720
41721         this.view.on('click', this.onViewClick, this);
41722
41723         this.store.on('beforeload', this.onBeforeLoad, this);
41724         this.store.on('load', this.onLoad, this);
41725         this.store.on('loadexception', this.onLoadException, this);
41726
41727         if(this.resizable){
41728             this.resizer = new Roo.Resizable(this.list,  {
41729                pinned:true, handles:'se'
41730             });
41731             this.resizer.on('resize', function(r, w, h){
41732                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41733                 this.listWidth = w;
41734                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41735                 this.restrictHeight();
41736             }, this);
41737             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41738         }
41739         if(!this.editable){
41740             this.editable = true;
41741             this.setEditable(false);
41742         }  
41743         
41744         
41745         if (typeof(this.events.add.listeners) != 'undefined') {
41746             
41747             this.addicon = this.wrap.createChild(
41748                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41749        
41750             this.addicon.on('click', function(e) {
41751                 this.fireEvent('add', this);
41752             }, this);
41753         }
41754         if (typeof(this.events.edit.listeners) != 'undefined') {
41755             
41756             this.editicon = this.wrap.createChild(
41757                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41758             if (this.addicon) {
41759                 this.editicon.setStyle('margin-left', '40px');
41760             }
41761             this.editicon.on('click', function(e) {
41762                 
41763                 // we fire even  if inothing is selected..
41764                 this.fireEvent('edit', this, this.lastData );
41765                 
41766             }, this);
41767         }
41768         
41769         
41770         
41771     },
41772
41773     // private
41774     initEvents : function(){
41775         Roo.form.ComboBox.superclass.initEvents.call(this);
41776
41777         this.keyNav = new Roo.KeyNav(this.el, {
41778             "up" : function(e){
41779                 this.inKeyMode = true;
41780                 this.selectPrev();
41781             },
41782
41783             "down" : function(e){
41784                 if(!this.isExpanded()){
41785                     this.onTriggerClick();
41786                 }else{
41787                     this.inKeyMode = true;
41788                     this.selectNext();
41789                 }
41790             },
41791
41792             "enter" : function(e){
41793                 this.onViewClick();
41794                 //return true;
41795             },
41796
41797             "esc" : function(e){
41798                 this.collapse();
41799             },
41800
41801             "tab" : function(e){
41802                 this.onViewClick(false);
41803                 this.fireEvent("specialkey", this, e);
41804                 return true;
41805             },
41806
41807             scope : this,
41808
41809             doRelay : function(foo, bar, hname){
41810                 if(hname == 'down' || this.scope.isExpanded()){
41811                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41812                 }
41813                 return true;
41814             },
41815
41816             forceKeyDown: true
41817         });
41818         this.queryDelay = Math.max(this.queryDelay || 10,
41819                 this.mode == 'local' ? 10 : 250);
41820         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41821         if(this.typeAhead){
41822             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41823         }
41824         if(this.editable !== false){
41825             this.el.on("keyup", this.onKeyUp, this);
41826         }
41827         if(this.forceSelection){
41828             this.on('blur', this.doForce, this);
41829         }
41830     },
41831
41832     onDestroy : function(){
41833         if(this.view){
41834             this.view.setStore(null);
41835             this.view.el.removeAllListeners();
41836             this.view.el.remove();
41837             this.view.purgeListeners();
41838         }
41839         if(this.list){
41840             this.list.destroy();
41841         }
41842         if(this.store){
41843             this.store.un('beforeload', this.onBeforeLoad, this);
41844             this.store.un('load', this.onLoad, this);
41845             this.store.un('loadexception', this.onLoadException, this);
41846         }
41847         Roo.form.ComboBox.superclass.onDestroy.call(this);
41848     },
41849
41850     // private
41851     fireKey : function(e){
41852         if(e.isNavKeyPress() && !this.list.isVisible()){
41853             this.fireEvent("specialkey", this, e);
41854         }
41855     },
41856
41857     // private
41858     onResize: function(w, h){
41859         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41860         
41861         if(typeof w != 'number'){
41862             // we do not handle it!?!?
41863             return;
41864         }
41865         var tw = this.trigger.getWidth();
41866         tw += this.addicon ? this.addicon.getWidth() : 0;
41867         tw += this.editicon ? this.editicon.getWidth() : 0;
41868         var x = w - tw;
41869         this.el.setWidth( this.adjustWidth('input', x));
41870             
41871         this.trigger.setStyle('left', x+'px');
41872         
41873         if(this.list && this.listWidth === undefined){
41874             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41875             this.list.setWidth(lw);
41876             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41877         }
41878         
41879     
41880         
41881     },
41882
41883     /**
41884      * Allow or prevent the user from directly editing the field text.  If false is passed,
41885      * the user will only be able to select from the items defined in the dropdown list.  This method
41886      * is the runtime equivalent of setting the 'editable' config option at config time.
41887      * @param {Boolean} value True to allow the user to directly edit the field text
41888      */
41889     setEditable : function(value){
41890         if(value == this.editable){
41891             return;
41892         }
41893         this.editable = value;
41894         if(!value){
41895             this.el.dom.setAttribute('readOnly', true);
41896             this.el.on('mousedown', this.onTriggerClick,  this);
41897             this.el.addClass('x-combo-noedit');
41898         }else{
41899             this.el.dom.setAttribute('readOnly', false);
41900             this.el.un('mousedown', this.onTriggerClick,  this);
41901             this.el.removeClass('x-combo-noedit');
41902         }
41903     },
41904
41905     // private
41906     onBeforeLoad : function(){
41907         if(!this.hasFocus){
41908             return;
41909         }
41910         this.innerList.update(this.loadingText ?
41911                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41912         this.restrictHeight();
41913         this.selectedIndex = -1;
41914     },
41915
41916     // private
41917     onLoad : function(){
41918         if(!this.hasFocus){
41919             return;
41920         }
41921         if(this.store.getCount() > 0){
41922             this.expand();
41923             this.restrictHeight();
41924             if(this.lastQuery == this.allQuery){
41925                 if(this.editable){
41926                     this.el.dom.select();
41927                 }
41928                 if(!this.selectByValue(this.value, true)){
41929                     this.select(0, true);
41930                 }
41931             }else{
41932                 this.selectNext();
41933                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41934                     this.taTask.delay(this.typeAheadDelay);
41935                 }
41936             }
41937         }else{
41938             this.onEmptyResults();
41939         }
41940         //this.el.focus();
41941     },
41942     // private
41943     onLoadException : function()
41944     {
41945         this.collapse();
41946         Roo.log(this.store.reader.jsonData);
41947         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41948             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41949         }
41950         
41951         
41952     },
41953     // private
41954     onTypeAhead : function(){
41955         if(this.store.getCount() > 0){
41956             var r = this.store.getAt(0);
41957             var newValue = r.data[this.displayField];
41958             var len = newValue.length;
41959             var selStart = this.getRawValue().length;
41960             if(selStart != len){
41961                 this.setRawValue(newValue);
41962                 this.selectText(selStart, newValue.length);
41963             }
41964         }
41965     },
41966
41967     // private
41968     onSelect : function(record, index){
41969         if(this.fireEvent('beforeselect', this, record, index) !== false){
41970             this.setFromData(index > -1 ? record.data : false);
41971             this.collapse();
41972             this.fireEvent('select', this, record, index);
41973         }
41974     },
41975
41976     /**
41977      * Returns the currently selected field value or empty string if no value is set.
41978      * @return {String} value The selected value
41979      */
41980     getValue : function(){
41981         if(this.valueField){
41982             return typeof this.value != 'undefined' ? this.value : '';
41983         }
41984         return Roo.form.ComboBox.superclass.getValue.call(this);
41985     },
41986
41987     /**
41988      * Clears any text/value currently set in the field
41989      */
41990     clearValue : function(){
41991         if(this.hiddenField){
41992             this.hiddenField.value = '';
41993         }
41994         this.value = '';
41995         this.setRawValue('');
41996         this.lastSelectionText = '';
41997         
41998     },
41999
42000     /**
42001      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42002      * will be displayed in the field.  If the value does not match the data value of an existing item,
42003      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42004      * Otherwise the field will be blank (although the value will still be set).
42005      * @param {String} value The value to match
42006      */
42007     setValue : function(v){
42008         var text = v;
42009         if(this.valueField){
42010             var r = this.findRecord(this.valueField, v);
42011             if(r){
42012                 text = r.data[this.displayField];
42013             }else if(this.valueNotFoundText !== undefined){
42014                 text = this.valueNotFoundText;
42015             }
42016         }
42017         this.lastSelectionText = text;
42018         if(this.hiddenField){
42019             this.hiddenField.value = v;
42020         }
42021         Roo.form.ComboBox.superclass.setValue.call(this, text);
42022         this.value = v;
42023     },
42024     /**
42025      * @property {Object} the last set data for the element
42026      */
42027     
42028     lastData : false,
42029     /**
42030      * Sets the value of the field based on a object which is related to the record format for the store.
42031      * @param {Object} value the value to set as. or false on reset?
42032      */
42033     setFromData : function(o){
42034         var dv = ''; // display value
42035         var vv = ''; // value value..
42036         this.lastData = o;
42037         if (this.displayField) {
42038             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42039         } else {
42040             // this is an error condition!!!
42041             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42042         }
42043         
42044         if(this.valueField){
42045             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42046         }
42047         if(this.hiddenField){
42048             this.hiddenField.value = vv;
42049             
42050             this.lastSelectionText = dv;
42051             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42052             this.value = vv;
42053             return;
42054         }
42055         // no hidden field.. - we store the value in 'value', but still display
42056         // display field!!!!
42057         this.lastSelectionText = dv;
42058         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42059         this.value = vv;
42060         
42061         
42062     },
42063     // private
42064     reset : function(){
42065         // overridden so that last data is reset..
42066         this.setValue(this.resetValue);
42067         this.originalValue = this.getValue();
42068         this.clearInvalid();
42069         this.lastData = false;
42070         if (this.view) {
42071             this.view.clearSelections();
42072         }
42073     },
42074     // private
42075     findRecord : function(prop, value){
42076         var record;
42077         if(this.store.getCount() > 0){
42078             this.store.each(function(r){
42079                 if(r.data[prop] == value){
42080                     record = r;
42081                     return false;
42082                 }
42083                 return true;
42084             });
42085         }
42086         return record;
42087     },
42088     
42089     getName: function()
42090     {
42091         // returns hidden if it's set..
42092         if (!this.rendered) {return ''};
42093         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42094         
42095     },
42096     // private
42097     onViewMove : function(e, t){
42098         this.inKeyMode = false;
42099     },
42100
42101     // private
42102     onViewOver : function(e, t){
42103         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42104             return;
42105         }
42106         var item = this.view.findItemFromChild(t);
42107         if(item){
42108             var index = this.view.indexOf(item);
42109             this.select(index, false);
42110         }
42111     },
42112
42113     // private
42114     onViewClick : function(doFocus)
42115     {
42116         var index = this.view.getSelectedIndexes()[0];
42117         var r = this.store.getAt(index);
42118         if(r){
42119             this.onSelect(r, index);
42120         }
42121         if(doFocus !== false && !this.blockFocus){
42122             this.el.focus();
42123         }
42124     },
42125
42126     // private
42127     restrictHeight : function(){
42128         this.innerList.dom.style.height = '';
42129         var inner = this.innerList.dom;
42130         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42131         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42132         this.list.beginUpdate();
42133         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42134         this.list.alignTo(this.el, this.listAlign);
42135         this.list.endUpdate();
42136     },
42137
42138     // private
42139     onEmptyResults : function(){
42140         this.collapse();
42141     },
42142
42143     /**
42144      * Returns true if the dropdown list is expanded, else false.
42145      */
42146     isExpanded : function(){
42147         return this.list.isVisible();
42148     },
42149
42150     /**
42151      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42152      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42153      * @param {String} value The data value of the item to select
42154      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42155      * selected item if it is not currently in view (defaults to true)
42156      * @return {Boolean} True if the value matched an item in the list, else false
42157      */
42158     selectByValue : function(v, scrollIntoView){
42159         if(v !== undefined && v !== null){
42160             var r = this.findRecord(this.valueField || this.displayField, v);
42161             if(r){
42162                 this.select(this.store.indexOf(r), scrollIntoView);
42163                 return true;
42164             }
42165         }
42166         return false;
42167     },
42168
42169     /**
42170      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42171      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42172      * @param {Number} index The zero-based index of the list item to select
42173      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42174      * selected item if it is not currently in view (defaults to true)
42175      */
42176     select : function(index, scrollIntoView){
42177         this.selectedIndex = index;
42178         this.view.select(index);
42179         if(scrollIntoView !== false){
42180             var el = this.view.getNode(index);
42181             if(el){
42182                 this.innerList.scrollChildIntoView(el, false);
42183             }
42184         }
42185     },
42186
42187     // private
42188     selectNext : function(){
42189         var ct = this.store.getCount();
42190         if(ct > 0){
42191             if(this.selectedIndex == -1){
42192                 this.select(0);
42193             }else if(this.selectedIndex < ct-1){
42194                 this.select(this.selectedIndex+1);
42195             }
42196         }
42197     },
42198
42199     // private
42200     selectPrev : function(){
42201         var ct = this.store.getCount();
42202         if(ct > 0){
42203             if(this.selectedIndex == -1){
42204                 this.select(0);
42205             }else if(this.selectedIndex != 0){
42206                 this.select(this.selectedIndex-1);
42207             }
42208         }
42209     },
42210
42211     // private
42212     onKeyUp : function(e){
42213         if(this.editable !== false && !e.isSpecialKey()){
42214             this.lastKey = e.getKey();
42215             this.dqTask.delay(this.queryDelay);
42216         }
42217     },
42218
42219     // private
42220     validateBlur : function(){
42221         return !this.list || !this.list.isVisible();   
42222     },
42223
42224     // private
42225     initQuery : function(){
42226         this.doQuery(this.getRawValue());
42227     },
42228
42229     // private
42230     doForce : function(){
42231         if(this.el.dom.value.length > 0){
42232             this.el.dom.value =
42233                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42234              
42235         }
42236     },
42237
42238     /**
42239      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42240      * query allowing the query action to be canceled if needed.
42241      * @param {String} query The SQL query to execute
42242      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42243      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42244      * saved in the current store (defaults to false)
42245      */
42246     doQuery : function(q, forceAll){
42247         if(q === undefined || q === null){
42248             q = '';
42249         }
42250         var qe = {
42251             query: q,
42252             forceAll: forceAll,
42253             combo: this,
42254             cancel:false
42255         };
42256         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42257             return false;
42258         }
42259         q = qe.query;
42260         forceAll = qe.forceAll;
42261         if(forceAll === true || (q.length >= this.minChars)){
42262             if(this.lastQuery != q || this.alwaysQuery){
42263                 this.lastQuery = q;
42264                 if(this.mode == 'local'){
42265                     this.selectedIndex = -1;
42266                     if(forceAll){
42267                         this.store.clearFilter();
42268                     }else{
42269                         this.store.filter(this.displayField, q);
42270                     }
42271                     this.onLoad();
42272                 }else{
42273                     this.store.baseParams[this.queryParam] = q;
42274                     this.store.load({
42275                         params: this.getParams(q)
42276                     });
42277                     this.expand();
42278                 }
42279             }else{
42280                 this.selectedIndex = -1;
42281                 this.onLoad();   
42282             }
42283         }
42284     },
42285
42286     // private
42287     getParams : function(q){
42288         var p = {};
42289         //p[this.queryParam] = q;
42290         if(this.pageSize){
42291             p.start = 0;
42292             p.limit = this.pageSize;
42293         }
42294         return p;
42295     },
42296
42297     /**
42298      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42299      */
42300     collapse : function(){
42301         if(!this.isExpanded()){
42302             return;
42303         }
42304         this.list.hide();
42305         Roo.get(document).un('mousedown', this.collapseIf, this);
42306         Roo.get(document).un('mousewheel', this.collapseIf, this);
42307         if (!this.editable) {
42308             Roo.get(document).un('keydown', this.listKeyPress, this);
42309         }
42310         this.fireEvent('collapse', this);
42311     },
42312
42313     // private
42314     collapseIf : function(e){
42315         if(!e.within(this.wrap) && !e.within(this.list)){
42316             this.collapse();
42317         }
42318     },
42319
42320     /**
42321      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42322      */
42323     expand : function(){
42324         if(this.isExpanded() || !this.hasFocus){
42325             return;
42326         }
42327         this.list.alignTo(this.el, this.listAlign);
42328         this.list.show();
42329         Roo.get(document).on('mousedown', this.collapseIf, this);
42330         Roo.get(document).on('mousewheel', this.collapseIf, this);
42331         if (!this.editable) {
42332             Roo.get(document).on('keydown', this.listKeyPress, this);
42333         }
42334         
42335         this.fireEvent('expand', this);
42336     },
42337
42338     // private
42339     // Implements the default empty TriggerField.onTriggerClick function
42340     onTriggerClick : function(){
42341         if(this.disabled){
42342             return;
42343         }
42344         if(this.isExpanded()){
42345             this.collapse();
42346             if (!this.blockFocus) {
42347                 this.el.focus();
42348             }
42349             
42350         }else {
42351             this.hasFocus = true;
42352             if(this.triggerAction == 'all') {
42353                 this.doQuery(this.allQuery, true);
42354             } else {
42355                 this.doQuery(this.getRawValue());
42356             }
42357             if (!this.blockFocus) {
42358                 this.el.focus();
42359             }
42360         }
42361     },
42362     listKeyPress : function(e)
42363     {
42364         //Roo.log('listkeypress');
42365         // scroll to first matching element based on key pres..
42366         if (e.isSpecialKey()) {
42367             return false;
42368         }
42369         var k = String.fromCharCode(e.getKey()).toUpperCase();
42370         //Roo.log(k);
42371         var match  = false;
42372         var csel = this.view.getSelectedNodes();
42373         var cselitem = false;
42374         if (csel.length) {
42375             var ix = this.view.indexOf(csel[0]);
42376             cselitem  = this.store.getAt(ix);
42377             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42378                 cselitem = false;
42379             }
42380             
42381         }
42382         
42383         this.store.each(function(v) { 
42384             if (cselitem) {
42385                 // start at existing selection.
42386                 if (cselitem.id == v.id) {
42387                     cselitem = false;
42388                 }
42389                 return;
42390             }
42391                 
42392             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42393                 match = this.store.indexOf(v);
42394                 return false;
42395             }
42396         }, this);
42397         
42398         if (match === false) {
42399             return true; // no more action?
42400         }
42401         // scroll to?
42402         this.view.select(match);
42403         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42404         sn.scrollIntoView(sn.dom.parentNode, false);
42405     } 
42406
42407     /** 
42408     * @cfg {Boolean} grow 
42409     * @hide 
42410     */
42411     /** 
42412     * @cfg {Number} growMin 
42413     * @hide 
42414     */
42415     /** 
42416     * @cfg {Number} growMax 
42417     * @hide 
42418     */
42419     /**
42420      * @hide
42421      * @method autoSize
42422      */
42423 });/*
42424  * Copyright(c) 2010-2012, Roo J Solutions Limited
42425  *
42426  * Licence LGPL
42427  *
42428  */
42429
42430 /**
42431  * @class Roo.form.ComboBoxArray
42432  * @extends Roo.form.TextField
42433  * A facebook style adder... for lists of email / people / countries  etc...
42434  * pick multiple items from a combo box, and shows each one.
42435  *
42436  *  Fred [x]  Brian [x]  [Pick another |v]
42437  *
42438  *
42439  *  For this to work: it needs various extra information
42440  *    - normal combo problay has
42441  *      name, hiddenName
42442  *    + displayField, valueField
42443  *
42444  *    For our purpose...
42445  *
42446  *
42447  *   If we change from 'extends' to wrapping...
42448  *   
42449  *  
42450  *
42451  
42452  
42453  * @constructor
42454  * Create a new ComboBoxArray.
42455  * @param {Object} config Configuration options
42456  */
42457  
42458
42459 Roo.form.ComboBoxArray = function(config)
42460 {
42461     this.addEvents({
42462         /**
42463          * @event beforeremove
42464          * Fires before remove the value from the list
42465              * @param {Roo.form.ComboBoxArray} _self This combo box array
42466              * @param {Roo.form.ComboBoxArray.Item} item removed item
42467              */
42468         'beforeremove' : true,
42469         /**
42470          * @event remove
42471          * Fires when remove the value from the list
42472              * @param {Roo.form.ComboBoxArray} _self This combo box array
42473              * @param {Roo.form.ComboBoxArray.Item} item removed item
42474              */
42475         'remove' : true
42476         
42477         
42478     });
42479     
42480     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42481     
42482     this.items = new Roo.util.MixedCollection(false);
42483     
42484     // construct the child combo...
42485     
42486     
42487     
42488     
42489    
42490     
42491 }
42492
42493  
42494 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42495
42496     /**
42497      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42498      */
42499     
42500     lastData : false,
42501     
42502     // behavies liek a hiddne field
42503     inputType:      'hidden',
42504     /**
42505      * @cfg {Number} width The width of the box that displays the selected element
42506      */ 
42507     width:          300,
42508
42509     
42510     
42511     /**
42512      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42513      */
42514     name : false,
42515     /**
42516      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42517      */
42518     hiddenName : false,
42519       /**
42520      * @cfg {String} seperator    The value seperator normally ',' 
42521      */
42522     seperator : ',',
42523     
42524     // private the array of items that are displayed..
42525     items  : false,
42526     // private - the hidden field el.
42527     hiddenEl : false,
42528     // private - the filed el..
42529     el : false,
42530     
42531     //validateValue : function() { return true; }, // all values are ok!
42532     //onAddClick: function() { },
42533     
42534     onRender : function(ct, position) 
42535     {
42536         
42537         // create the standard hidden element
42538         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42539         
42540         
42541         // give fake names to child combo;
42542         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42543         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42544         
42545         this.combo = Roo.factory(this.combo, Roo.form);
42546         this.combo.onRender(ct, position);
42547         if (typeof(this.combo.width) != 'undefined') {
42548             this.combo.onResize(this.combo.width,0);
42549         }
42550         
42551         this.combo.initEvents();
42552         
42553         // assigned so form know we need to do this..
42554         this.store          = this.combo.store;
42555         this.valueField     = this.combo.valueField;
42556         this.displayField   = this.combo.displayField ;
42557         
42558         
42559         this.combo.wrap.addClass('x-cbarray-grp');
42560         
42561         var cbwrap = this.combo.wrap.createChild(
42562             {tag: 'div', cls: 'x-cbarray-cb'},
42563             this.combo.el.dom
42564         );
42565         
42566              
42567         this.hiddenEl = this.combo.wrap.createChild({
42568             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42569         });
42570         this.el = this.combo.wrap.createChild({
42571             tag: 'input',  type:'hidden' , name: this.name, value : ''
42572         });
42573          //   this.el.dom.removeAttribute("name");
42574         
42575         
42576         this.outerWrap = this.combo.wrap;
42577         this.wrap = cbwrap;
42578         
42579         this.outerWrap.setWidth(this.width);
42580         this.outerWrap.dom.removeChild(this.el.dom);
42581         
42582         this.wrap.dom.appendChild(this.el.dom);
42583         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42584         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42585         
42586         this.combo.trigger.setStyle('position','relative');
42587         this.combo.trigger.setStyle('left', '0px');
42588         this.combo.trigger.setStyle('top', '2px');
42589         
42590         this.combo.el.setStyle('vertical-align', 'text-bottom');
42591         
42592         //this.trigger.setStyle('vertical-align', 'top');
42593         
42594         // this should use the code from combo really... on('add' ....)
42595         if (this.adder) {
42596             
42597         
42598             this.adder = this.outerWrap.createChild(
42599                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42600             var _t = this;
42601             this.adder.on('click', function(e) {
42602                 _t.fireEvent('adderclick', this, e);
42603             }, _t);
42604         }
42605         //var _t = this;
42606         //this.adder.on('click', this.onAddClick, _t);
42607         
42608         
42609         this.combo.on('select', function(cb, rec, ix) {
42610             this.addItem(rec.data);
42611             
42612             cb.setValue('');
42613             cb.el.dom.value = '';
42614             //cb.lastData = rec.data;
42615             // add to list
42616             
42617         }, this);
42618         
42619         
42620     },
42621     
42622     
42623     getName: function()
42624     {
42625         // returns hidden if it's set..
42626         if (!this.rendered) {return ''};
42627         return  this.hiddenName ? this.hiddenName : this.name;
42628         
42629     },
42630     
42631     
42632     onResize: function(w, h){
42633         
42634         return;
42635         // not sure if this is needed..
42636         //this.combo.onResize(w,h);
42637         
42638         if(typeof w != 'number'){
42639             // we do not handle it!?!?
42640             return;
42641         }
42642         var tw = this.combo.trigger.getWidth();
42643         tw += this.addicon ? this.addicon.getWidth() : 0;
42644         tw += this.editicon ? this.editicon.getWidth() : 0;
42645         var x = w - tw;
42646         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42647             
42648         this.combo.trigger.setStyle('left', '0px');
42649         
42650         if(this.list && this.listWidth === undefined){
42651             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42652             this.list.setWidth(lw);
42653             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42654         }
42655         
42656     
42657         
42658     },
42659     
42660     addItem: function(rec)
42661     {
42662         var valueField = this.combo.valueField;
42663         var displayField = this.combo.displayField;
42664         
42665         if (this.items.indexOfKey(rec[valueField]) > -1) {
42666             //console.log("GOT " + rec.data.id);
42667             return;
42668         }
42669         
42670         var x = new Roo.form.ComboBoxArray.Item({
42671             //id : rec[this.idField],
42672             data : rec,
42673             displayField : displayField ,
42674             tipField : displayField ,
42675             cb : this
42676         });
42677         // use the 
42678         this.items.add(rec[valueField],x);
42679         // add it before the element..
42680         this.updateHiddenEl();
42681         x.render(this.outerWrap, this.wrap.dom);
42682         // add the image handler..
42683     },
42684     
42685     updateHiddenEl : function()
42686     {
42687         this.validate();
42688         if (!this.hiddenEl) {
42689             return;
42690         }
42691         var ar = [];
42692         var idField = this.combo.valueField;
42693         
42694         this.items.each(function(f) {
42695             ar.push(f.data[idField]);
42696         });
42697         this.hiddenEl.dom.value = ar.join(this.seperator);
42698         this.validate();
42699     },
42700     
42701     reset : function()
42702     {
42703         this.items.clear();
42704         
42705         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42706            el.remove();
42707         });
42708         
42709         this.el.dom.value = '';
42710         if (this.hiddenEl) {
42711             this.hiddenEl.dom.value = '';
42712         }
42713         
42714     },
42715     getValue: function()
42716     {
42717         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42718     },
42719     setValue: function(v) // not a valid action - must use addItems..
42720     {
42721         
42722         this.reset();
42723          
42724         if (this.store.isLocal && (typeof(v) == 'string')) {
42725             // then we can use the store to find the values..
42726             // comma seperated at present.. this needs to allow JSON based encoding..
42727             this.hiddenEl.value  = v;
42728             var v_ar = [];
42729             Roo.each(v.split(this.seperator), function(k) {
42730                 Roo.log("CHECK " + this.valueField + ',' + k);
42731                 var li = this.store.query(this.valueField, k);
42732                 if (!li.length) {
42733                     return;
42734                 }
42735                 var add = {};
42736                 add[this.valueField] = k;
42737                 add[this.displayField] = li.item(0).data[this.displayField];
42738                 
42739                 this.addItem(add);
42740             }, this) 
42741              
42742         }
42743         if (typeof(v) == 'object' ) {
42744             // then let's assume it's an array of objects..
42745             Roo.each(v, function(l) {
42746                 var add = l;
42747                 if (typeof(l) == 'string') {
42748                     add = {};
42749                     add[this.valueField] = l;
42750                     add[this.displayField] = l
42751                 }
42752                 this.addItem(add);
42753             }, this);
42754              
42755         }
42756         
42757         
42758     },
42759     setFromData: function(v)
42760     {
42761         // this recieves an object, if setValues is called.
42762         this.reset();
42763         this.el.dom.value = v[this.displayField];
42764         this.hiddenEl.dom.value = v[this.valueField];
42765         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42766             return;
42767         }
42768         var kv = v[this.valueField];
42769         var dv = v[this.displayField];
42770         kv = typeof(kv) != 'string' ? '' : kv;
42771         dv = typeof(dv) != 'string' ? '' : dv;
42772         
42773         
42774         var keys = kv.split(this.seperator);
42775         var display = dv.split(this.seperator);
42776         for (var i = 0 ; i < keys.length; i++) {
42777             add = {};
42778             add[this.valueField] = keys[i];
42779             add[this.displayField] = display[i];
42780             this.addItem(add);
42781         }
42782       
42783         
42784     },
42785     
42786     /**
42787      * Validates the combox array value
42788      * @return {Boolean} True if the value is valid, else false
42789      */
42790     validate : function(){
42791         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42792             this.clearInvalid();
42793             return true;
42794         }
42795         return false;
42796     },
42797     
42798     validateValue : function(value){
42799         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42800         
42801     },
42802     
42803     /*@
42804      * overide
42805      * 
42806      */
42807     isDirty : function() {
42808         if(this.disabled) {
42809             return false;
42810         }
42811         
42812         try {
42813             var d = Roo.decode(String(this.originalValue));
42814         } catch (e) {
42815             return String(this.getValue()) !== String(this.originalValue);
42816         }
42817         
42818         var originalValue = [];
42819         
42820         for (var i = 0; i < d.length; i++){
42821             originalValue.push(d[i][this.valueField]);
42822         }
42823         
42824         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42825         
42826     }
42827     
42828 });
42829
42830
42831
42832 /**
42833  * @class Roo.form.ComboBoxArray.Item
42834  * @extends Roo.BoxComponent
42835  * A selected item in the list
42836  *  Fred [x]  Brian [x]  [Pick another |v]
42837  * 
42838  * @constructor
42839  * Create a new item.
42840  * @param {Object} config Configuration options
42841  */
42842  
42843 Roo.form.ComboBoxArray.Item = function(config) {
42844     config.id = Roo.id();
42845     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42846 }
42847
42848 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42849     data : {},
42850     cb: false,
42851     displayField : false,
42852     tipField : false,
42853     
42854     
42855     defaultAutoCreate : {
42856         tag: 'div',
42857         cls: 'x-cbarray-item',
42858         cn : [ 
42859             { tag: 'div' },
42860             {
42861                 tag: 'img',
42862                 width:16,
42863                 height : 16,
42864                 src : Roo.BLANK_IMAGE_URL ,
42865                 align: 'center'
42866             }
42867         ]
42868         
42869     },
42870     
42871  
42872     onRender : function(ct, position)
42873     {
42874         Roo.form.Field.superclass.onRender.call(this, ct, position);
42875         
42876         if(!this.el){
42877             var cfg = this.getAutoCreate();
42878             this.el = ct.createChild(cfg, position);
42879         }
42880         
42881         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42882         
42883         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42884             this.cb.renderer(this.data) :
42885             String.format('{0}',this.data[this.displayField]);
42886         
42887             
42888         this.el.child('div').dom.setAttribute('qtip',
42889                         String.format('{0}',this.data[this.tipField])
42890         );
42891         
42892         this.el.child('img').on('click', this.remove, this);
42893         
42894     },
42895    
42896     remove : function()
42897     {
42898         if(this.cb.disabled){
42899             return;
42900         }
42901         
42902         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42903             this.cb.items.remove(this);
42904             this.el.child('img').un('click', this.remove, this);
42905             this.el.remove();
42906             this.cb.updateHiddenEl();
42907
42908             this.cb.fireEvent('remove', this.cb, this);
42909         }
42910         
42911     }
42912 });/*
42913  * RooJS Library 1.1.1
42914  * Copyright(c) 2008-2011  Alan Knowles
42915  *
42916  * License - LGPL
42917  */
42918  
42919
42920 /**
42921  * @class Roo.form.ComboNested
42922  * @extends Roo.form.ComboBox
42923  * A combobox for that allows selection of nested items in a list,
42924  * eg.
42925  *
42926  *  Book
42927  *    -> red
42928  *    -> green
42929  *  Table
42930  *    -> square
42931  *      ->red
42932  *      ->green
42933  *    -> rectangle
42934  *      ->green
42935  *      
42936  * 
42937  * @constructor
42938  * Create a new ComboNested
42939  * @param {Object} config Configuration options
42940  */
42941 Roo.form.ComboNested = function(config){
42942     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42943     // should verify some data...
42944     // like
42945     // hiddenName = required..
42946     // displayField = required
42947     // valudField == required
42948     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42949     var _t = this;
42950     Roo.each(req, function(e) {
42951         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42952             throw "Roo.form.ComboNested : missing value for: " + e;
42953         }
42954     });
42955      
42956     
42957 };
42958
42959 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42960    
42961     /*
42962      * @config {Number} max Number of columns to show
42963      */
42964     
42965     maxColumns : 3,
42966    
42967     list : null, // the outermost div..
42968     innerLists : null, // the
42969     views : null,
42970     stores : null,
42971     // private
42972     loadingChildren : false,
42973     
42974     onRender : function(ct, position)
42975     {
42976         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42977         
42978         if(this.hiddenName){
42979             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42980                     'before', true);
42981             this.hiddenField.value =
42982                 this.hiddenValue !== undefined ? this.hiddenValue :
42983                 this.value !== undefined ? this.value : '';
42984
42985             // prevent input submission
42986             this.el.dom.removeAttribute('name');
42987              
42988              
42989         }
42990         
42991         if(Roo.isGecko){
42992             this.el.dom.setAttribute('autocomplete', 'off');
42993         }
42994
42995         var cls = 'x-combo-list';
42996
42997         this.list = new Roo.Layer({
42998             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42999         });
43000
43001         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43002         this.list.setWidth(lw);
43003         this.list.swallowEvent('mousewheel');
43004         this.assetHeight = 0;
43005
43006         if(this.title){
43007             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43008             this.assetHeight += this.header.getHeight();
43009         }
43010         this.innerLists = [];
43011         this.views = [];
43012         this.stores = [];
43013         for (var i =0 ; i < this.maxColumns; i++) {
43014             this.onRenderList( cls, i);
43015         }
43016         
43017         // always needs footer, as we are going to have an 'OK' button.
43018         this.footer = this.list.createChild({cls:cls+'-ft'});
43019         this.pageTb = new Roo.Toolbar(this.footer);  
43020         var _this = this;
43021         this.pageTb.add(  {
43022             
43023             text: 'Done',
43024             handler: function()
43025             {
43026                 _this.collapse();
43027             }
43028         });
43029         
43030         if ( this.allowBlank && !this.disableClear) {
43031             
43032             this.pageTb.add(new Roo.Toolbar.Fill(), {
43033                 cls: 'x-btn-icon x-btn-clear',
43034                 text: '&#160;',
43035                 handler: function()
43036                 {
43037                     _this.collapse();
43038                     _this.clearValue();
43039                     _this.onSelect(false, -1);
43040                 }
43041             });
43042         }
43043         if (this.footer) {
43044             this.assetHeight += this.footer.getHeight();
43045         }
43046         
43047     },
43048     onRenderList : function (  cls, i)
43049     {
43050         
43051         var lw = Math.floor(
43052                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43053         );
43054         
43055         this.list.setWidth(lw); // default to '1'
43056
43057         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43058         //il.on('mouseover', this.onViewOver, this, { list:  i });
43059         //il.on('mousemove', this.onViewMove, this, { list:  i });
43060         il.setWidth(lw);
43061         il.setStyle({ 'overflow-x' : 'hidden'});
43062
43063         if(!this.tpl){
43064             this.tpl = new Roo.Template({
43065                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43066                 isEmpty: function (value, allValues) {
43067                     //Roo.log(value);
43068                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43069                     return dl ? 'has-children' : 'no-children'
43070                 }
43071             });
43072         }
43073         
43074         var store  = this.store;
43075         if (i > 0) {
43076             store  = new Roo.data.SimpleStore({
43077                 //fields : this.store.reader.meta.fields,
43078                 reader : this.store.reader,
43079                 data : [ ]
43080             });
43081         }
43082         this.stores[i]  = store;
43083                   
43084         var view = this.views[i] = new Roo.View(
43085             il,
43086             this.tpl,
43087             {
43088                 singleSelect:true,
43089                 store: store,
43090                 selectedClass: this.selectedClass
43091             }
43092         );
43093         view.getEl().setWidth(lw);
43094         view.getEl().setStyle({
43095             position: i < 1 ? 'relative' : 'absolute',
43096             top: 0,
43097             left: (i * lw ) + 'px',
43098             display : i > 0 ? 'none' : 'block'
43099         });
43100         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43101         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43102         //view.on('click', this.onViewClick, this, { list : i });
43103
43104         store.on('beforeload', this.onBeforeLoad, this);
43105         store.on('load',  this.onLoad, this, { list  : i});
43106         store.on('loadexception', this.onLoadException, this);
43107
43108         // hide the other vies..
43109         
43110         
43111         
43112     },
43113       
43114     restrictHeight : function()
43115     {
43116         var mh = 0;
43117         Roo.each(this.innerLists, function(il,i) {
43118             var el = this.views[i].getEl();
43119             el.dom.style.height = '';
43120             var inner = el.dom;
43121             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43122             // only adjust heights on other ones..
43123             mh = Math.max(h, mh);
43124             if (i < 1) {
43125                 
43126                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43127                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43128                
43129             }
43130             
43131             
43132         }, this);
43133         
43134         this.list.beginUpdate();
43135         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43136         this.list.alignTo(this.el, this.listAlign);
43137         this.list.endUpdate();
43138         
43139     },
43140      
43141     
43142     // -- store handlers..
43143     // private
43144     onBeforeLoad : function()
43145     {
43146         if(!this.hasFocus){
43147             return;
43148         }
43149         this.innerLists[0].update(this.loadingText ?
43150                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43151         this.restrictHeight();
43152         this.selectedIndex = -1;
43153     },
43154     // private
43155     onLoad : function(a,b,c,d)
43156     {
43157         if (!this.loadingChildren) {
43158             // then we are loading the top level. - hide the children
43159             for (var i = 1;i < this.views.length; i++) {
43160                 this.views[i].getEl().setStyle({ display : 'none' });
43161             }
43162             var lw = Math.floor(
43163                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43164             );
43165         
43166              this.list.setWidth(lw); // default to '1'
43167
43168             
43169         }
43170         if(!this.hasFocus){
43171             return;
43172         }
43173         
43174         if(this.store.getCount() > 0) {
43175             this.expand();
43176             this.restrictHeight();   
43177         } else {
43178             this.onEmptyResults();
43179         }
43180         
43181         if (!this.loadingChildren) {
43182             this.selectActive();
43183         }
43184         /*
43185         this.stores[1].loadData([]);
43186         this.stores[2].loadData([]);
43187         this.views
43188         */    
43189     
43190         //this.el.focus();
43191     },
43192     
43193     
43194     // private
43195     onLoadException : function()
43196     {
43197         this.collapse();
43198         Roo.log(this.store.reader.jsonData);
43199         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43200             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43201         }
43202         
43203         
43204     },
43205     // no cleaning of leading spaces on blur here.
43206     cleanLeadingSpace : function(e) { },
43207     
43208
43209     onSelectChange : function (view, sels, opts )
43210     {
43211         var ix = view.getSelectedIndexes();
43212          
43213         if (opts.list > this.maxColumns - 2) {
43214             if (view.store.getCount()<  1) {
43215                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43216
43217             } else  {
43218                 if (ix.length) {
43219                     // used to clear ?? but if we are loading unselected 
43220                     this.setFromData(view.store.getAt(ix[0]).data);
43221                 }
43222                 
43223             }
43224             
43225             return;
43226         }
43227         
43228         if (!ix.length) {
43229             // this get's fired when trigger opens..
43230            // this.setFromData({});
43231             var str = this.stores[opts.list+1];
43232             str.data.clear(); // removeall wihtout the fire events..
43233             return;
43234         }
43235         
43236         var rec = view.store.getAt(ix[0]);
43237          
43238         this.setFromData(rec.data);
43239         this.fireEvent('select', this, rec, ix[0]);
43240         
43241         var lw = Math.floor(
43242              (
43243                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43244              ) / this.maxColumns
43245         );
43246         this.loadingChildren = true;
43247         this.stores[opts.list+1].loadDataFromChildren( rec );
43248         this.loadingChildren = false;
43249         var dl = this.stores[opts.list+1]. getTotalCount();
43250         
43251         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43252         
43253         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43254         for (var i = opts.list+2; i < this.views.length;i++) {
43255             this.views[i].getEl().setStyle({ display : 'none' });
43256         }
43257         
43258         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43259         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43260         
43261         if (this.isLoading) {
43262            // this.selectActive(opts.list);
43263         }
43264          
43265     },
43266     
43267     
43268     
43269     
43270     onDoubleClick : function()
43271     {
43272         this.collapse(); //??
43273     },
43274     
43275      
43276     
43277     
43278     
43279     // private
43280     recordToStack : function(store, prop, value, stack)
43281     {
43282         var cstore = new Roo.data.SimpleStore({
43283             //fields : this.store.reader.meta.fields, // we need array reader.. for
43284             reader : this.store.reader,
43285             data : [ ]
43286         });
43287         var _this = this;
43288         var record  = false;
43289         var srec = false;
43290         if(store.getCount() < 1){
43291             return false;
43292         }
43293         store.each(function(r){
43294             if(r.data[prop] == value){
43295                 record = r;
43296             srec = r;
43297                 return false;
43298             }
43299             if (r.data.cn && r.data.cn.length) {
43300                 cstore.loadDataFromChildren( r);
43301                 var cret = _this.recordToStack(cstore, prop, value, stack);
43302                 if (cret !== false) {
43303                     record = cret;
43304                     srec = r;
43305                     return false;
43306                 }
43307             }
43308              
43309             return true;
43310         });
43311         if (record == false) {
43312             return false
43313         }
43314         stack.unshift(srec);
43315         return record;
43316     },
43317     
43318     /*
43319      * find the stack of stores that match our value.
43320      *
43321      * 
43322      */
43323     
43324     selectActive : function ()
43325     {
43326         // if store is not loaded, then we will need to wait for that to happen first.
43327         var stack = [];
43328         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43329         for (var i = 0; i < stack.length; i++ ) {
43330             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43331         }
43332         
43333     }
43334         
43335          
43336     
43337     
43338     
43339     
43340 });/*
43341  * Based on:
43342  * Ext JS Library 1.1.1
43343  * Copyright(c) 2006-2007, Ext JS, LLC.
43344  *
43345  * Originally Released Under LGPL - original licence link has changed is not relivant.
43346  *
43347  * Fork - LGPL
43348  * <script type="text/javascript">
43349  */
43350 /**
43351  * @class Roo.form.Checkbox
43352  * @extends Roo.form.Field
43353  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43354  * @constructor
43355  * Creates a new Checkbox
43356  * @param {Object} config Configuration options
43357  */
43358 Roo.form.Checkbox = function(config){
43359     Roo.form.Checkbox.superclass.constructor.call(this, config);
43360     this.addEvents({
43361         /**
43362          * @event check
43363          * Fires when the checkbox is checked or unchecked.
43364              * @param {Roo.form.Checkbox} this This checkbox
43365              * @param {Boolean} checked The new checked value
43366              */
43367         check : true
43368     });
43369 };
43370
43371 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43372     /**
43373      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43374      */
43375     focusClass : undefined,
43376     /**
43377      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43378      */
43379     fieldClass: "x-form-field",
43380     /**
43381      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43382      */
43383     checked: false,
43384     /**
43385      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43386      * {tag: "input", type: "checkbox", autocomplete: "off"})
43387      */
43388     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43389     /**
43390      * @cfg {String} boxLabel The text that appears beside the checkbox
43391      */
43392     boxLabel : "",
43393     /**
43394      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43395      */  
43396     inputValue : '1',
43397     /**
43398      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43399      */
43400      valueOff: '0', // value when not checked..
43401
43402     actionMode : 'viewEl', 
43403     //
43404     // private
43405     itemCls : 'x-menu-check-item x-form-item',
43406     groupClass : 'x-menu-group-item',
43407     inputType : 'hidden',
43408     
43409     
43410     inSetChecked: false, // check that we are not calling self...
43411     
43412     inputElement: false, // real input element?
43413     basedOn: false, // ????
43414     
43415     isFormField: true, // not sure where this is needed!!!!
43416
43417     onResize : function(){
43418         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43419         if(!this.boxLabel){
43420             this.el.alignTo(this.wrap, 'c-c');
43421         }
43422     },
43423
43424     initEvents : function(){
43425         Roo.form.Checkbox.superclass.initEvents.call(this);
43426         this.el.on("click", this.onClick,  this);
43427         this.el.on("change", this.onClick,  this);
43428     },
43429
43430
43431     getResizeEl : function(){
43432         return this.wrap;
43433     },
43434
43435     getPositionEl : function(){
43436         return this.wrap;
43437     },
43438
43439     // private
43440     onRender : function(ct, position){
43441         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43442         /*
43443         if(this.inputValue !== undefined){
43444             this.el.dom.value = this.inputValue;
43445         }
43446         */
43447         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43448         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43449         var viewEl = this.wrap.createChild({ 
43450             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43451         this.viewEl = viewEl;   
43452         this.wrap.on('click', this.onClick,  this); 
43453         
43454         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43455         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43456         
43457         
43458         
43459         if(this.boxLabel){
43460             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43461         //    viewEl.on('click', this.onClick,  this); 
43462         }
43463         //if(this.checked){
43464             this.setChecked(this.checked);
43465         //}else{
43466             //this.checked = this.el.dom;
43467         //}
43468
43469     },
43470
43471     // private
43472     initValue : Roo.emptyFn,
43473
43474     /**
43475      * Returns the checked state of the checkbox.
43476      * @return {Boolean} True if checked, else false
43477      */
43478     getValue : function(){
43479         if(this.el){
43480             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43481         }
43482         return this.valueOff;
43483         
43484     },
43485
43486         // private
43487     onClick : function(){ 
43488         if (this.disabled) {
43489             return;
43490         }
43491         this.setChecked(!this.checked);
43492
43493         //if(this.el.dom.checked != this.checked){
43494         //    this.setValue(this.el.dom.checked);
43495        // }
43496     },
43497
43498     /**
43499      * Sets the checked state of the checkbox.
43500      * On is always based on a string comparison between inputValue and the param.
43501      * @param {Boolean/String} value - the value to set 
43502      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43503      */
43504     setValue : function(v,suppressEvent){
43505         
43506         
43507         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43508         //if(this.el && this.el.dom){
43509         //    this.el.dom.checked = this.checked;
43510         //    this.el.dom.defaultChecked = this.checked;
43511         //}
43512         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43513         //this.fireEvent("check", this, this.checked);
43514     },
43515     // private..
43516     setChecked : function(state,suppressEvent)
43517     {
43518         if (this.inSetChecked) {
43519             this.checked = state;
43520             return;
43521         }
43522         
43523     
43524         if(this.wrap){
43525             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43526         }
43527         this.checked = state;
43528         if(suppressEvent !== true){
43529             this.fireEvent('check', this, state);
43530         }
43531         this.inSetChecked = true;
43532         this.el.dom.value = state ? this.inputValue : this.valueOff;
43533         this.inSetChecked = false;
43534         
43535     },
43536     // handle setting of hidden value by some other method!!?!?
43537     setFromHidden: function()
43538     {
43539         if(!this.el){
43540             return;
43541         }
43542         //console.log("SET FROM HIDDEN");
43543         //alert('setFrom hidden');
43544         this.setValue(this.el.dom.value);
43545     },
43546     
43547     onDestroy : function()
43548     {
43549         if(this.viewEl){
43550             Roo.get(this.viewEl).remove();
43551         }
43552          
43553         Roo.form.Checkbox.superclass.onDestroy.call(this);
43554     },
43555     
43556     setBoxLabel : function(str)
43557     {
43558         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43559     }
43560
43561 });/*
43562  * Based on:
43563  * Ext JS Library 1.1.1
43564  * Copyright(c) 2006-2007, Ext JS, LLC.
43565  *
43566  * Originally Released Under LGPL - original licence link has changed is not relivant.
43567  *
43568  * Fork - LGPL
43569  * <script type="text/javascript">
43570  */
43571  
43572 /**
43573  * @class Roo.form.Radio
43574  * @extends Roo.form.Checkbox
43575  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43576  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43577  * @constructor
43578  * Creates a new Radio
43579  * @param {Object} config Configuration options
43580  */
43581 Roo.form.Radio = function(){
43582     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43583 };
43584 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43585     inputType: 'radio',
43586
43587     /**
43588      * If this radio is part of a group, it will return the selected value
43589      * @return {String}
43590      */
43591     getGroupValue : function(){
43592         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43593     },
43594     
43595     
43596     onRender : function(ct, position){
43597         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43598         
43599         if(this.inputValue !== undefined){
43600             this.el.dom.value = this.inputValue;
43601         }
43602          
43603         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43604         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43605         //var viewEl = this.wrap.createChild({ 
43606         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43607         //this.viewEl = viewEl;   
43608         //this.wrap.on('click', this.onClick,  this); 
43609         
43610         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43611         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43612         
43613         
43614         
43615         if(this.boxLabel){
43616             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43617         //    viewEl.on('click', this.onClick,  this); 
43618         }
43619          if(this.checked){
43620             this.el.dom.checked =   'checked' ;
43621         }
43622          
43623     } 
43624     
43625     
43626 });//<script type="text/javascript">
43627
43628 /*
43629  * Based  Ext JS Library 1.1.1
43630  * Copyright(c) 2006-2007, Ext JS, LLC.
43631  * LGPL
43632  *
43633  */
43634  
43635 /**
43636  * @class Roo.HtmlEditorCore
43637  * @extends Roo.Component
43638  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43639  *
43640  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43641  */
43642
43643 Roo.HtmlEditorCore = function(config){
43644     
43645     
43646     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43647     
43648     
43649     this.addEvents({
43650         /**
43651          * @event initialize
43652          * Fires when the editor is fully initialized (including the iframe)
43653          * @param {Roo.HtmlEditorCore} this
43654          */
43655         initialize: true,
43656         /**
43657          * @event activate
43658          * Fires when the editor is first receives the focus. Any insertion must wait
43659          * until after this event.
43660          * @param {Roo.HtmlEditorCore} this
43661          */
43662         activate: true,
43663          /**
43664          * @event beforesync
43665          * Fires before the textarea is updated with content from the editor iframe. Return false
43666          * to cancel the sync.
43667          * @param {Roo.HtmlEditorCore} this
43668          * @param {String} html
43669          */
43670         beforesync: true,
43671          /**
43672          * @event beforepush
43673          * Fires before the iframe editor is updated with content from the textarea. Return false
43674          * to cancel the push.
43675          * @param {Roo.HtmlEditorCore} this
43676          * @param {String} html
43677          */
43678         beforepush: true,
43679          /**
43680          * @event sync
43681          * Fires when the textarea is updated with content from the editor iframe.
43682          * @param {Roo.HtmlEditorCore} this
43683          * @param {String} html
43684          */
43685         sync: true,
43686          /**
43687          * @event push
43688          * Fires when the iframe editor is updated with content from the textarea.
43689          * @param {Roo.HtmlEditorCore} this
43690          * @param {String} html
43691          */
43692         push: true,
43693         
43694         /**
43695          * @event editorevent
43696          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43697          * @param {Roo.HtmlEditorCore} this
43698          */
43699         editorevent: true
43700         
43701     });
43702     
43703     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43704     
43705     // defaults : white / black...
43706     this.applyBlacklists();
43707     
43708     
43709     
43710 };
43711
43712
43713 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43714
43715
43716      /**
43717      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43718      */
43719     
43720     owner : false,
43721     
43722      /**
43723      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43724      *                        Roo.resizable.
43725      */
43726     resizable : false,
43727      /**
43728      * @cfg {Number} height (in pixels)
43729      */   
43730     height: 300,
43731    /**
43732      * @cfg {Number} width (in pixels)
43733      */   
43734     width: 500,
43735     
43736     /**
43737      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43738      * 
43739      */
43740     stylesheets: false,
43741     
43742     // id of frame..
43743     frameId: false,
43744     
43745     // private properties
43746     validationEvent : false,
43747     deferHeight: true,
43748     initialized : false,
43749     activated : false,
43750     sourceEditMode : false,
43751     onFocus : Roo.emptyFn,
43752     iframePad:3,
43753     hideMode:'offsets',
43754     
43755     clearUp: true,
43756     
43757     // blacklist + whitelisted elements..
43758     black: false,
43759     white: false,
43760      
43761     bodyCls : '',
43762
43763     /**
43764      * Protected method that will not generally be called directly. It
43765      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43766      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43767      */
43768     getDocMarkup : function(){
43769         // body styles..
43770         var st = '';
43771         
43772         // inherit styels from page...?? 
43773         if (this.stylesheets === false) {
43774             
43775             Roo.get(document.head).select('style').each(function(node) {
43776                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43777             });
43778             
43779             Roo.get(document.head).select('link').each(function(node) { 
43780                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43781             });
43782             
43783         } else if (!this.stylesheets.length) {
43784                 // simple..
43785                 st = '<style type="text/css">' +
43786                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43787                    '</style>';
43788         } else {
43789             for (var i in this.stylesheets) { 
43790                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43791             }
43792             
43793         }
43794         
43795         st +=  '<style type="text/css">' +
43796             'IMG { cursor: pointer } ' +
43797         '</style>';
43798
43799         var cls = 'roo-htmleditor-body';
43800         
43801         if(this.bodyCls.length){
43802             cls += ' ' + this.bodyCls;
43803         }
43804         
43805         return '<html><head>' + st  +
43806             //<style type="text/css">' +
43807             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43808             //'</style>' +
43809             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43810     },
43811
43812     // private
43813     onRender : function(ct, position)
43814     {
43815         var _t = this;
43816         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43817         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43818         
43819         
43820         this.el.dom.style.border = '0 none';
43821         this.el.dom.setAttribute('tabIndex', -1);
43822         this.el.addClass('x-hidden hide');
43823         
43824         
43825         
43826         if(Roo.isIE){ // fix IE 1px bogus margin
43827             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43828         }
43829        
43830         
43831         this.frameId = Roo.id();
43832         
43833          
43834         
43835         var iframe = this.owner.wrap.createChild({
43836             tag: 'iframe',
43837             cls: 'form-control', // bootstrap..
43838             id: this.frameId,
43839             name: this.frameId,
43840             frameBorder : 'no',
43841             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43842         }, this.el
43843         );
43844         
43845         
43846         this.iframe = iframe.dom;
43847
43848          this.assignDocWin();
43849         
43850         this.doc.designMode = 'on';
43851        
43852         this.doc.open();
43853         this.doc.write(this.getDocMarkup());
43854         this.doc.close();
43855
43856         
43857         var task = { // must defer to wait for browser to be ready
43858             run : function(){
43859                 //console.log("run task?" + this.doc.readyState);
43860                 this.assignDocWin();
43861                 if(this.doc.body || this.doc.readyState == 'complete'){
43862                     try {
43863                         this.doc.designMode="on";
43864                     } catch (e) {
43865                         return;
43866                     }
43867                     Roo.TaskMgr.stop(task);
43868                     this.initEditor.defer(10, this);
43869                 }
43870             },
43871             interval : 10,
43872             duration: 10000,
43873             scope: this
43874         };
43875         Roo.TaskMgr.start(task);
43876
43877     },
43878
43879     // private
43880     onResize : function(w, h)
43881     {
43882          Roo.log('resize: ' +w + ',' + h );
43883         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43884         if(!this.iframe){
43885             return;
43886         }
43887         if(typeof w == 'number'){
43888             
43889             this.iframe.style.width = w + 'px';
43890         }
43891         if(typeof h == 'number'){
43892             
43893             this.iframe.style.height = h + 'px';
43894             if(this.doc){
43895                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43896             }
43897         }
43898         
43899     },
43900
43901     /**
43902      * Toggles the editor between standard and source edit mode.
43903      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43904      */
43905     toggleSourceEdit : function(sourceEditMode){
43906         
43907         this.sourceEditMode = sourceEditMode === true;
43908         
43909         if(this.sourceEditMode){
43910  
43911             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43912             
43913         }else{
43914             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43915             //this.iframe.className = '';
43916             this.deferFocus();
43917         }
43918         //this.setSize(this.owner.wrap.getSize());
43919         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43920     },
43921
43922     
43923   
43924
43925     /**
43926      * Protected method that will not generally be called directly. If you need/want
43927      * custom HTML cleanup, this is the method you should override.
43928      * @param {String} html The HTML to be cleaned
43929      * return {String} The cleaned HTML
43930      */
43931     cleanHtml : function(html){
43932         html = String(html);
43933         if(html.length > 5){
43934             if(Roo.isSafari){ // strip safari nonsense
43935                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43936             }
43937         }
43938         if(html == '&nbsp;'){
43939             html = '';
43940         }
43941         return html;
43942     },
43943
43944     /**
43945      * HTML Editor -> Textarea
43946      * Protected method that will not generally be called directly. Syncs the contents
43947      * of the editor iframe with the textarea.
43948      */
43949     syncValue : function(){
43950         if(this.initialized){
43951             var bd = (this.doc.body || this.doc.documentElement);
43952             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43953             var html = bd.innerHTML;
43954             if(Roo.isSafari){
43955                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43956                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43957                 if(m && m[1]){
43958                     html = '<div style="'+m[0]+'">' + html + '</div>';
43959                 }
43960             }
43961             html = this.cleanHtml(html);
43962             // fix up the special chars.. normaly like back quotes in word...
43963             // however we do not want to do this with chinese..
43964             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43965                 
43966                 var cc = match.charCodeAt();
43967
43968                 // Get the character value, handling surrogate pairs
43969                 if (match.length == 2) {
43970                     // It's a surrogate pair, calculate the Unicode code point
43971                     var high = match.charCodeAt(0) - 0xD800;
43972                     var low  = match.charCodeAt(1) - 0xDC00;
43973                     cc = (high * 0x400) + low + 0x10000;
43974                 }  else if (
43975                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43976                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43977                     (cc >= 0xf900 && cc < 0xfb00 )
43978                 ) {
43979                         return match;
43980                 }  
43981          
43982                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43983                 return "&#" + cc + ";";
43984                 
43985                 
43986             });
43987             
43988             
43989              
43990             if(this.owner.fireEvent('beforesync', this, html) !== false){
43991                 this.el.dom.value = html;
43992                 this.owner.fireEvent('sync', this, html);
43993             }
43994         }
43995     },
43996
43997     /**
43998      * Protected method that will not generally be called directly. Pushes the value of the textarea
43999      * into the iframe editor.
44000      */
44001     pushValue : function(){
44002         if(this.initialized){
44003             var v = this.el.dom.value.trim();
44004             
44005 //            if(v.length < 1){
44006 //                v = '&#160;';
44007 //            }
44008             
44009             if(this.owner.fireEvent('beforepush', this, v) !== false){
44010                 var d = (this.doc.body || this.doc.documentElement);
44011                 d.innerHTML = v;
44012                 this.cleanUpPaste();
44013                 this.el.dom.value = d.innerHTML;
44014                 this.owner.fireEvent('push', this, v);
44015             }
44016         }
44017     },
44018
44019     // private
44020     deferFocus : function(){
44021         this.focus.defer(10, this);
44022     },
44023
44024     // doc'ed in Field
44025     focus : function(){
44026         if(this.win && !this.sourceEditMode){
44027             this.win.focus();
44028         }else{
44029             this.el.focus();
44030         }
44031     },
44032     
44033     assignDocWin: function()
44034     {
44035         var iframe = this.iframe;
44036         
44037          if(Roo.isIE){
44038             this.doc = iframe.contentWindow.document;
44039             this.win = iframe.contentWindow;
44040         } else {
44041 //            if (!Roo.get(this.frameId)) {
44042 //                return;
44043 //            }
44044 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44045 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44046             
44047             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44048                 return;
44049             }
44050             
44051             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44052             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44053         }
44054     },
44055     
44056     // private
44057     initEditor : function(){
44058         //console.log("INIT EDITOR");
44059         this.assignDocWin();
44060         
44061         
44062         
44063         this.doc.designMode="on";
44064         this.doc.open();
44065         this.doc.write(this.getDocMarkup());
44066         this.doc.close();
44067         
44068         var dbody = (this.doc.body || this.doc.documentElement);
44069         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44070         // this copies styles from the containing element into thsi one..
44071         // not sure why we need all of this..
44072         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44073         
44074         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44075         //ss['background-attachment'] = 'fixed'; // w3c
44076         dbody.bgProperties = 'fixed'; // ie
44077         //Roo.DomHelper.applyStyles(dbody, ss);
44078         Roo.EventManager.on(this.doc, {
44079             //'mousedown': this.onEditorEvent,
44080             'mouseup': this.onEditorEvent,
44081             'dblclick': this.onEditorEvent,
44082             'click': this.onEditorEvent,
44083             'keyup': this.onEditorEvent,
44084             buffer:100,
44085             scope: this
44086         });
44087         if(Roo.isGecko){
44088             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44089         }
44090         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44091             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44092         }
44093         this.initialized = true;
44094
44095         this.owner.fireEvent('initialize', this);
44096         this.pushValue();
44097     },
44098
44099     // private
44100     onDestroy : function(){
44101         
44102         
44103         
44104         if(this.rendered){
44105             
44106             //for (var i =0; i < this.toolbars.length;i++) {
44107             //    // fixme - ask toolbars for heights?
44108             //    this.toolbars[i].onDestroy();
44109            // }
44110             
44111             //this.wrap.dom.innerHTML = '';
44112             //this.wrap.remove();
44113         }
44114     },
44115
44116     // private
44117     onFirstFocus : function(){
44118         
44119         this.assignDocWin();
44120         
44121         
44122         this.activated = true;
44123          
44124     
44125         if(Roo.isGecko){ // prevent silly gecko errors
44126             this.win.focus();
44127             var s = this.win.getSelection();
44128             if(!s.focusNode || s.focusNode.nodeType != 3){
44129                 var r = s.getRangeAt(0);
44130                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44131                 r.collapse(true);
44132                 this.deferFocus();
44133             }
44134             try{
44135                 this.execCmd('useCSS', true);
44136                 this.execCmd('styleWithCSS', false);
44137             }catch(e){}
44138         }
44139         this.owner.fireEvent('activate', this);
44140     },
44141
44142     // private
44143     adjustFont: function(btn){
44144         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44145         //if(Roo.isSafari){ // safari
44146         //    adjust *= 2;
44147        // }
44148         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44149         if(Roo.isSafari){ // safari
44150             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44151             v =  (v < 10) ? 10 : v;
44152             v =  (v > 48) ? 48 : v;
44153             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44154             
44155         }
44156         
44157         
44158         v = Math.max(1, v+adjust);
44159         
44160         this.execCmd('FontSize', v  );
44161     },
44162
44163     onEditorEvent : function(e)
44164     {
44165         this.owner.fireEvent('editorevent', this, e);
44166       //  this.updateToolbar();
44167         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44168     },
44169
44170     insertTag : function(tg)
44171     {
44172         // could be a bit smarter... -> wrap the current selected tRoo..
44173         if (tg.toLowerCase() == 'span' ||
44174             tg.toLowerCase() == 'code' ||
44175             tg.toLowerCase() == 'sup' ||
44176             tg.toLowerCase() == 'sub' 
44177             ) {
44178             
44179             range = this.createRange(this.getSelection());
44180             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44181             wrappingNode.appendChild(range.extractContents());
44182             range.insertNode(wrappingNode);
44183
44184             return;
44185             
44186             
44187             
44188         }
44189         this.execCmd("formatblock",   tg);
44190         
44191     },
44192     
44193     insertText : function(txt)
44194     {
44195         
44196         
44197         var range = this.createRange();
44198         range.deleteContents();
44199                //alert(Sender.getAttribute('label'));
44200                
44201         range.insertNode(this.doc.createTextNode(txt));
44202     } ,
44203     
44204      
44205
44206     /**
44207      * Executes a Midas editor command on the editor document and performs necessary focus and
44208      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44209      * @param {String} cmd The Midas command
44210      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44211      */
44212     relayCmd : function(cmd, value){
44213         this.win.focus();
44214         this.execCmd(cmd, value);
44215         this.owner.fireEvent('editorevent', this);
44216         //this.updateToolbar();
44217         this.owner.deferFocus();
44218     },
44219
44220     /**
44221      * Executes a Midas editor command directly on the editor document.
44222      * For visual commands, you should use {@link #relayCmd} instead.
44223      * <b>This should only be called after the editor is initialized.</b>
44224      * @param {String} cmd The Midas command
44225      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44226      */
44227     execCmd : function(cmd, value){
44228         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44229         this.syncValue();
44230     },
44231  
44232  
44233    
44234     /**
44235      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44236      * to insert tRoo.
44237      * @param {String} text | dom node.. 
44238      */
44239     insertAtCursor : function(text)
44240     {
44241         
44242         if(!this.activated){
44243             return;
44244         }
44245         /*
44246         if(Roo.isIE){
44247             this.win.focus();
44248             var r = this.doc.selection.createRange();
44249             if(r){
44250                 r.collapse(true);
44251                 r.pasteHTML(text);
44252                 this.syncValue();
44253                 this.deferFocus();
44254             
44255             }
44256             return;
44257         }
44258         */
44259         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44260             this.win.focus();
44261             
44262             
44263             // from jquery ui (MIT licenced)
44264             var range, node;
44265             var win = this.win;
44266             
44267             if (win.getSelection && win.getSelection().getRangeAt) {
44268                 range = win.getSelection().getRangeAt(0);
44269                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44270                 range.insertNode(node);
44271             } else if (win.document.selection && win.document.selection.createRange) {
44272                 // no firefox support
44273                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44274                 win.document.selection.createRange().pasteHTML(txt);
44275             } else {
44276                 // no firefox support
44277                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44278                 this.execCmd('InsertHTML', txt);
44279             } 
44280             
44281             this.syncValue();
44282             
44283             this.deferFocus();
44284         }
44285     },
44286  // private
44287     mozKeyPress : function(e){
44288         if(e.ctrlKey){
44289             var c = e.getCharCode(), cmd;
44290           
44291             if(c > 0){
44292                 c = String.fromCharCode(c).toLowerCase();
44293                 switch(c){
44294                     case 'b':
44295                         cmd = 'bold';
44296                         break;
44297                     case 'i':
44298                         cmd = 'italic';
44299                         break;
44300                     
44301                     case 'u':
44302                         cmd = 'underline';
44303                         break;
44304                     
44305                     case 'v':
44306                         this.cleanUpPaste.defer(100, this);
44307                         return;
44308                         
44309                 }
44310                 if(cmd){
44311                     this.win.focus();
44312                     this.execCmd(cmd);
44313                     this.deferFocus();
44314                     e.preventDefault();
44315                 }
44316                 
44317             }
44318         }
44319     },
44320
44321     // private
44322     fixKeys : function(){ // load time branching for fastest keydown performance
44323         if(Roo.isIE){
44324             return function(e){
44325                 var k = e.getKey(), r;
44326                 if(k == e.TAB){
44327                     e.stopEvent();
44328                     r = this.doc.selection.createRange();
44329                     if(r){
44330                         r.collapse(true);
44331                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44332                         this.deferFocus();
44333                     }
44334                     return;
44335                 }
44336                 
44337                 if(k == e.ENTER){
44338                     r = this.doc.selection.createRange();
44339                     if(r){
44340                         var target = r.parentElement();
44341                         if(!target || target.tagName.toLowerCase() != 'li'){
44342                             e.stopEvent();
44343                             r.pasteHTML('<br />');
44344                             r.collapse(false);
44345                             r.select();
44346                         }
44347                     }
44348                 }
44349                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44350                     this.cleanUpPaste.defer(100, this);
44351                     return;
44352                 }
44353                 
44354                 
44355             };
44356         }else if(Roo.isOpera){
44357             return function(e){
44358                 var k = e.getKey();
44359                 if(k == e.TAB){
44360                     e.stopEvent();
44361                     this.win.focus();
44362                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44363                     this.deferFocus();
44364                 }
44365                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44366                     this.cleanUpPaste.defer(100, this);
44367                     return;
44368                 }
44369                 
44370             };
44371         }else if(Roo.isSafari){
44372             return function(e){
44373                 var k = e.getKey();
44374                 
44375                 if(k == e.TAB){
44376                     e.stopEvent();
44377                     this.execCmd('InsertText','\t');
44378                     this.deferFocus();
44379                     return;
44380                 }
44381                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44382                     this.cleanUpPaste.defer(100, this);
44383                     return;
44384                 }
44385                 
44386              };
44387         }
44388     }(),
44389     
44390     getAllAncestors: function()
44391     {
44392         var p = this.getSelectedNode();
44393         var a = [];
44394         if (!p) {
44395             a.push(p); // push blank onto stack..
44396             p = this.getParentElement();
44397         }
44398         
44399         
44400         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44401             a.push(p);
44402             p = p.parentNode;
44403         }
44404         a.push(this.doc.body);
44405         return a;
44406     },
44407     lastSel : false,
44408     lastSelNode : false,
44409     
44410     
44411     getSelection : function() 
44412     {
44413         this.assignDocWin();
44414         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44415     },
44416     
44417     getSelectedNode: function() 
44418     {
44419         // this may only work on Gecko!!!
44420         
44421         // should we cache this!!!!
44422         
44423         
44424         
44425          
44426         var range = this.createRange(this.getSelection()).cloneRange();
44427         
44428         if (Roo.isIE) {
44429             var parent = range.parentElement();
44430             while (true) {
44431                 var testRange = range.duplicate();
44432                 testRange.moveToElementText(parent);
44433                 if (testRange.inRange(range)) {
44434                     break;
44435                 }
44436                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44437                     break;
44438                 }
44439                 parent = parent.parentElement;
44440             }
44441             return parent;
44442         }
44443         
44444         // is ancestor a text element.
44445         var ac =  range.commonAncestorContainer;
44446         if (ac.nodeType == 3) {
44447             ac = ac.parentNode;
44448         }
44449         
44450         var ar = ac.childNodes;
44451          
44452         var nodes = [];
44453         var other_nodes = [];
44454         var has_other_nodes = false;
44455         for (var i=0;i<ar.length;i++) {
44456             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44457                 continue;
44458             }
44459             // fullly contained node.
44460             
44461             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44462                 nodes.push(ar[i]);
44463                 continue;
44464             }
44465             
44466             // probably selected..
44467             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44468                 other_nodes.push(ar[i]);
44469                 continue;
44470             }
44471             // outer..
44472             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44473                 continue;
44474             }
44475             
44476             
44477             has_other_nodes = true;
44478         }
44479         if (!nodes.length && other_nodes.length) {
44480             nodes= other_nodes;
44481         }
44482         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44483             return false;
44484         }
44485         
44486         return nodes[0];
44487     },
44488     createRange: function(sel)
44489     {
44490         // this has strange effects when using with 
44491         // top toolbar - not sure if it's a great idea.
44492         //this.editor.contentWindow.focus();
44493         if (typeof sel != "undefined") {
44494             try {
44495                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44496             } catch(e) {
44497                 return this.doc.createRange();
44498             }
44499         } else {
44500             return this.doc.createRange();
44501         }
44502     },
44503     getParentElement: function()
44504     {
44505         
44506         this.assignDocWin();
44507         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44508         
44509         var range = this.createRange(sel);
44510          
44511         try {
44512             var p = range.commonAncestorContainer;
44513             while (p.nodeType == 3) { // text node
44514                 p = p.parentNode;
44515             }
44516             return p;
44517         } catch (e) {
44518             return null;
44519         }
44520     
44521     },
44522     /***
44523      *
44524      * Range intersection.. the hard stuff...
44525      *  '-1' = before
44526      *  '0' = hits..
44527      *  '1' = after.
44528      *         [ -- selected range --- ]
44529      *   [fail]                        [fail]
44530      *
44531      *    basically..
44532      *      if end is before start or  hits it. fail.
44533      *      if start is after end or hits it fail.
44534      *
44535      *   if either hits (but other is outside. - then it's not 
44536      *   
44537      *    
44538      **/
44539     
44540     
44541     // @see http://www.thismuchiknow.co.uk/?p=64.
44542     rangeIntersectsNode : function(range, node)
44543     {
44544         var nodeRange = node.ownerDocument.createRange();
44545         try {
44546             nodeRange.selectNode(node);
44547         } catch (e) {
44548             nodeRange.selectNodeContents(node);
44549         }
44550     
44551         var rangeStartRange = range.cloneRange();
44552         rangeStartRange.collapse(true);
44553     
44554         var rangeEndRange = range.cloneRange();
44555         rangeEndRange.collapse(false);
44556     
44557         var nodeStartRange = nodeRange.cloneRange();
44558         nodeStartRange.collapse(true);
44559     
44560         var nodeEndRange = nodeRange.cloneRange();
44561         nodeEndRange.collapse(false);
44562     
44563         return rangeStartRange.compareBoundaryPoints(
44564                  Range.START_TO_START, nodeEndRange) == -1 &&
44565                rangeEndRange.compareBoundaryPoints(
44566                  Range.START_TO_START, nodeStartRange) == 1;
44567         
44568          
44569     },
44570     rangeCompareNode : function(range, node)
44571     {
44572         var nodeRange = node.ownerDocument.createRange();
44573         try {
44574             nodeRange.selectNode(node);
44575         } catch (e) {
44576             nodeRange.selectNodeContents(node);
44577         }
44578         
44579         
44580         range.collapse(true);
44581     
44582         nodeRange.collapse(true);
44583      
44584         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44585         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44586          
44587         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44588         
44589         var nodeIsBefore   =  ss == 1;
44590         var nodeIsAfter    = ee == -1;
44591         
44592         if (nodeIsBefore && nodeIsAfter) {
44593             return 0; // outer
44594         }
44595         if (!nodeIsBefore && nodeIsAfter) {
44596             return 1; //right trailed.
44597         }
44598         
44599         if (nodeIsBefore && !nodeIsAfter) {
44600             return 2;  // left trailed.
44601         }
44602         // fully contined.
44603         return 3;
44604     },
44605
44606     // private? - in a new class?
44607     cleanUpPaste :  function()
44608     {
44609         // cleans up the whole document..
44610         Roo.log('cleanuppaste');
44611         
44612         this.cleanUpChildren(this.doc.body);
44613         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44614         if (clean != this.doc.body.innerHTML) {
44615             this.doc.body.innerHTML = clean;
44616         }
44617         
44618     },
44619     
44620     cleanWordChars : function(input) {// change the chars to hex code
44621         var he = Roo.HtmlEditorCore;
44622         
44623         var output = input;
44624         Roo.each(he.swapCodes, function(sw) { 
44625             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44626             
44627             output = output.replace(swapper, sw[1]);
44628         });
44629         
44630         return output;
44631     },
44632     
44633     
44634     cleanUpChildren : function (n)
44635     {
44636         if (!n.childNodes.length) {
44637             return;
44638         }
44639         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44640            this.cleanUpChild(n.childNodes[i]);
44641         }
44642     },
44643     
44644     
44645         
44646     
44647     cleanUpChild : function (node)
44648     {
44649         var ed = this;
44650         //console.log(node);
44651         if (node.nodeName == "#text") {
44652             // clean up silly Windows -- stuff?
44653             return; 
44654         }
44655         if (node.nodeName == "#comment") {
44656             node.parentNode.removeChild(node);
44657             // clean up silly Windows -- stuff?
44658             return; 
44659         }
44660         var lcname = node.tagName.toLowerCase();
44661         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44662         // whitelist of tags..
44663         
44664         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44665             // remove node.
44666             node.parentNode.removeChild(node);
44667             return;
44668             
44669         }
44670         
44671         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44672         
44673         // spans with no attributes - just remove them..
44674         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44675             remove_keep_children = true;
44676         }
44677         
44678         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44679         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44680         
44681         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44682         //    remove_keep_children = true;
44683         //}
44684         
44685         if (remove_keep_children) {
44686             this.cleanUpChildren(node);
44687             // inserts everything just before this node...
44688             while (node.childNodes.length) {
44689                 var cn = node.childNodes[0];
44690                 node.removeChild(cn);
44691                 node.parentNode.insertBefore(cn, node);
44692             }
44693             node.parentNode.removeChild(node);
44694             return;
44695         }
44696         
44697         if (!node.attributes || !node.attributes.length) {
44698             
44699           
44700             
44701             
44702             this.cleanUpChildren(node);
44703             return;
44704         }
44705         
44706         function cleanAttr(n,v)
44707         {
44708             
44709             if (v.match(/^\./) || v.match(/^\//)) {
44710                 return;
44711             }
44712             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44713                 return;
44714             }
44715             if (v.match(/^#/)) {
44716                 return;
44717             }
44718             if (v.match(/^\{/)) { // allow template editing.
44719                 return;
44720             }
44721 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44722             node.removeAttribute(n);
44723             
44724         }
44725         
44726         var cwhite = this.cwhite;
44727         var cblack = this.cblack;
44728             
44729         function cleanStyle(n,v)
44730         {
44731             if (v.match(/expression/)) { //XSS?? should we even bother..
44732                 node.removeAttribute(n);
44733                 return;
44734             }
44735             
44736             var parts = v.split(/;/);
44737             var clean = [];
44738             
44739             Roo.each(parts, function(p) {
44740                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44741                 if (!p.length) {
44742                     return true;
44743                 }
44744                 var l = p.split(':').shift().replace(/\s+/g,'');
44745                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44746                 
44747                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44748 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44749                     //node.removeAttribute(n);
44750                     return true;
44751                 }
44752                 //Roo.log()
44753                 // only allow 'c whitelisted system attributes'
44754                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44755 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44756                     //node.removeAttribute(n);
44757                     return true;
44758                 }
44759                 
44760                 
44761                  
44762                 
44763                 clean.push(p);
44764                 return true;
44765             });
44766             if (clean.length) { 
44767                 node.setAttribute(n, clean.join(';'));
44768             } else {
44769                 node.removeAttribute(n);
44770             }
44771             
44772         }
44773         
44774         
44775         for (var i = node.attributes.length-1; i > -1 ; i--) {
44776             var a = node.attributes[i];
44777             //console.log(a);
44778             
44779             if (a.name.toLowerCase().substr(0,2)=='on')  {
44780                 node.removeAttribute(a.name);
44781                 continue;
44782             }
44783             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44784                 node.removeAttribute(a.name);
44785                 continue;
44786             }
44787             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44788                 cleanAttr(a.name,a.value); // fixme..
44789                 continue;
44790             }
44791             if (a.name == 'style') {
44792                 cleanStyle(a.name,a.value);
44793                 continue;
44794             }
44795             /// clean up MS crap..
44796             // tecnically this should be a list of valid class'es..
44797             
44798             
44799             if (a.name == 'class') {
44800                 if (a.value.match(/^Mso/)) {
44801                     node.removeAttribute('class');
44802                 }
44803                 
44804                 if (a.value.match(/^body$/)) {
44805                     node.removeAttribute('class');
44806                 }
44807                 continue;
44808             }
44809             
44810             // style cleanup!?
44811             // class cleanup?
44812             
44813         }
44814         
44815         
44816         this.cleanUpChildren(node);
44817         
44818         
44819     },
44820     
44821     /**
44822      * Clean up MS wordisms...
44823      */
44824     cleanWord : function(node)
44825     {
44826         if (!node) {
44827             this.cleanWord(this.doc.body);
44828             return;
44829         }
44830         
44831         if(
44832                 node.nodeName == 'SPAN' &&
44833                 !node.hasAttributes() &&
44834                 node.childNodes.length == 1 &&
44835                 node.firstChild.nodeName == "#text"  
44836         ) {
44837             var textNode = node.firstChild;
44838             node.removeChild(textNode);
44839             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44840                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44841             }
44842             node.parentNode.insertBefore(textNode, node);
44843             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44844                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44845             }
44846             node.parentNode.removeChild(node);
44847         }
44848         
44849         if (node.nodeName == "#text") {
44850             // clean up silly Windows -- stuff?
44851             return; 
44852         }
44853         if (node.nodeName == "#comment") {
44854             node.parentNode.removeChild(node);
44855             // clean up silly Windows -- stuff?
44856             return; 
44857         }
44858         
44859         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44860             node.parentNode.removeChild(node);
44861             return;
44862         }
44863         //Roo.log(node.tagName);
44864         // remove - but keep children..
44865         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44866             //Roo.log('-- removed');
44867             while (node.childNodes.length) {
44868                 var cn = node.childNodes[0];
44869                 node.removeChild(cn);
44870                 node.parentNode.insertBefore(cn, node);
44871                 // move node to parent - and clean it..
44872                 this.cleanWord(cn);
44873             }
44874             node.parentNode.removeChild(node);
44875             /// no need to iterate chidlren = it's got none..
44876             //this.iterateChildren(node, this.cleanWord);
44877             return;
44878         }
44879         // clean styles
44880         if (node.className.length) {
44881             
44882             var cn = node.className.split(/\W+/);
44883             var cna = [];
44884             Roo.each(cn, function(cls) {
44885                 if (cls.match(/Mso[a-zA-Z]+/)) {
44886                     return;
44887                 }
44888                 cna.push(cls);
44889             });
44890             node.className = cna.length ? cna.join(' ') : '';
44891             if (!cna.length) {
44892                 node.removeAttribute("class");
44893             }
44894         }
44895         
44896         if (node.hasAttribute("lang")) {
44897             node.removeAttribute("lang");
44898         }
44899         
44900         if (node.hasAttribute("style")) {
44901             
44902             var styles = node.getAttribute("style").split(";");
44903             var nstyle = [];
44904             Roo.each(styles, function(s) {
44905                 if (!s.match(/:/)) {
44906                     return;
44907                 }
44908                 var kv = s.split(":");
44909                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44910                     return;
44911                 }
44912                 // what ever is left... we allow.
44913                 nstyle.push(s);
44914             });
44915             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44916             if (!nstyle.length) {
44917                 node.removeAttribute('style');
44918             }
44919         }
44920         this.iterateChildren(node, this.cleanWord);
44921         
44922         
44923         
44924     },
44925     /**
44926      * iterateChildren of a Node, calling fn each time, using this as the scole..
44927      * @param {DomNode} node node to iterate children of.
44928      * @param {Function} fn method of this class to call on each item.
44929      */
44930     iterateChildren : function(node, fn)
44931     {
44932         if (!node.childNodes.length) {
44933                 return;
44934         }
44935         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44936            fn.call(this, node.childNodes[i])
44937         }
44938     },
44939     
44940     
44941     /**
44942      * cleanTableWidths.
44943      *
44944      * Quite often pasting from word etc.. results in tables with column and widths.
44945      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44946      *
44947      */
44948     cleanTableWidths : function(node)
44949     {
44950          
44951          
44952         if (!node) {
44953             this.cleanTableWidths(this.doc.body);
44954             return;
44955         }
44956         
44957         // ignore list...
44958         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44959             return; 
44960         }
44961         Roo.log(node.tagName);
44962         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44963             this.iterateChildren(node, this.cleanTableWidths);
44964             return;
44965         }
44966         if (node.hasAttribute('width')) {
44967             node.removeAttribute('width');
44968         }
44969         
44970          
44971         if (node.hasAttribute("style")) {
44972             // pretty basic...
44973             
44974             var styles = node.getAttribute("style").split(";");
44975             var nstyle = [];
44976             Roo.each(styles, function(s) {
44977                 if (!s.match(/:/)) {
44978                     return;
44979                 }
44980                 var kv = s.split(":");
44981                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44982                     return;
44983                 }
44984                 // what ever is left... we allow.
44985                 nstyle.push(s);
44986             });
44987             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44988             if (!nstyle.length) {
44989                 node.removeAttribute('style');
44990             }
44991         }
44992         
44993         this.iterateChildren(node, this.cleanTableWidths);
44994         
44995         
44996     },
44997     
44998     
44999     
45000     
45001     domToHTML : function(currentElement, depth, nopadtext) {
45002         
45003         depth = depth || 0;
45004         nopadtext = nopadtext || false;
45005     
45006         if (!currentElement) {
45007             return this.domToHTML(this.doc.body);
45008         }
45009         
45010         //Roo.log(currentElement);
45011         var j;
45012         var allText = false;
45013         var nodeName = currentElement.nodeName;
45014         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45015         
45016         if  (nodeName == '#text') {
45017             
45018             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45019         }
45020         
45021         
45022         var ret = '';
45023         if (nodeName != 'BODY') {
45024              
45025             var i = 0;
45026             // Prints the node tagName, such as <A>, <IMG>, etc
45027             if (tagName) {
45028                 var attr = [];
45029                 for(i = 0; i < currentElement.attributes.length;i++) {
45030                     // quoting?
45031                     var aname = currentElement.attributes.item(i).name;
45032                     if (!currentElement.attributes.item(i).value.length) {
45033                         continue;
45034                     }
45035                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45036                 }
45037                 
45038                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45039             } 
45040             else {
45041                 
45042                 // eack
45043             }
45044         } else {
45045             tagName = false;
45046         }
45047         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45048             return ret;
45049         }
45050         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45051             nopadtext = true;
45052         }
45053         
45054         
45055         // Traverse the tree
45056         i = 0;
45057         var currentElementChild = currentElement.childNodes.item(i);
45058         var allText = true;
45059         var innerHTML  = '';
45060         lastnode = '';
45061         while (currentElementChild) {
45062             // Formatting code (indent the tree so it looks nice on the screen)
45063             var nopad = nopadtext;
45064             if (lastnode == 'SPAN') {
45065                 nopad  = true;
45066             }
45067             // text
45068             if  (currentElementChild.nodeName == '#text') {
45069                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45070                 toadd = nopadtext ? toadd : toadd.trim();
45071                 if (!nopad && toadd.length > 80) {
45072                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45073                 }
45074                 innerHTML  += toadd;
45075                 
45076                 i++;
45077                 currentElementChild = currentElement.childNodes.item(i);
45078                 lastNode = '';
45079                 continue;
45080             }
45081             allText = false;
45082             
45083             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45084                 
45085             // Recursively traverse the tree structure of the child node
45086             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45087             lastnode = currentElementChild.nodeName;
45088             i++;
45089             currentElementChild=currentElement.childNodes.item(i);
45090         }
45091         
45092         ret += innerHTML;
45093         
45094         if (!allText) {
45095                 // The remaining code is mostly for formatting the tree
45096             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45097         }
45098         
45099         
45100         if (tagName) {
45101             ret+= "</"+tagName+">";
45102         }
45103         return ret;
45104         
45105     },
45106         
45107     applyBlacklists : function()
45108     {
45109         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45110         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45111         
45112         this.white = [];
45113         this.black = [];
45114         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45115             if (b.indexOf(tag) > -1) {
45116                 return;
45117             }
45118             this.white.push(tag);
45119             
45120         }, this);
45121         
45122         Roo.each(w, function(tag) {
45123             if (b.indexOf(tag) > -1) {
45124                 return;
45125             }
45126             if (this.white.indexOf(tag) > -1) {
45127                 return;
45128             }
45129             this.white.push(tag);
45130             
45131         }, this);
45132         
45133         
45134         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45135             if (w.indexOf(tag) > -1) {
45136                 return;
45137             }
45138             this.black.push(tag);
45139             
45140         }, this);
45141         
45142         Roo.each(b, function(tag) {
45143             if (w.indexOf(tag) > -1) {
45144                 return;
45145             }
45146             if (this.black.indexOf(tag) > -1) {
45147                 return;
45148             }
45149             this.black.push(tag);
45150             
45151         }, this);
45152         
45153         
45154         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45155         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45156         
45157         this.cwhite = [];
45158         this.cblack = [];
45159         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45160             if (b.indexOf(tag) > -1) {
45161                 return;
45162             }
45163             this.cwhite.push(tag);
45164             
45165         }, this);
45166         
45167         Roo.each(w, function(tag) {
45168             if (b.indexOf(tag) > -1) {
45169                 return;
45170             }
45171             if (this.cwhite.indexOf(tag) > -1) {
45172                 return;
45173             }
45174             this.cwhite.push(tag);
45175             
45176         }, this);
45177         
45178         
45179         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45180             if (w.indexOf(tag) > -1) {
45181                 return;
45182             }
45183             this.cblack.push(tag);
45184             
45185         }, this);
45186         
45187         Roo.each(b, function(tag) {
45188             if (w.indexOf(tag) > -1) {
45189                 return;
45190             }
45191             if (this.cblack.indexOf(tag) > -1) {
45192                 return;
45193             }
45194             this.cblack.push(tag);
45195             
45196         }, this);
45197     },
45198     
45199     setStylesheets : function(stylesheets)
45200     {
45201         if(typeof(stylesheets) == 'string'){
45202             Roo.get(this.iframe.contentDocument.head).createChild({
45203                 tag : 'link',
45204                 rel : 'stylesheet',
45205                 type : 'text/css',
45206                 href : stylesheets
45207             });
45208             
45209             return;
45210         }
45211         var _this = this;
45212      
45213         Roo.each(stylesheets, function(s) {
45214             if(!s.length){
45215                 return;
45216             }
45217             
45218             Roo.get(_this.iframe.contentDocument.head).createChild({
45219                 tag : 'link',
45220                 rel : 'stylesheet',
45221                 type : 'text/css',
45222                 href : s
45223             });
45224         });
45225
45226         
45227     },
45228     
45229     removeStylesheets : function()
45230     {
45231         var _this = this;
45232         
45233         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45234             s.remove();
45235         });
45236     },
45237     
45238     setStyle : function(style)
45239     {
45240         Roo.get(this.iframe.contentDocument.head).createChild({
45241             tag : 'style',
45242             type : 'text/css',
45243             html : style
45244         });
45245
45246         return;
45247     }
45248     
45249     // hide stuff that is not compatible
45250     /**
45251      * @event blur
45252      * @hide
45253      */
45254     /**
45255      * @event change
45256      * @hide
45257      */
45258     /**
45259      * @event focus
45260      * @hide
45261      */
45262     /**
45263      * @event specialkey
45264      * @hide
45265      */
45266     /**
45267      * @cfg {String} fieldClass @hide
45268      */
45269     /**
45270      * @cfg {String} focusClass @hide
45271      */
45272     /**
45273      * @cfg {String} autoCreate @hide
45274      */
45275     /**
45276      * @cfg {String} inputType @hide
45277      */
45278     /**
45279      * @cfg {String} invalidClass @hide
45280      */
45281     /**
45282      * @cfg {String} invalidText @hide
45283      */
45284     /**
45285      * @cfg {String} msgFx @hide
45286      */
45287     /**
45288      * @cfg {String} validateOnBlur @hide
45289      */
45290 });
45291
45292 Roo.HtmlEditorCore.white = [
45293         'area', 'br', 'img', 'input', 'hr', 'wbr',
45294         
45295        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45296        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45297        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45298        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45299        'table',   'ul',         'xmp', 
45300        
45301        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45302       'thead',   'tr', 
45303      
45304       'dir', 'menu', 'ol', 'ul', 'dl',
45305        
45306       'embed',  'object'
45307 ];
45308
45309
45310 Roo.HtmlEditorCore.black = [
45311     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45312         'applet', // 
45313         'base',   'basefont', 'bgsound', 'blink',  'body', 
45314         'frame',  'frameset', 'head',    'html',   'ilayer', 
45315         'iframe', 'layer',  'link',     'meta',    'object',   
45316         'script', 'style' ,'title',  'xml' // clean later..
45317 ];
45318 Roo.HtmlEditorCore.clean = [
45319     'script', 'style', 'title', 'xml'
45320 ];
45321 Roo.HtmlEditorCore.remove = [
45322     'font'
45323 ];
45324 // attributes..
45325
45326 Roo.HtmlEditorCore.ablack = [
45327     'on'
45328 ];
45329     
45330 Roo.HtmlEditorCore.aclean = [ 
45331     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45332 ];
45333
45334 // protocols..
45335 Roo.HtmlEditorCore.pwhite= [
45336         'http',  'https',  'mailto'
45337 ];
45338
45339 // white listed style attributes.
45340 Roo.HtmlEditorCore.cwhite= [
45341       //  'text-align', /// default is to allow most things..
45342       
45343          
45344 //        'font-size'//??
45345 ];
45346
45347 // black listed style attributes.
45348 Roo.HtmlEditorCore.cblack= [
45349       //  'font-size' -- this can be set by the project 
45350 ];
45351
45352
45353 Roo.HtmlEditorCore.swapCodes   =[ 
45354     [    8211, "&#8211;" ], 
45355     [    8212, "&#8212;" ], 
45356     [    8216,  "'" ],  
45357     [    8217, "'" ],  
45358     [    8220, '"' ],  
45359     [    8221, '"' ],  
45360     [    8226, "*" ],  
45361     [    8230, "..." ]
45362 ]; 
45363
45364     //<script type="text/javascript">
45365
45366 /*
45367  * Ext JS Library 1.1.1
45368  * Copyright(c) 2006-2007, Ext JS, LLC.
45369  * Licence LGPL
45370  * 
45371  */
45372  
45373  
45374 Roo.form.HtmlEditor = function(config){
45375     
45376     
45377     
45378     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45379     
45380     if (!this.toolbars) {
45381         this.toolbars = [];
45382     }
45383     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45384     
45385     
45386 };
45387
45388 /**
45389  * @class Roo.form.HtmlEditor
45390  * @extends Roo.form.Field
45391  * Provides a lightweight HTML Editor component.
45392  *
45393  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45394  * 
45395  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45396  * supported by this editor.</b><br/><br/>
45397  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45398  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45399  */
45400 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45401     /**
45402      * @cfg {Boolean} clearUp
45403      */
45404     clearUp : true,
45405       /**
45406      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45407      */
45408     toolbars : false,
45409    
45410      /**
45411      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45412      *                        Roo.resizable.
45413      */
45414     resizable : false,
45415      /**
45416      * @cfg {Number} height (in pixels)
45417      */   
45418     height: 300,
45419    /**
45420      * @cfg {Number} width (in pixels)
45421      */   
45422     width: 500,
45423     
45424     /**
45425      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45426      * 
45427      */
45428     stylesheets: false,
45429     
45430     
45431      /**
45432      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45433      * 
45434      */
45435     cblack: false,
45436     /**
45437      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45438      * 
45439      */
45440     cwhite: false,
45441     
45442      /**
45443      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45444      * 
45445      */
45446     black: false,
45447     /**
45448      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45449      * 
45450      */
45451     white: false,
45452     
45453     // id of frame..
45454     frameId: false,
45455     
45456     // private properties
45457     validationEvent : false,
45458     deferHeight: true,
45459     initialized : false,
45460     activated : false,
45461     
45462     onFocus : Roo.emptyFn,
45463     iframePad:3,
45464     hideMode:'offsets',
45465     
45466     actionMode : 'container', // defaults to hiding it...
45467     
45468     defaultAutoCreate : { // modified by initCompnoent..
45469         tag: "textarea",
45470         style:"width:500px;height:300px;",
45471         autocomplete: "new-password"
45472     },
45473
45474     // private
45475     initComponent : function(){
45476         this.addEvents({
45477             /**
45478              * @event initialize
45479              * Fires when the editor is fully initialized (including the iframe)
45480              * @param {HtmlEditor} this
45481              */
45482             initialize: true,
45483             /**
45484              * @event activate
45485              * Fires when the editor is first receives the focus. Any insertion must wait
45486              * until after this event.
45487              * @param {HtmlEditor} this
45488              */
45489             activate: true,
45490              /**
45491              * @event beforesync
45492              * Fires before the textarea is updated with content from the editor iframe. Return false
45493              * to cancel the sync.
45494              * @param {HtmlEditor} this
45495              * @param {String} html
45496              */
45497             beforesync: true,
45498              /**
45499              * @event beforepush
45500              * Fires before the iframe editor is updated with content from the textarea. Return false
45501              * to cancel the push.
45502              * @param {HtmlEditor} this
45503              * @param {String} html
45504              */
45505             beforepush: true,
45506              /**
45507              * @event sync
45508              * Fires when the textarea is updated with content from the editor iframe.
45509              * @param {HtmlEditor} this
45510              * @param {String} html
45511              */
45512             sync: true,
45513              /**
45514              * @event push
45515              * Fires when the iframe editor is updated with content from the textarea.
45516              * @param {HtmlEditor} this
45517              * @param {String} html
45518              */
45519             push: true,
45520              /**
45521              * @event editmodechange
45522              * Fires when the editor switches edit modes
45523              * @param {HtmlEditor} this
45524              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45525              */
45526             editmodechange: true,
45527             /**
45528              * @event editorevent
45529              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45530              * @param {HtmlEditor} this
45531              */
45532             editorevent: true,
45533             /**
45534              * @event firstfocus
45535              * Fires when on first focus - needed by toolbars..
45536              * @param {HtmlEditor} this
45537              */
45538             firstfocus: true,
45539             /**
45540              * @event autosave
45541              * Auto save the htmlEditor value as a file into Events
45542              * @param {HtmlEditor} this
45543              */
45544             autosave: true,
45545             /**
45546              * @event savedpreview
45547              * preview the saved version of htmlEditor
45548              * @param {HtmlEditor} this
45549              */
45550             savedpreview: true,
45551             
45552             /**
45553             * @event stylesheetsclick
45554             * Fires when press the Sytlesheets button
45555             * @param {Roo.HtmlEditorCore} this
45556             */
45557             stylesheetsclick: true
45558         });
45559         this.defaultAutoCreate =  {
45560             tag: "textarea",
45561             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45562             autocomplete: "new-password"
45563         };
45564     },
45565
45566     /**
45567      * Protected method that will not generally be called directly. It
45568      * is called when the editor creates its toolbar. Override this method if you need to
45569      * add custom toolbar buttons.
45570      * @param {HtmlEditor} editor
45571      */
45572     createToolbar : function(editor){
45573         Roo.log("create toolbars");
45574         if (!editor.toolbars || !editor.toolbars.length) {
45575             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45576         }
45577         
45578         for (var i =0 ; i < editor.toolbars.length;i++) {
45579             editor.toolbars[i] = Roo.factory(
45580                     typeof(editor.toolbars[i]) == 'string' ?
45581                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45582                 Roo.form.HtmlEditor);
45583             editor.toolbars[i].init(editor);
45584         }
45585          
45586         
45587     },
45588
45589      
45590     // private
45591     onRender : function(ct, position)
45592     {
45593         var _t = this;
45594         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45595         
45596         this.wrap = this.el.wrap({
45597             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45598         });
45599         
45600         this.editorcore.onRender(ct, position);
45601          
45602         if (this.resizable) {
45603             this.resizeEl = new Roo.Resizable(this.wrap, {
45604                 pinned : true,
45605                 wrap: true,
45606                 dynamic : true,
45607                 minHeight : this.height,
45608                 height: this.height,
45609                 handles : this.resizable,
45610                 width: this.width,
45611                 listeners : {
45612                     resize : function(r, w, h) {
45613                         _t.onResize(w,h); // -something
45614                     }
45615                 }
45616             });
45617             
45618         }
45619         this.createToolbar(this);
45620        
45621         
45622         if(!this.width){
45623             this.setSize(this.wrap.getSize());
45624         }
45625         if (this.resizeEl) {
45626             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45627             // should trigger onReize..
45628         }
45629         
45630         this.keyNav = new Roo.KeyNav(this.el, {
45631             
45632             "tab" : function(e){
45633                 e.preventDefault();
45634                 
45635                 var value = this.getValue();
45636                 
45637                 var start = this.el.dom.selectionStart;
45638                 var end = this.el.dom.selectionEnd;
45639                 
45640                 if(!e.shiftKey){
45641                     
45642                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45643                     this.el.dom.setSelectionRange(end + 1, end + 1);
45644                     return;
45645                 }
45646                 
45647                 var f = value.substring(0, start).split("\t");
45648                 
45649                 if(f.pop().length != 0){
45650                     return;
45651                 }
45652                 
45653                 this.setValue(f.join("\t") + value.substring(end));
45654                 this.el.dom.setSelectionRange(start - 1, start - 1);
45655                 
45656             },
45657             
45658             "home" : function(e){
45659                 e.preventDefault();
45660                 
45661                 var curr = this.el.dom.selectionStart;
45662                 var lines = this.getValue().split("\n");
45663                 
45664                 if(!lines.length){
45665                     return;
45666                 }
45667                 
45668                 if(e.ctrlKey){
45669                     this.el.dom.setSelectionRange(0, 0);
45670                     return;
45671                 }
45672                 
45673                 var pos = 0;
45674                 
45675                 for (var i = 0; i < lines.length;i++) {
45676                     pos += lines[i].length;
45677                     
45678                     if(i != 0){
45679                         pos += 1;
45680                     }
45681                     
45682                     if(pos < curr){
45683                         continue;
45684                     }
45685                     
45686                     pos -= lines[i].length;
45687                     
45688                     break;
45689                 }
45690                 
45691                 if(!e.shiftKey){
45692                     this.el.dom.setSelectionRange(pos, pos);
45693                     return;
45694                 }
45695                 
45696                 this.el.dom.selectionStart = pos;
45697                 this.el.dom.selectionEnd = curr;
45698             },
45699             
45700             "end" : function(e){
45701                 e.preventDefault();
45702                 
45703                 var curr = this.el.dom.selectionStart;
45704                 var lines = this.getValue().split("\n");
45705                 
45706                 if(!lines.length){
45707                     return;
45708                 }
45709                 
45710                 if(e.ctrlKey){
45711                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45712                     return;
45713                 }
45714                 
45715                 var pos = 0;
45716                 
45717                 for (var i = 0; i < lines.length;i++) {
45718                     
45719                     pos += lines[i].length;
45720                     
45721                     if(i != 0){
45722                         pos += 1;
45723                     }
45724                     
45725                     if(pos < curr){
45726                         continue;
45727                     }
45728                     
45729                     break;
45730                 }
45731                 
45732                 if(!e.shiftKey){
45733                     this.el.dom.setSelectionRange(pos, pos);
45734                     return;
45735                 }
45736                 
45737                 this.el.dom.selectionStart = curr;
45738                 this.el.dom.selectionEnd = pos;
45739             },
45740
45741             scope : this,
45742
45743             doRelay : function(foo, bar, hname){
45744                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45745             },
45746
45747             forceKeyDown: true
45748         });
45749         
45750 //        if(this.autosave && this.w){
45751 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45752 //        }
45753     },
45754
45755     // private
45756     onResize : function(w, h)
45757     {
45758         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45759         var ew = false;
45760         var eh = false;
45761         
45762         if(this.el ){
45763             if(typeof w == 'number'){
45764                 var aw = w - this.wrap.getFrameWidth('lr');
45765                 this.el.setWidth(this.adjustWidth('textarea', aw));
45766                 ew = aw;
45767             }
45768             if(typeof h == 'number'){
45769                 var tbh = 0;
45770                 for (var i =0; i < this.toolbars.length;i++) {
45771                     // fixme - ask toolbars for heights?
45772                     tbh += this.toolbars[i].tb.el.getHeight();
45773                     if (this.toolbars[i].footer) {
45774                         tbh += this.toolbars[i].footer.el.getHeight();
45775                     }
45776                 }
45777                 
45778                 
45779                 
45780                 
45781                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45782                 ah -= 5; // knock a few pixes off for look..
45783 //                Roo.log(ah);
45784                 this.el.setHeight(this.adjustWidth('textarea', ah));
45785                 var eh = ah;
45786             }
45787         }
45788         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45789         this.editorcore.onResize(ew,eh);
45790         
45791     },
45792
45793     /**
45794      * Toggles the editor between standard and source edit mode.
45795      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45796      */
45797     toggleSourceEdit : function(sourceEditMode)
45798     {
45799         this.editorcore.toggleSourceEdit(sourceEditMode);
45800         
45801         if(this.editorcore.sourceEditMode){
45802             Roo.log('editor - showing textarea');
45803             
45804 //            Roo.log('in');
45805 //            Roo.log(this.syncValue());
45806             this.editorcore.syncValue();
45807             this.el.removeClass('x-hidden');
45808             this.el.dom.removeAttribute('tabIndex');
45809             this.el.focus();
45810             
45811             for (var i = 0; i < this.toolbars.length; i++) {
45812                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45813                     this.toolbars[i].tb.hide();
45814                     this.toolbars[i].footer.hide();
45815                 }
45816             }
45817             
45818         }else{
45819             Roo.log('editor - hiding textarea');
45820 //            Roo.log('out')
45821 //            Roo.log(this.pushValue()); 
45822             this.editorcore.pushValue();
45823             
45824             this.el.addClass('x-hidden');
45825             this.el.dom.setAttribute('tabIndex', -1);
45826             
45827             for (var i = 0; i < this.toolbars.length; i++) {
45828                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45829                     this.toolbars[i].tb.show();
45830                     this.toolbars[i].footer.show();
45831                 }
45832             }
45833             
45834             //this.deferFocus();
45835         }
45836         
45837         this.setSize(this.wrap.getSize());
45838         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45839         
45840         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45841     },
45842  
45843     // private (for BoxComponent)
45844     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45845
45846     // private (for BoxComponent)
45847     getResizeEl : function(){
45848         return this.wrap;
45849     },
45850
45851     // private (for BoxComponent)
45852     getPositionEl : function(){
45853         return this.wrap;
45854     },
45855
45856     // private
45857     initEvents : function(){
45858         this.originalValue = this.getValue();
45859     },
45860
45861     /**
45862      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45863      * @method
45864      */
45865     markInvalid : Roo.emptyFn,
45866     /**
45867      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45868      * @method
45869      */
45870     clearInvalid : Roo.emptyFn,
45871
45872     setValue : function(v){
45873         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45874         this.editorcore.pushValue();
45875     },
45876
45877      
45878     // private
45879     deferFocus : function(){
45880         this.focus.defer(10, this);
45881     },
45882
45883     // doc'ed in Field
45884     focus : function(){
45885         this.editorcore.focus();
45886         
45887     },
45888       
45889
45890     // private
45891     onDestroy : function(){
45892         
45893         
45894         
45895         if(this.rendered){
45896             
45897             for (var i =0; i < this.toolbars.length;i++) {
45898                 // fixme - ask toolbars for heights?
45899                 this.toolbars[i].onDestroy();
45900             }
45901             
45902             this.wrap.dom.innerHTML = '';
45903             this.wrap.remove();
45904         }
45905     },
45906
45907     // private
45908     onFirstFocus : function(){
45909         //Roo.log("onFirstFocus");
45910         this.editorcore.onFirstFocus();
45911          for (var i =0; i < this.toolbars.length;i++) {
45912             this.toolbars[i].onFirstFocus();
45913         }
45914         
45915     },
45916     
45917     // private
45918     syncValue : function()
45919     {
45920         this.editorcore.syncValue();
45921     },
45922     
45923     pushValue : function()
45924     {
45925         this.editorcore.pushValue();
45926     },
45927     
45928     setStylesheets : function(stylesheets)
45929     {
45930         this.editorcore.setStylesheets(stylesheets);
45931     },
45932     
45933     removeStylesheets : function()
45934     {
45935         this.editorcore.removeStylesheets();
45936     }
45937      
45938     
45939     // hide stuff that is not compatible
45940     /**
45941      * @event blur
45942      * @hide
45943      */
45944     /**
45945      * @event change
45946      * @hide
45947      */
45948     /**
45949      * @event focus
45950      * @hide
45951      */
45952     /**
45953      * @event specialkey
45954      * @hide
45955      */
45956     /**
45957      * @cfg {String} fieldClass @hide
45958      */
45959     /**
45960      * @cfg {String} focusClass @hide
45961      */
45962     /**
45963      * @cfg {String} autoCreate @hide
45964      */
45965     /**
45966      * @cfg {String} inputType @hide
45967      */
45968     /**
45969      * @cfg {String} invalidClass @hide
45970      */
45971     /**
45972      * @cfg {String} invalidText @hide
45973      */
45974     /**
45975      * @cfg {String} msgFx @hide
45976      */
45977     /**
45978      * @cfg {String} validateOnBlur @hide
45979      */
45980 });
45981  
45982     // <script type="text/javascript">
45983 /*
45984  * Based on
45985  * Ext JS Library 1.1.1
45986  * Copyright(c) 2006-2007, Ext JS, LLC.
45987  *  
45988  
45989  */
45990
45991 /**
45992  * @class Roo.form.HtmlEditorToolbar1
45993  * Basic Toolbar
45994  * 
45995  * Usage:
45996  *
45997  new Roo.form.HtmlEditor({
45998     ....
45999     toolbars : [
46000         new Roo.form.HtmlEditorToolbar1({
46001             disable : { fonts: 1 , format: 1, ..., ... , ...],
46002             btns : [ .... ]
46003         })
46004     }
46005      
46006  * 
46007  * @cfg {Object} disable List of elements to disable..
46008  * @cfg {Array} btns List of additional buttons.
46009  * 
46010  * 
46011  * NEEDS Extra CSS? 
46012  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46013  */
46014  
46015 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46016 {
46017     
46018     Roo.apply(this, config);
46019     
46020     // default disabled, based on 'good practice'..
46021     this.disable = this.disable || {};
46022     Roo.applyIf(this.disable, {
46023         fontSize : true,
46024         colors : true,
46025         specialElements : true
46026     });
46027     
46028     
46029     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46030     // dont call parent... till later.
46031 }
46032
46033 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46034     
46035     tb: false,
46036     
46037     rendered: false,
46038     
46039     editor : false,
46040     editorcore : false,
46041     /**
46042      * @cfg {Object} disable  List of toolbar elements to disable
46043          
46044      */
46045     disable : false,
46046     
46047     
46048      /**
46049      * @cfg {String} createLinkText The default text for the create link prompt
46050      */
46051     createLinkText : 'Please enter the URL for the link:',
46052     /**
46053      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46054      */
46055     defaultLinkValue : 'http:/'+'/',
46056    
46057     
46058       /**
46059      * @cfg {Array} fontFamilies An array of available font families
46060      */
46061     fontFamilies : [
46062         'Arial',
46063         'Courier New',
46064         'Tahoma',
46065         'Times New Roman',
46066         'Verdana'
46067     ],
46068     
46069     specialChars : [
46070            "&#169;",
46071           "&#174;",     
46072           "&#8482;",    
46073           "&#163;" ,    
46074          // "&#8212;",    
46075           "&#8230;",    
46076           "&#247;" ,    
46077         //  "&#225;" ,     ?? a acute?
46078            "&#8364;"    , //Euro
46079        //   "&#8220;"    ,
46080         //  "&#8221;"    ,
46081         //  "&#8226;"    ,
46082           "&#176;"  //   , // degrees
46083
46084          // "&#233;"     , // e ecute
46085          // "&#250;"     , // u ecute?
46086     ],
46087     
46088     specialElements : [
46089         {
46090             text: "Insert Table",
46091             xtype: 'MenuItem',
46092             xns : Roo.Menu,
46093             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46094                 
46095         },
46096         {    
46097             text: "Insert Image",
46098             xtype: 'MenuItem',
46099             xns : Roo.Menu,
46100             ihtml : '<img src="about:blank"/>'
46101             
46102         }
46103         
46104          
46105     ],
46106     
46107     
46108     inputElements : [ 
46109             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46110             "input:submit", "input:button", "select", "textarea", "label" ],
46111     formats : [
46112         ["p"] ,  
46113         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46114         ["pre"],[ "code"], 
46115         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46116         ['div'],['span'],
46117         ['sup'],['sub']
46118     ],
46119     
46120     cleanStyles : [
46121         "font-size"
46122     ],
46123      /**
46124      * @cfg {String} defaultFont default font to use.
46125      */
46126     defaultFont: 'tahoma',
46127    
46128     fontSelect : false,
46129     
46130     
46131     formatCombo : false,
46132     
46133     init : function(editor)
46134     {
46135         this.editor = editor;
46136         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46137         var editorcore = this.editorcore;
46138         
46139         var _t = this;
46140         
46141         var fid = editorcore.frameId;
46142         var etb = this;
46143         function btn(id, toggle, handler){
46144             var xid = fid + '-'+ id ;
46145             return {
46146                 id : xid,
46147                 cmd : id,
46148                 cls : 'x-btn-icon x-edit-'+id,
46149                 enableToggle:toggle !== false,
46150                 scope: _t, // was editor...
46151                 handler:handler||_t.relayBtnCmd,
46152                 clickEvent:'mousedown',
46153                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46154                 tabIndex:-1
46155             };
46156         }
46157         
46158         
46159         
46160         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46161         this.tb = tb;
46162          // stop form submits
46163         tb.el.on('click', function(e){
46164             e.preventDefault(); // what does this do?
46165         });
46166
46167         if(!this.disable.font) { // && !Roo.isSafari){
46168             /* why no safari for fonts 
46169             editor.fontSelect = tb.el.createChild({
46170                 tag:'select',
46171                 tabIndex: -1,
46172                 cls:'x-font-select',
46173                 html: this.createFontOptions()
46174             });
46175             
46176             editor.fontSelect.on('change', function(){
46177                 var font = editor.fontSelect.dom.value;
46178                 editor.relayCmd('fontname', font);
46179                 editor.deferFocus();
46180             }, editor);
46181             
46182             tb.add(
46183                 editor.fontSelect.dom,
46184                 '-'
46185             );
46186             */
46187             
46188         };
46189         if(!this.disable.formats){
46190             this.formatCombo = new Roo.form.ComboBox({
46191                 store: new Roo.data.SimpleStore({
46192                     id : 'tag',
46193                     fields: ['tag'],
46194                     data : this.formats // from states.js
46195                 }),
46196                 blockFocus : true,
46197                 name : '',
46198                 //autoCreate : {tag: "div",  size: "20"},
46199                 displayField:'tag',
46200                 typeAhead: false,
46201                 mode: 'local',
46202                 editable : false,
46203                 triggerAction: 'all',
46204                 emptyText:'Add tag',
46205                 selectOnFocus:true,
46206                 width:135,
46207                 listeners : {
46208                     'select': function(c, r, i) {
46209                         editorcore.insertTag(r.get('tag'));
46210                         editor.focus();
46211                     }
46212                 }
46213
46214             });
46215             tb.addField(this.formatCombo);
46216             
46217         }
46218         
46219         if(!this.disable.format){
46220             tb.add(
46221                 btn('bold'),
46222                 btn('italic'),
46223                 btn('underline'),
46224                 btn('strikethrough')
46225             );
46226         };
46227         if(!this.disable.fontSize){
46228             tb.add(
46229                 '-',
46230                 
46231                 
46232                 btn('increasefontsize', false, editorcore.adjustFont),
46233                 btn('decreasefontsize', false, editorcore.adjustFont)
46234             );
46235         };
46236         
46237         
46238         if(!this.disable.colors){
46239             tb.add(
46240                 '-', {
46241                     id:editorcore.frameId +'-forecolor',
46242                     cls:'x-btn-icon x-edit-forecolor',
46243                     clickEvent:'mousedown',
46244                     tooltip: this.buttonTips['forecolor'] || undefined,
46245                     tabIndex:-1,
46246                     menu : new Roo.menu.ColorMenu({
46247                         allowReselect: true,
46248                         focus: Roo.emptyFn,
46249                         value:'000000',
46250                         plain:true,
46251                         selectHandler: function(cp, color){
46252                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46253                             editor.deferFocus();
46254                         },
46255                         scope: editorcore,
46256                         clickEvent:'mousedown'
46257                     })
46258                 }, {
46259                     id:editorcore.frameId +'backcolor',
46260                     cls:'x-btn-icon x-edit-backcolor',
46261                     clickEvent:'mousedown',
46262                     tooltip: this.buttonTips['backcolor'] || undefined,
46263                     tabIndex:-1,
46264                     menu : new Roo.menu.ColorMenu({
46265                         focus: Roo.emptyFn,
46266                         value:'FFFFFF',
46267                         plain:true,
46268                         allowReselect: true,
46269                         selectHandler: function(cp, color){
46270                             if(Roo.isGecko){
46271                                 editorcore.execCmd('useCSS', false);
46272                                 editorcore.execCmd('hilitecolor', color);
46273                                 editorcore.execCmd('useCSS', true);
46274                                 editor.deferFocus();
46275                             }else{
46276                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46277                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46278                                 editor.deferFocus();
46279                             }
46280                         },
46281                         scope:editorcore,
46282                         clickEvent:'mousedown'
46283                     })
46284                 }
46285             );
46286         };
46287         // now add all the items...
46288         
46289
46290         if(!this.disable.alignments){
46291             tb.add(
46292                 '-',
46293                 btn('justifyleft'),
46294                 btn('justifycenter'),
46295                 btn('justifyright')
46296             );
46297         };
46298
46299         //if(!Roo.isSafari){
46300             if(!this.disable.links){
46301                 tb.add(
46302                     '-',
46303                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46304                 );
46305             };
46306
46307             if(!this.disable.lists){
46308                 tb.add(
46309                     '-',
46310                     btn('insertorderedlist'),
46311                     btn('insertunorderedlist')
46312                 );
46313             }
46314             if(!this.disable.sourceEdit){
46315                 tb.add(
46316                     '-',
46317                     btn('sourceedit', true, function(btn){
46318                         this.toggleSourceEdit(btn.pressed);
46319                     })
46320                 );
46321             }
46322         //}
46323         
46324         var smenu = { };
46325         // special menu.. - needs to be tidied up..
46326         if (!this.disable.special) {
46327             smenu = {
46328                 text: "&#169;",
46329                 cls: 'x-edit-none',
46330                 
46331                 menu : {
46332                     items : []
46333                 }
46334             };
46335             for (var i =0; i < this.specialChars.length; i++) {
46336                 smenu.menu.items.push({
46337                     
46338                     html: this.specialChars[i],
46339                     handler: function(a,b) {
46340                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46341                         //editor.insertAtCursor(a.html);
46342                         
46343                     },
46344                     tabIndex:-1
46345                 });
46346             }
46347             
46348             
46349             tb.add(smenu);
46350             
46351             
46352         }
46353         
46354         var cmenu = { };
46355         if (!this.disable.cleanStyles) {
46356             cmenu = {
46357                 cls: 'x-btn-icon x-btn-clear',
46358                 
46359                 menu : {
46360                     items : []
46361                 }
46362             };
46363             for (var i =0; i < this.cleanStyles.length; i++) {
46364                 cmenu.menu.items.push({
46365                     actiontype : this.cleanStyles[i],
46366                     html: 'Remove ' + this.cleanStyles[i],
46367                     handler: function(a,b) {
46368 //                        Roo.log(a);
46369 //                        Roo.log(b);
46370                         var c = Roo.get(editorcore.doc.body);
46371                         c.select('[style]').each(function(s) {
46372                             s.dom.style.removeProperty(a.actiontype);
46373                         });
46374                         editorcore.syncValue();
46375                     },
46376                     tabIndex:-1
46377                 });
46378             }
46379              cmenu.menu.items.push({
46380                 actiontype : 'tablewidths',
46381                 html: 'Remove Table Widths',
46382                 handler: function(a,b) {
46383                     editorcore.cleanTableWidths();
46384                     editorcore.syncValue();
46385                 },
46386                 tabIndex:-1
46387             });
46388             cmenu.menu.items.push({
46389                 actiontype : 'word',
46390                 html: 'Remove MS Word Formating',
46391                 handler: function(a,b) {
46392                     editorcore.cleanWord();
46393                     editorcore.syncValue();
46394                 },
46395                 tabIndex:-1
46396             });
46397             
46398             cmenu.menu.items.push({
46399                 actiontype : 'all',
46400                 html: 'Remove All Styles',
46401                 handler: function(a,b) {
46402                     
46403                     var c = Roo.get(editorcore.doc.body);
46404                     c.select('[style]').each(function(s) {
46405                         s.dom.removeAttribute('style');
46406                     });
46407                     editorcore.syncValue();
46408                 },
46409                 tabIndex:-1
46410             });
46411             
46412             cmenu.menu.items.push({
46413                 actiontype : 'all',
46414                 html: 'Remove All CSS Classes',
46415                 handler: function(a,b) {
46416                     
46417                     var c = Roo.get(editorcore.doc.body);
46418                     c.select('[class]').each(function(s) {
46419                         s.dom.removeAttribute('class');
46420                     });
46421                     editorcore.cleanWord();
46422                     editorcore.syncValue();
46423                 },
46424                 tabIndex:-1
46425             });
46426             
46427              cmenu.menu.items.push({
46428                 actiontype : 'tidy',
46429                 html: 'Tidy HTML Source',
46430                 handler: function(a,b) {
46431                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46432                     editorcore.syncValue();
46433                 },
46434                 tabIndex:-1
46435             });
46436             
46437             
46438             tb.add(cmenu);
46439         }
46440          
46441         if (!this.disable.specialElements) {
46442             var semenu = {
46443                 text: "Other;",
46444                 cls: 'x-edit-none',
46445                 menu : {
46446                     items : []
46447                 }
46448             };
46449             for (var i =0; i < this.specialElements.length; i++) {
46450                 semenu.menu.items.push(
46451                     Roo.apply({ 
46452                         handler: function(a,b) {
46453                             editor.insertAtCursor(this.ihtml);
46454                         }
46455                     }, this.specialElements[i])
46456                 );
46457                     
46458             }
46459             
46460             tb.add(semenu);
46461             
46462             
46463         }
46464          
46465         
46466         if (this.btns) {
46467             for(var i =0; i< this.btns.length;i++) {
46468                 var b = Roo.factory(this.btns[i],Roo.form);
46469                 b.cls =  'x-edit-none';
46470                 
46471                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46472                     b.cls += ' x-init-enable';
46473                 }
46474                 
46475                 b.scope = editorcore;
46476                 tb.add(b);
46477             }
46478         
46479         }
46480         
46481         
46482         
46483         // disable everything...
46484         
46485         this.tb.items.each(function(item){
46486             
46487            if(
46488                 item.id != editorcore.frameId+ '-sourceedit' && 
46489                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46490             ){
46491                 
46492                 item.disable();
46493             }
46494         });
46495         this.rendered = true;
46496         
46497         // the all the btns;
46498         editor.on('editorevent', this.updateToolbar, this);
46499         // other toolbars need to implement this..
46500         //editor.on('editmodechange', this.updateToolbar, this);
46501     },
46502     
46503     
46504     relayBtnCmd : function(btn) {
46505         this.editorcore.relayCmd(btn.cmd);
46506     },
46507     // private used internally
46508     createLink : function(){
46509         Roo.log("create link?");
46510         var url = prompt(this.createLinkText, this.defaultLinkValue);
46511         if(url && url != 'http:/'+'/'){
46512             this.editorcore.relayCmd('createlink', url);
46513         }
46514     },
46515
46516     
46517     /**
46518      * Protected method that will not generally be called directly. It triggers
46519      * a toolbar update by reading the markup state of the current selection in the editor.
46520      */
46521     updateToolbar: function(){
46522
46523         if(!this.editorcore.activated){
46524             this.editor.onFirstFocus();
46525             return;
46526         }
46527
46528         var btns = this.tb.items.map, 
46529             doc = this.editorcore.doc,
46530             frameId = this.editorcore.frameId;
46531
46532         if(!this.disable.font && !Roo.isSafari){
46533             /*
46534             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46535             if(name != this.fontSelect.dom.value){
46536                 this.fontSelect.dom.value = name;
46537             }
46538             */
46539         }
46540         if(!this.disable.format){
46541             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46542             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46543             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46544             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46545         }
46546         if(!this.disable.alignments){
46547             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46548             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46549             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46550         }
46551         if(!Roo.isSafari && !this.disable.lists){
46552             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46553             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46554         }
46555         
46556         var ans = this.editorcore.getAllAncestors();
46557         if (this.formatCombo) {
46558             
46559             
46560             var store = this.formatCombo.store;
46561             this.formatCombo.setValue("");
46562             for (var i =0; i < ans.length;i++) {
46563                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46564                     // select it..
46565                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46566                     break;
46567                 }
46568             }
46569         }
46570         
46571         
46572         
46573         // hides menus... - so this cant be on a menu...
46574         Roo.menu.MenuMgr.hideAll();
46575
46576         //this.editorsyncValue();
46577     },
46578    
46579     
46580     createFontOptions : function(){
46581         var buf = [], fs = this.fontFamilies, ff, lc;
46582         
46583         
46584         
46585         for(var i = 0, len = fs.length; i< len; i++){
46586             ff = fs[i];
46587             lc = ff.toLowerCase();
46588             buf.push(
46589                 '<option value="',lc,'" style="font-family:',ff,';"',
46590                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46591                     ff,
46592                 '</option>'
46593             );
46594         }
46595         return buf.join('');
46596     },
46597     
46598     toggleSourceEdit : function(sourceEditMode){
46599         
46600         Roo.log("toolbar toogle");
46601         if(sourceEditMode === undefined){
46602             sourceEditMode = !this.sourceEditMode;
46603         }
46604         this.sourceEditMode = sourceEditMode === true;
46605         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46606         // just toggle the button?
46607         if(btn.pressed !== this.sourceEditMode){
46608             btn.toggle(this.sourceEditMode);
46609             return;
46610         }
46611         
46612         if(sourceEditMode){
46613             Roo.log("disabling buttons");
46614             this.tb.items.each(function(item){
46615                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46616                     item.disable();
46617                 }
46618             });
46619           
46620         }else{
46621             Roo.log("enabling buttons");
46622             if(this.editorcore.initialized){
46623                 this.tb.items.each(function(item){
46624                     item.enable();
46625                 });
46626             }
46627             
46628         }
46629         Roo.log("calling toggole on editor");
46630         // tell the editor that it's been pressed..
46631         this.editor.toggleSourceEdit(sourceEditMode);
46632        
46633     },
46634      /**
46635      * Object collection of toolbar tooltips for the buttons in the editor. The key
46636      * is the command id associated with that button and the value is a valid QuickTips object.
46637      * For example:
46638 <pre><code>
46639 {
46640     bold : {
46641         title: 'Bold (Ctrl+B)',
46642         text: 'Make the selected text bold.',
46643         cls: 'x-html-editor-tip'
46644     },
46645     italic : {
46646         title: 'Italic (Ctrl+I)',
46647         text: 'Make the selected text italic.',
46648         cls: 'x-html-editor-tip'
46649     },
46650     ...
46651 </code></pre>
46652     * @type Object
46653      */
46654     buttonTips : {
46655         bold : {
46656             title: 'Bold (Ctrl+B)',
46657             text: 'Make the selected text bold.',
46658             cls: 'x-html-editor-tip'
46659         },
46660         italic : {
46661             title: 'Italic (Ctrl+I)',
46662             text: 'Make the selected text italic.',
46663             cls: 'x-html-editor-tip'
46664         },
46665         underline : {
46666             title: 'Underline (Ctrl+U)',
46667             text: 'Underline the selected text.',
46668             cls: 'x-html-editor-tip'
46669         },
46670         strikethrough : {
46671             title: 'Strikethrough',
46672             text: 'Strikethrough the selected text.',
46673             cls: 'x-html-editor-tip'
46674         },
46675         increasefontsize : {
46676             title: 'Grow Text',
46677             text: 'Increase the font size.',
46678             cls: 'x-html-editor-tip'
46679         },
46680         decreasefontsize : {
46681             title: 'Shrink Text',
46682             text: 'Decrease the font size.',
46683             cls: 'x-html-editor-tip'
46684         },
46685         backcolor : {
46686             title: 'Text Highlight Color',
46687             text: 'Change the background color of the selected text.',
46688             cls: 'x-html-editor-tip'
46689         },
46690         forecolor : {
46691             title: 'Font Color',
46692             text: 'Change the color of the selected text.',
46693             cls: 'x-html-editor-tip'
46694         },
46695         justifyleft : {
46696             title: 'Align Text Left',
46697             text: 'Align text to the left.',
46698             cls: 'x-html-editor-tip'
46699         },
46700         justifycenter : {
46701             title: 'Center Text',
46702             text: 'Center text in the editor.',
46703             cls: 'x-html-editor-tip'
46704         },
46705         justifyright : {
46706             title: 'Align Text Right',
46707             text: 'Align text to the right.',
46708             cls: 'x-html-editor-tip'
46709         },
46710         insertunorderedlist : {
46711             title: 'Bullet List',
46712             text: 'Start a bulleted list.',
46713             cls: 'x-html-editor-tip'
46714         },
46715         insertorderedlist : {
46716             title: 'Numbered List',
46717             text: 'Start a numbered list.',
46718             cls: 'x-html-editor-tip'
46719         },
46720         createlink : {
46721             title: 'Hyperlink',
46722             text: 'Make the selected text a hyperlink.',
46723             cls: 'x-html-editor-tip'
46724         },
46725         sourceedit : {
46726             title: 'Source Edit',
46727             text: 'Switch to source editing mode.',
46728             cls: 'x-html-editor-tip'
46729         }
46730     },
46731     // private
46732     onDestroy : function(){
46733         if(this.rendered){
46734             
46735             this.tb.items.each(function(item){
46736                 if(item.menu){
46737                     item.menu.removeAll();
46738                     if(item.menu.el){
46739                         item.menu.el.destroy();
46740                     }
46741                 }
46742                 item.destroy();
46743             });
46744              
46745         }
46746     },
46747     onFirstFocus: function() {
46748         this.tb.items.each(function(item){
46749            item.enable();
46750         });
46751     }
46752 });
46753
46754
46755
46756
46757 // <script type="text/javascript">
46758 /*
46759  * Based on
46760  * Ext JS Library 1.1.1
46761  * Copyright(c) 2006-2007, Ext JS, LLC.
46762  *  
46763  
46764  */
46765
46766  
46767 /**
46768  * @class Roo.form.HtmlEditor.ToolbarContext
46769  * Context Toolbar
46770  * 
46771  * Usage:
46772  *
46773  new Roo.form.HtmlEditor({
46774     ....
46775     toolbars : [
46776         { xtype: 'ToolbarStandard', styles : {} }
46777         { xtype: 'ToolbarContext', disable : {} }
46778     ]
46779 })
46780
46781      
46782  * 
46783  * @config : {Object} disable List of elements to disable.. (not done yet.)
46784  * @config : {Object} styles  Map of styles available.
46785  * 
46786  */
46787
46788 Roo.form.HtmlEditor.ToolbarContext = function(config)
46789 {
46790     
46791     Roo.apply(this, config);
46792     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46793     // dont call parent... till later.
46794     this.styles = this.styles || {};
46795 }
46796
46797  
46798
46799 Roo.form.HtmlEditor.ToolbarContext.types = {
46800     'IMG' : {
46801         width : {
46802             title: "Width",
46803             width: 40
46804         },
46805         height:  {
46806             title: "Height",
46807             width: 40
46808         },
46809         align: {
46810             title: "Align",
46811             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46812             width : 80
46813             
46814         },
46815         border: {
46816             title: "Border",
46817             width: 40
46818         },
46819         alt: {
46820             title: "Alt",
46821             width: 120
46822         },
46823         src : {
46824             title: "Src",
46825             width: 220
46826         }
46827         
46828     },
46829     'A' : {
46830         name : {
46831             title: "Name",
46832             width: 50
46833         },
46834         target:  {
46835             title: "Target",
46836             width: 120
46837         },
46838         href:  {
46839             title: "Href",
46840             width: 220
46841         } // border?
46842         
46843     },
46844     'TABLE' : {
46845         rows : {
46846             title: "Rows",
46847             width: 20
46848         },
46849         cols : {
46850             title: "Cols",
46851             width: 20
46852         },
46853         width : {
46854             title: "Width",
46855             width: 40
46856         },
46857         height : {
46858             title: "Height",
46859             width: 40
46860         },
46861         border : {
46862             title: "Border",
46863             width: 20
46864         }
46865     },
46866     'TD' : {
46867         width : {
46868             title: "Width",
46869             width: 40
46870         },
46871         height : {
46872             title: "Height",
46873             width: 40
46874         },   
46875         align: {
46876             title: "Align",
46877             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46878             width: 80
46879         },
46880         valign: {
46881             title: "Valign",
46882             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46883             width: 80
46884         },
46885         colspan: {
46886             title: "Colspan",
46887             width: 20
46888             
46889         },
46890          'font-family'  : {
46891             title : "Font",
46892             style : 'fontFamily',
46893             displayField: 'display',
46894             optname : 'font-family',
46895             width: 140
46896         }
46897     },
46898     'INPUT' : {
46899         name : {
46900             title: "name",
46901             width: 120
46902         },
46903         value : {
46904             title: "Value",
46905             width: 120
46906         },
46907         width : {
46908             title: "Width",
46909             width: 40
46910         }
46911     },
46912     'LABEL' : {
46913         'for' : {
46914             title: "For",
46915             width: 120
46916         }
46917     },
46918     'TEXTAREA' : {
46919           name : {
46920             title: "name",
46921             width: 120
46922         },
46923         rows : {
46924             title: "Rows",
46925             width: 20
46926         },
46927         cols : {
46928             title: "Cols",
46929             width: 20
46930         }
46931     },
46932     'SELECT' : {
46933         name : {
46934             title: "name",
46935             width: 120
46936         },
46937         selectoptions : {
46938             title: "Options",
46939             width: 200
46940         }
46941     },
46942     
46943     // should we really allow this??
46944     // should this just be 
46945     'BODY' : {
46946         title : {
46947             title: "Title",
46948             width: 200,
46949             disabled : true
46950         }
46951     },
46952     'SPAN' : {
46953         'font-family'  : {
46954             title : "Font",
46955             style : 'fontFamily',
46956             displayField: 'display',
46957             optname : 'font-family',
46958             width: 140
46959         }
46960     },
46961     'DIV' : {
46962         'font-family'  : {
46963             title : "Font",
46964             style : 'fontFamily',
46965             displayField: 'display',
46966             optname : 'font-family',
46967             width: 140
46968         }
46969     },
46970      'P' : {
46971         'font-family'  : {
46972             title : "Font",
46973             style : 'fontFamily',
46974             displayField: 'display',
46975             optname : 'font-family',
46976             width: 140
46977         }
46978     },
46979     
46980     '*' : {
46981         // empty..
46982     }
46983
46984 };
46985
46986 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46987 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46988
46989 Roo.form.HtmlEditor.ToolbarContext.options = {
46990         'font-family'  : [ 
46991                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46992                 [ 'Courier New', 'Courier New'],
46993                 [ 'Tahoma', 'Tahoma'],
46994                 [ 'Times New Roman,serif', 'Times'],
46995                 [ 'Verdana','Verdana' ]
46996         ]
46997 };
46998
46999 // fixme - these need to be configurable..
47000  
47001
47002 //Roo.form.HtmlEditor.ToolbarContext.types
47003
47004
47005 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47006     
47007     tb: false,
47008     
47009     rendered: false,
47010     
47011     editor : false,
47012     editorcore : false,
47013     /**
47014      * @cfg {Object} disable  List of toolbar elements to disable
47015          
47016      */
47017     disable : false,
47018     /**
47019      * @cfg {Object} styles List of styles 
47020      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47021      *
47022      * These must be defined in the page, so they get rendered correctly..
47023      * .headline { }
47024      * TD.underline { }
47025      * 
47026      */
47027     styles : false,
47028     
47029     options: false,
47030     
47031     toolbars : false,
47032     
47033     init : function(editor)
47034     {
47035         this.editor = editor;
47036         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47037         var editorcore = this.editorcore;
47038         
47039         var fid = editorcore.frameId;
47040         var etb = this;
47041         function btn(id, toggle, handler){
47042             var xid = fid + '-'+ id ;
47043             return {
47044                 id : xid,
47045                 cmd : id,
47046                 cls : 'x-btn-icon x-edit-'+id,
47047                 enableToggle:toggle !== false,
47048                 scope: editorcore, // was editor...
47049                 handler:handler||editorcore.relayBtnCmd,
47050                 clickEvent:'mousedown',
47051                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47052                 tabIndex:-1
47053             };
47054         }
47055         // create a new element.
47056         var wdiv = editor.wrap.createChild({
47057                 tag: 'div'
47058             }, editor.wrap.dom.firstChild.nextSibling, true);
47059         
47060         // can we do this more than once??
47061         
47062          // stop form submits
47063       
47064  
47065         // disable everything...
47066         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47067         this.toolbars = {};
47068            
47069         for (var i in  ty) {
47070           
47071             this.toolbars[i] = this.buildToolbar(ty[i],i);
47072         }
47073         this.tb = this.toolbars.BODY;
47074         this.tb.el.show();
47075         this.buildFooter();
47076         this.footer.show();
47077         editor.on('hide', function( ) { this.footer.hide() }, this);
47078         editor.on('show', function( ) { this.footer.show() }, this);
47079         
47080          
47081         this.rendered = true;
47082         
47083         // the all the btns;
47084         editor.on('editorevent', this.updateToolbar, this);
47085         // other toolbars need to implement this..
47086         //editor.on('editmodechange', this.updateToolbar, this);
47087     },
47088     
47089     
47090     
47091     /**
47092      * Protected method that will not generally be called directly. It triggers
47093      * a toolbar update by reading the markup state of the current selection in the editor.
47094      *
47095      * Note you can force an update by calling on('editorevent', scope, false)
47096      */
47097     updateToolbar: function(editor,ev,sel){
47098
47099         //Roo.log(ev);
47100         // capture mouse up - this is handy for selecting images..
47101         // perhaps should go somewhere else...
47102         if(!this.editorcore.activated){
47103              this.editor.onFirstFocus();
47104             return;
47105         }
47106         
47107         
47108         
47109         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47110         // selectNode - might want to handle IE?
47111         if (ev &&
47112             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47113             ev.target && ev.target.tagName == 'IMG') {
47114             // they have click on an image...
47115             // let's see if we can change the selection...
47116             sel = ev.target;
47117          
47118               var nodeRange = sel.ownerDocument.createRange();
47119             try {
47120                 nodeRange.selectNode(sel);
47121             } catch (e) {
47122                 nodeRange.selectNodeContents(sel);
47123             }
47124             //nodeRange.collapse(true);
47125             var s = this.editorcore.win.getSelection();
47126             s.removeAllRanges();
47127             s.addRange(nodeRange);
47128         }  
47129         
47130       
47131         var updateFooter = sel ? false : true;
47132         
47133         
47134         var ans = this.editorcore.getAllAncestors();
47135         
47136         // pick
47137         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47138         
47139         if (!sel) { 
47140             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47141             sel = sel ? sel : this.editorcore.doc.body;
47142             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47143             
47144         }
47145         // pick a menu that exists..
47146         var tn = sel.tagName.toUpperCase();
47147         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47148         
47149         tn = sel.tagName.toUpperCase();
47150         
47151         var lastSel = this.tb.selectedNode;
47152         
47153         this.tb.selectedNode = sel;
47154         
47155         // if current menu does not match..
47156         
47157         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47158                 
47159             this.tb.el.hide();
47160             ///console.log("show: " + tn);
47161             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47162             this.tb.el.show();
47163             // update name
47164             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47165             
47166             
47167             // update attributes
47168             if (this.tb.fields) {
47169                 this.tb.fields.each(function(e) {
47170                     if (e.stylename) {
47171                         e.setValue(sel.style[e.stylename]);
47172                         return;
47173                     } 
47174                    e.setValue(sel.getAttribute(e.attrname));
47175                 });
47176             }
47177             
47178             var hasStyles = false;
47179             for(var i in this.styles) {
47180                 hasStyles = true;
47181                 break;
47182             }
47183             
47184             // update styles
47185             if (hasStyles) { 
47186                 var st = this.tb.fields.item(0);
47187                 
47188                 st.store.removeAll();
47189                
47190                 
47191                 var cn = sel.className.split(/\s+/);
47192                 
47193                 var avs = [];
47194                 if (this.styles['*']) {
47195                     
47196                     Roo.each(this.styles['*'], function(v) {
47197                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47198                     });
47199                 }
47200                 if (this.styles[tn]) { 
47201                     Roo.each(this.styles[tn], function(v) {
47202                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47203                     });
47204                 }
47205                 
47206                 st.store.loadData(avs);
47207                 st.collapse();
47208                 st.setValue(cn);
47209             }
47210             // flag our selected Node.
47211             this.tb.selectedNode = sel;
47212            
47213            
47214             Roo.menu.MenuMgr.hideAll();
47215
47216         }
47217         
47218         if (!updateFooter) {
47219             //this.footDisp.dom.innerHTML = ''; 
47220             return;
47221         }
47222         // update the footer
47223         //
47224         var html = '';
47225         
47226         this.footerEls = ans.reverse();
47227         Roo.each(this.footerEls, function(a,i) {
47228             if (!a) { return; }
47229             html += html.length ? ' &gt; '  :  '';
47230             
47231             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47232             
47233         });
47234        
47235         // 
47236         var sz = this.footDisp.up('td').getSize();
47237         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47238         this.footDisp.dom.style.marginLeft = '5px';
47239         
47240         this.footDisp.dom.style.overflow = 'hidden';
47241         
47242         this.footDisp.dom.innerHTML = html;
47243             
47244         //this.editorsyncValue();
47245     },
47246      
47247     
47248    
47249        
47250     // private
47251     onDestroy : function(){
47252         if(this.rendered){
47253             
47254             this.tb.items.each(function(item){
47255                 if(item.menu){
47256                     item.menu.removeAll();
47257                     if(item.menu.el){
47258                         item.menu.el.destroy();
47259                     }
47260                 }
47261                 item.destroy();
47262             });
47263              
47264         }
47265     },
47266     onFirstFocus: function() {
47267         // need to do this for all the toolbars..
47268         this.tb.items.each(function(item){
47269            item.enable();
47270         });
47271     },
47272     buildToolbar: function(tlist, nm)
47273     {
47274         var editor = this.editor;
47275         var editorcore = this.editorcore;
47276          // create a new element.
47277         var wdiv = editor.wrap.createChild({
47278                 tag: 'div'
47279             }, editor.wrap.dom.firstChild.nextSibling, true);
47280         
47281        
47282         var tb = new Roo.Toolbar(wdiv);
47283         // add the name..
47284         
47285         tb.add(nm+ ":&nbsp;");
47286         
47287         var styles = [];
47288         for(var i in this.styles) {
47289             styles.push(i);
47290         }
47291         
47292         // styles...
47293         if (styles && styles.length) {
47294             
47295             // this needs a multi-select checkbox...
47296             tb.addField( new Roo.form.ComboBox({
47297                 store: new Roo.data.SimpleStore({
47298                     id : 'val',
47299                     fields: ['val', 'selected'],
47300                     data : [] 
47301                 }),
47302                 name : '-roo-edit-className',
47303                 attrname : 'className',
47304                 displayField: 'val',
47305                 typeAhead: false,
47306                 mode: 'local',
47307                 editable : false,
47308                 triggerAction: 'all',
47309                 emptyText:'Select Style',
47310                 selectOnFocus:true,
47311                 width: 130,
47312                 listeners : {
47313                     'select': function(c, r, i) {
47314                         // initial support only for on class per el..
47315                         tb.selectedNode.className =  r ? r.get('val') : '';
47316                         editorcore.syncValue();
47317                     }
47318                 }
47319     
47320             }));
47321         }
47322         
47323         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47324         var tbops = tbc.options;
47325         
47326         for (var i in tlist) {
47327             
47328             var item = tlist[i];
47329             tb.add(item.title + ":&nbsp;");
47330             
47331             
47332             //optname == used so you can configure the options available..
47333             var opts = item.opts ? item.opts : false;
47334             if (item.optname) {
47335                 opts = tbops[item.optname];
47336            
47337             }
47338             
47339             if (opts) {
47340                 // opts == pulldown..
47341                 tb.addField( new Roo.form.ComboBox({
47342                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47343                         id : 'val',
47344                         fields: ['val', 'display'],
47345                         data : opts  
47346                     }),
47347                     name : '-roo-edit-' + i,
47348                     attrname : i,
47349                     stylename : item.style ? item.style : false,
47350                     displayField: item.displayField ? item.displayField : 'val',
47351                     valueField :  'val',
47352                     typeAhead: false,
47353                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47354                     editable : false,
47355                     triggerAction: 'all',
47356                     emptyText:'Select',
47357                     selectOnFocus:true,
47358                     width: item.width ? item.width  : 130,
47359                     listeners : {
47360                         'select': function(c, r, i) {
47361                             if (c.stylename) {
47362                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47363                                 return;
47364                             }
47365                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47366                         }
47367                     }
47368
47369                 }));
47370                 continue;
47371                     
47372                  
47373                 
47374                 tb.addField( new Roo.form.TextField({
47375                     name: i,
47376                     width: 100,
47377                     //allowBlank:false,
47378                     value: ''
47379                 }));
47380                 continue;
47381             }
47382             tb.addField( new Roo.form.TextField({
47383                 name: '-roo-edit-' + i,
47384                 attrname : i,
47385                 
47386                 width: item.width,
47387                 //allowBlank:true,
47388                 value: '',
47389                 listeners: {
47390                     'change' : function(f, nv, ov) {
47391                         tb.selectedNode.setAttribute(f.attrname, nv);
47392                         editorcore.syncValue();
47393                     }
47394                 }
47395             }));
47396              
47397         }
47398         
47399         var _this = this;
47400         
47401         if(nm == 'BODY'){
47402             tb.addSeparator();
47403         
47404             tb.addButton( {
47405                 text: 'Stylesheets',
47406
47407                 listeners : {
47408                     click : function ()
47409                     {
47410                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47411                     }
47412                 }
47413             });
47414         }
47415         
47416         tb.addFill();
47417         tb.addButton( {
47418             text: 'Remove Tag',
47419     
47420             listeners : {
47421                 click : function ()
47422                 {
47423                     // remove
47424                     // undo does not work.
47425                      
47426                     var sn = tb.selectedNode;
47427                     
47428                     var pn = sn.parentNode;
47429                     
47430                     var stn =  sn.childNodes[0];
47431                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47432                     while (sn.childNodes.length) {
47433                         var node = sn.childNodes[0];
47434                         sn.removeChild(node);
47435                         //Roo.log(node);
47436                         pn.insertBefore(node, sn);
47437                         
47438                     }
47439                     pn.removeChild(sn);
47440                     var range = editorcore.createRange();
47441         
47442                     range.setStart(stn,0);
47443                     range.setEnd(en,0); //????
47444                     //range.selectNode(sel);
47445                     
47446                     
47447                     var selection = editorcore.getSelection();
47448                     selection.removeAllRanges();
47449                     selection.addRange(range);
47450                     
47451                     
47452                     
47453                     //_this.updateToolbar(null, null, pn);
47454                     _this.updateToolbar(null, null, null);
47455                     _this.footDisp.dom.innerHTML = ''; 
47456                 }
47457             }
47458             
47459                     
47460                 
47461             
47462         });
47463         
47464         
47465         tb.el.on('click', function(e){
47466             e.preventDefault(); // what does this do?
47467         });
47468         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47469         tb.el.hide();
47470         tb.name = nm;
47471         // dont need to disable them... as they will get hidden
47472         return tb;
47473          
47474         
47475     },
47476     buildFooter : function()
47477     {
47478         
47479         var fel = this.editor.wrap.createChild();
47480         this.footer = new Roo.Toolbar(fel);
47481         // toolbar has scrolly on left / right?
47482         var footDisp= new Roo.Toolbar.Fill();
47483         var _t = this;
47484         this.footer.add(
47485             {
47486                 text : '&lt;',
47487                 xtype: 'Button',
47488                 handler : function() {
47489                     _t.footDisp.scrollTo('left',0,true)
47490                 }
47491             }
47492         );
47493         this.footer.add( footDisp );
47494         this.footer.add( 
47495             {
47496                 text : '&gt;',
47497                 xtype: 'Button',
47498                 handler : function() {
47499                     // no animation..
47500                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47501                 }
47502             }
47503         );
47504         var fel = Roo.get(footDisp.el);
47505         fel.addClass('x-editor-context');
47506         this.footDispWrap = fel; 
47507         this.footDispWrap.overflow  = 'hidden';
47508         
47509         this.footDisp = fel.createChild();
47510         this.footDispWrap.on('click', this.onContextClick, this)
47511         
47512         
47513     },
47514     onContextClick : function (ev,dom)
47515     {
47516         ev.preventDefault();
47517         var  cn = dom.className;
47518         //Roo.log(cn);
47519         if (!cn.match(/x-ed-loc-/)) {
47520             return;
47521         }
47522         var n = cn.split('-').pop();
47523         var ans = this.footerEls;
47524         var sel = ans[n];
47525         
47526          // pick
47527         var range = this.editorcore.createRange();
47528         
47529         range.selectNodeContents(sel);
47530         //range.selectNode(sel);
47531         
47532         
47533         var selection = this.editorcore.getSelection();
47534         selection.removeAllRanges();
47535         selection.addRange(range);
47536         
47537         
47538         
47539         this.updateToolbar(null, null, sel);
47540         
47541         
47542     }
47543     
47544     
47545     
47546     
47547     
47548 });
47549
47550
47551
47552
47553
47554 /*
47555  * Based on:
47556  * Ext JS Library 1.1.1
47557  * Copyright(c) 2006-2007, Ext JS, LLC.
47558  *
47559  * Originally Released Under LGPL - original licence link has changed is not relivant.
47560  *
47561  * Fork - LGPL
47562  * <script type="text/javascript">
47563  */
47564  
47565 /**
47566  * @class Roo.form.BasicForm
47567  * @extends Roo.util.Observable
47568  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47569  * @constructor
47570  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47571  * @param {Object} config Configuration options
47572  */
47573 Roo.form.BasicForm = function(el, config){
47574     this.allItems = [];
47575     this.childForms = [];
47576     Roo.apply(this, config);
47577     /*
47578      * The Roo.form.Field items in this form.
47579      * @type MixedCollection
47580      */
47581      
47582      
47583     this.items = new Roo.util.MixedCollection(false, function(o){
47584         return o.id || (o.id = Roo.id());
47585     });
47586     this.addEvents({
47587         /**
47588          * @event beforeaction
47589          * Fires before any action is performed. Return false to cancel the action.
47590          * @param {Form} this
47591          * @param {Action} action The action to be performed
47592          */
47593         beforeaction: true,
47594         /**
47595          * @event actionfailed
47596          * Fires when an action fails.
47597          * @param {Form} this
47598          * @param {Action} action The action that failed
47599          */
47600         actionfailed : true,
47601         /**
47602          * @event actioncomplete
47603          * Fires when an action is completed.
47604          * @param {Form} this
47605          * @param {Action} action The action that completed
47606          */
47607         actioncomplete : true
47608     });
47609     if(el){
47610         this.initEl(el);
47611     }
47612     Roo.form.BasicForm.superclass.constructor.call(this);
47613     
47614     Roo.form.BasicForm.popover.apply();
47615 };
47616
47617 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47618     /**
47619      * @cfg {String} method
47620      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47621      */
47622     /**
47623      * @cfg {DataReader} reader
47624      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47625      * This is optional as there is built-in support for processing JSON.
47626      */
47627     /**
47628      * @cfg {DataReader} errorReader
47629      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47630      * This is completely optional as there is built-in support for processing JSON.
47631      */
47632     /**
47633      * @cfg {String} url
47634      * The URL to use for form actions if one isn't supplied in the action options.
47635      */
47636     /**
47637      * @cfg {Boolean} fileUpload
47638      * Set to true if this form is a file upload.
47639      */
47640      
47641     /**
47642      * @cfg {Object} baseParams
47643      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47644      */
47645      /**
47646      
47647     /**
47648      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47649      */
47650     timeout: 30,
47651
47652     // private
47653     activeAction : null,
47654
47655     /**
47656      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47657      * or setValues() data instead of when the form was first created.
47658      */
47659     trackResetOnLoad : false,
47660     
47661     
47662     /**
47663      * childForms - used for multi-tab forms
47664      * @type {Array}
47665      */
47666     childForms : false,
47667     
47668     /**
47669      * allItems - full list of fields.
47670      * @type {Array}
47671      */
47672     allItems : false,
47673     
47674     /**
47675      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47676      * element by passing it or its id or mask the form itself by passing in true.
47677      * @type Mixed
47678      */
47679     waitMsgTarget : false,
47680     
47681     /**
47682      * @type Boolean
47683      */
47684     disableMask : false,
47685     
47686     /**
47687      * @cfg {Boolean} errorMask (true|false) default false
47688      */
47689     errorMask : false,
47690     
47691     /**
47692      * @cfg {Number} maskOffset Default 100
47693      */
47694     maskOffset : 100,
47695
47696     // private
47697     initEl : function(el){
47698         this.el = Roo.get(el);
47699         this.id = this.el.id || Roo.id();
47700         this.el.on('submit', this.onSubmit, this);
47701         this.el.addClass('x-form');
47702     },
47703
47704     // private
47705     onSubmit : function(e){
47706         e.stopEvent();
47707     },
47708
47709     /**
47710      * Returns true if client-side validation on the form is successful.
47711      * @return Boolean
47712      */
47713     isValid : function(){
47714         var valid = true;
47715         var target = false;
47716         this.items.each(function(f){
47717             if(f.validate()){
47718                 return;
47719             }
47720             
47721             valid = false;
47722                 
47723             if(!target && f.el.isVisible(true)){
47724                 target = f;
47725             }
47726         });
47727         
47728         if(this.errorMask && !valid){
47729             Roo.form.BasicForm.popover.mask(this, target);
47730         }
47731         
47732         return valid;
47733     },
47734     /**
47735      * Returns array of invalid form fields.
47736      * @return Array
47737      */
47738     
47739     invalidFields : function()
47740     {
47741         var ret = [];
47742         this.items.each(function(f){
47743             if(f.validate()){
47744                 return;
47745             }
47746             ret.push(f);
47747             
47748         });
47749         
47750         return ret;
47751     },
47752     
47753     
47754     /**
47755      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47756      * @return Boolean
47757      */
47758     isDirty : function(){
47759         var dirty = false;
47760         this.items.each(function(f){
47761            if(f.isDirty()){
47762                dirty = true;
47763                return false;
47764            }
47765         });
47766         return dirty;
47767     },
47768     
47769     /**
47770      * Returns true if any fields in this form have changed since their original load. (New version)
47771      * @return Boolean
47772      */
47773     
47774     hasChanged : function()
47775     {
47776         var dirty = false;
47777         this.items.each(function(f){
47778            if(f.hasChanged()){
47779                dirty = true;
47780                return false;
47781            }
47782         });
47783         return dirty;
47784         
47785     },
47786     /**
47787      * Resets all hasChanged to 'false' -
47788      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47789      * So hasChanged storage is only to be used for this purpose
47790      * @return Boolean
47791      */
47792     resetHasChanged : function()
47793     {
47794         this.items.each(function(f){
47795            f.resetHasChanged();
47796         });
47797         
47798     },
47799     
47800     
47801     /**
47802      * Performs a predefined action (submit or load) or custom actions you define on this form.
47803      * @param {String} actionName The name of the action type
47804      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47805      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47806      * accept other config options):
47807      * <pre>
47808 Property          Type             Description
47809 ----------------  ---------------  ----------------------------------------------------------------------------------
47810 url               String           The url for the action (defaults to the form's url)
47811 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47812 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47813 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47814                                    validate the form on the client (defaults to false)
47815      * </pre>
47816      * @return {BasicForm} this
47817      */
47818     doAction : function(action, options){
47819         if(typeof action == 'string'){
47820             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47821         }
47822         if(this.fireEvent('beforeaction', this, action) !== false){
47823             this.beforeAction(action);
47824             action.run.defer(100, action);
47825         }
47826         return this;
47827     },
47828
47829     /**
47830      * Shortcut to do a submit action.
47831      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47832      * @return {BasicForm} this
47833      */
47834     submit : function(options){
47835         this.doAction('submit', options);
47836         return this;
47837     },
47838
47839     /**
47840      * Shortcut to do a load action.
47841      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47842      * @return {BasicForm} this
47843      */
47844     load : function(options){
47845         this.doAction('load', options);
47846         return this;
47847     },
47848
47849     /**
47850      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47851      * @param {Record} record The record to edit
47852      * @return {BasicForm} this
47853      */
47854     updateRecord : function(record){
47855         record.beginEdit();
47856         var fs = record.fields;
47857         fs.each(function(f){
47858             var field = this.findField(f.name);
47859             if(field){
47860                 record.set(f.name, field.getValue());
47861             }
47862         }, this);
47863         record.endEdit();
47864         return this;
47865     },
47866
47867     /**
47868      * Loads an Roo.data.Record into this form.
47869      * @param {Record} record The record to load
47870      * @return {BasicForm} this
47871      */
47872     loadRecord : function(record){
47873         this.setValues(record.data);
47874         return this;
47875     },
47876
47877     // private
47878     beforeAction : function(action){
47879         var o = action.options;
47880         
47881         if(!this.disableMask) {
47882             if(this.waitMsgTarget === true){
47883                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47884             }else if(this.waitMsgTarget){
47885                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47886                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47887             }else {
47888                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47889             }
47890         }
47891         
47892          
47893     },
47894
47895     // private
47896     afterAction : function(action, success){
47897         this.activeAction = null;
47898         var o = action.options;
47899         
47900         if(!this.disableMask) {
47901             if(this.waitMsgTarget === true){
47902                 this.el.unmask();
47903             }else if(this.waitMsgTarget){
47904                 this.waitMsgTarget.unmask();
47905             }else{
47906                 Roo.MessageBox.updateProgress(1);
47907                 Roo.MessageBox.hide();
47908             }
47909         }
47910         
47911         if(success){
47912             if(o.reset){
47913                 this.reset();
47914             }
47915             Roo.callback(o.success, o.scope, [this, action]);
47916             this.fireEvent('actioncomplete', this, action);
47917             
47918         }else{
47919             
47920             // failure condition..
47921             // we have a scenario where updates need confirming.
47922             // eg. if a locking scenario exists..
47923             // we look for { errors : { needs_confirm : true }} in the response.
47924             if (
47925                 (typeof(action.result) != 'undefined')  &&
47926                 (typeof(action.result.errors) != 'undefined')  &&
47927                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47928            ){
47929                 var _t = this;
47930                 Roo.MessageBox.confirm(
47931                     "Change requires confirmation",
47932                     action.result.errorMsg,
47933                     function(r) {
47934                         if (r != 'yes') {
47935                             return;
47936                         }
47937                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47938                     }
47939                     
47940                 );
47941                 
47942                 
47943                 
47944                 return;
47945             }
47946             
47947             Roo.callback(o.failure, o.scope, [this, action]);
47948             // show an error message if no failed handler is set..
47949             if (!this.hasListener('actionfailed')) {
47950                 Roo.MessageBox.alert("Error",
47951                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47952                         action.result.errorMsg :
47953                         "Saving Failed, please check your entries or try again"
47954                 );
47955             }
47956             
47957             this.fireEvent('actionfailed', this, action);
47958         }
47959         
47960     },
47961
47962     /**
47963      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47964      * @param {String} id The value to search for
47965      * @return Field
47966      */
47967     findField : function(id){
47968         var field = this.items.get(id);
47969         if(!field){
47970             this.items.each(function(f){
47971                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47972                     field = f;
47973                     return false;
47974                 }
47975             });
47976         }
47977         return field || null;
47978     },
47979
47980     /**
47981      * Add a secondary form to this one, 
47982      * Used to provide tabbed forms. One form is primary, with hidden values 
47983      * which mirror the elements from the other forms.
47984      * 
47985      * @param {Roo.form.Form} form to add.
47986      * 
47987      */
47988     addForm : function(form)
47989     {
47990        
47991         if (this.childForms.indexOf(form) > -1) {
47992             // already added..
47993             return;
47994         }
47995         this.childForms.push(form);
47996         var n = '';
47997         Roo.each(form.allItems, function (fe) {
47998             
47999             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48000             if (this.findField(n)) { // already added..
48001                 return;
48002             }
48003             var add = new Roo.form.Hidden({
48004                 name : n
48005             });
48006             add.render(this.el);
48007             
48008             this.add( add );
48009         }, this);
48010         
48011     },
48012     /**
48013      * Mark fields in this form invalid in bulk.
48014      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48015      * @return {BasicForm} this
48016      */
48017     markInvalid : function(errors){
48018         if(errors instanceof Array){
48019             for(var i = 0, len = errors.length; i < len; i++){
48020                 var fieldError = errors[i];
48021                 var f = this.findField(fieldError.id);
48022                 if(f){
48023                     f.markInvalid(fieldError.msg);
48024                 }
48025             }
48026         }else{
48027             var field, id;
48028             for(id in errors){
48029                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48030                     field.markInvalid(errors[id]);
48031                 }
48032             }
48033         }
48034         Roo.each(this.childForms || [], function (f) {
48035             f.markInvalid(errors);
48036         });
48037         
48038         return this;
48039     },
48040
48041     /**
48042      * Set values for fields in this form in bulk.
48043      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48044      * @return {BasicForm} this
48045      */
48046     setValues : function(values){
48047         if(values instanceof Array){ // array of objects
48048             for(var i = 0, len = values.length; i < len; i++){
48049                 var v = values[i];
48050                 var f = this.findField(v.id);
48051                 if(f){
48052                     f.setValue(v.value);
48053                     if(this.trackResetOnLoad){
48054                         f.originalValue = f.getValue();
48055                     }
48056                 }
48057             }
48058         }else{ // object hash
48059             var field, id;
48060             for(id in values){
48061                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48062                     
48063                     if (field.setFromData && 
48064                         field.valueField && 
48065                         field.displayField &&
48066                         // combos' with local stores can 
48067                         // be queried via setValue()
48068                         // to set their value..
48069                         (field.store && !field.store.isLocal)
48070                         ) {
48071                         // it's a combo
48072                         var sd = { };
48073                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48074                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48075                         field.setFromData(sd);
48076                         
48077                     } else {
48078                         field.setValue(values[id]);
48079                     }
48080                     
48081                     
48082                     if(this.trackResetOnLoad){
48083                         field.originalValue = field.getValue();
48084                     }
48085                 }
48086             }
48087         }
48088         this.resetHasChanged();
48089         
48090         
48091         Roo.each(this.childForms || [], function (f) {
48092             f.setValues(values);
48093             f.resetHasChanged();
48094         });
48095                 
48096         return this;
48097     },
48098  
48099     /**
48100      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48101      * they are returned as an array.
48102      * @param {Boolean} asString
48103      * @return {Object}
48104      */
48105     getValues : function(asString){
48106         if (this.childForms) {
48107             // copy values from the child forms
48108             Roo.each(this.childForms, function (f) {
48109                 this.setValues(f.getValues());
48110             }, this);
48111         }
48112         
48113         // use formdata
48114         if (typeof(FormData) != 'undefined' && asString !== true) {
48115             // this relies on a 'recent' version of chrome apparently...
48116             try {
48117                 var fd = (new FormData(this.el.dom)).entries();
48118                 var ret = {};
48119                 var ent = fd.next();
48120                 while (!ent.done) {
48121                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48122                     ent = fd.next();
48123                 };
48124                 return ret;
48125             } catch(e) {
48126                 
48127             }
48128             
48129         }
48130         
48131         
48132         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48133         if(asString === true){
48134             return fs;
48135         }
48136         return Roo.urlDecode(fs);
48137     },
48138     
48139     /**
48140      * Returns the fields in this form as an object with key/value pairs. 
48141      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48142      * @return {Object}
48143      */
48144     getFieldValues : function(with_hidden)
48145     {
48146         if (this.childForms) {
48147             // copy values from the child forms
48148             // should this call getFieldValues - probably not as we do not currently copy
48149             // hidden fields when we generate..
48150             Roo.each(this.childForms, function (f) {
48151                 this.setValues(f.getValues());
48152             }, this);
48153         }
48154         
48155         var ret = {};
48156         this.items.each(function(f){
48157             if (!f.getName()) {
48158                 return;
48159             }
48160             var v = f.getValue();
48161             if (f.inputType =='radio') {
48162                 if (typeof(ret[f.getName()]) == 'undefined') {
48163                     ret[f.getName()] = ''; // empty..
48164                 }
48165                 
48166                 if (!f.el.dom.checked) {
48167                     return;
48168                     
48169                 }
48170                 v = f.el.dom.value;
48171                 
48172             }
48173             
48174             // not sure if this supported any more..
48175             if ((typeof(v) == 'object') && f.getRawValue) {
48176                 v = f.getRawValue() ; // dates..
48177             }
48178             // combo boxes where name != hiddenName...
48179             if (f.name != f.getName()) {
48180                 ret[f.name] = f.getRawValue();
48181             }
48182             ret[f.getName()] = v;
48183         });
48184         
48185         return ret;
48186     },
48187
48188     /**
48189      * Clears all invalid messages in this form.
48190      * @return {BasicForm} this
48191      */
48192     clearInvalid : function(){
48193         this.items.each(function(f){
48194            f.clearInvalid();
48195         });
48196         
48197         Roo.each(this.childForms || [], function (f) {
48198             f.clearInvalid();
48199         });
48200         
48201         
48202         return this;
48203     },
48204
48205     /**
48206      * Resets this form.
48207      * @return {BasicForm} this
48208      */
48209     reset : function(){
48210         this.items.each(function(f){
48211             f.reset();
48212         });
48213         
48214         Roo.each(this.childForms || [], function (f) {
48215             f.reset();
48216         });
48217         this.resetHasChanged();
48218         
48219         return this;
48220     },
48221
48222     /**
48223      * Add Roo.form components to this form.
48224      * @param {Field} field1
48225      * @param {Field} field2 (optional)
48226      * @param {Field} etc (optional)
48227      * @return {BasicForm} this
48228      */
48229     add : function(){
48230         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48231         return this;
48232     },
48233
48234
48235     /**
48236      * Removes a field from the items collection (does NOT remove its markup).
48237      * @param {Field} field
48238      * @return {BasicForm} this
48239      */
48240     remove : function(field){
48241         this.items.remove(field);
48242         return this;
48243     },
48244
48245     /**
48246      * Looks at the fields in this form, checks them for an id attribute,
48247      * and calls applyTo on the existing dom element with that id.
48248      * @return {BasicForm} this
48249      */
48250     render : function(){
48251         this.items.each(function(f){
48252             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48253                 f.applyTo(f.id);
48254             }
48255         });
48256         return this;
48257     },
48258
48259     /**
48260      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48261      * @param {Object} values
48262      * @return {BasicForm} this
48263      */
48264     applyToFields : function(o){
48265         this.items.each(function(f){
48266            Roo.apply(f, o);
48267         });
48268         return this;
48269     },
48270
48271     /**
48272      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48273      * @param {Object} values
48274      * @return {BasicForm} this
48275      */
48276     applyIfToFields : function(o){
48277         this.items.each(function(f){
48278            Roo.applyIf(f, o);
48279         });
48280         return this;
48281     }
48282 });
48283
48284 // back compat
48285 Roo.BasicForm = Roo.form.BasicForm;
48286
48287 Roo.apply(Roo.form.BasicForm, {
48288     
48289     popover : {
48290         
48291         padding : 5,
48292         
48293         isApplied : false,
48294         
48295         isMasked : false,
48296         
48297         form : false,
48298         
48299         target : false,
48300         
48301         intervalID : false,
48302         
48303         maskEl : false,
48304         
48305         apply : function()
48306         {
48307             if(this.isApplied){
48308                 return;
48309             }
48310             
48311             this.maskEl = {
48312                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48313                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48314                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48315                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48316             };
48317             
48318             this.maskEl.top.enableDisplayMode("block");
48319             this.maskEl.left.enableDisplayMode("block");
48320             this.maskEl.bottom.enableDisplayMode("block");
48321             this.maskEl.right.enableDisplayMode("block");
48322             
48323             Roo.get(document.body).on('click', function(){
48324                 this.unmask();
48325             }, this);
48326             
48327             Roo.get(document.body).on('touchstart', function(){
48328                 this.unmask();
48329             }, this);
48330             
48331             this.isApplied = true
48332         },
48333         
48334         mask : function(form, target)
48335         {
48336             this.form = form;
48337             
48338             this.target = target;
48339             
48340             if(!this.form.errorMask || !target.el){
48341                 return;
48342             }
48343             
48344             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48345             
48346             var ot = this.target.el.calcOffsetsTo(scrollable);
48347             
48348             var scrollTo = ot[1] - this.form.maskOffset;
48349             
48350             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48351             
48352             scrollable.scrollTo('top', scrollTo);
48353             
48354             var el = this.target.wrap || this.target.el;
48355             
48356             var box = el.getBox();
48357             
48358             this.maskEl.top.setStyle('position', 'absolute');
48359             this.maskEl.top.setStyle('z-index', 10000);
48360             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48361             this.maskEl.top.setLeft(0);
48362             this.maskEl.top.setTop(0);
48363             this.maskEl.top.show();
48364             
48365             this.maskEl.left.setStyle('position', 'absolute');
48366             this.maskEl.left.setStyle('z-index', 10000);
48367             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48368             this.maskEl.left.setLeft(0);
48369             this.maskEl.left.setTop(box.y - this.padding);
48370             this.maskEl.left.show();
48371
48372             this.maskEl.bottom.setStyle('position', 'absolute');
48373             this.maskEl.bottom.setStyle('z-index', 10000);
48374             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48375             this.maskEl.bottom.setLeft(0);
48376             this.maskEl.bottom.setTop(box.bottom + this.padding);
48377             this.maskEl.bottom.show();
48378
48379             this.maskEl.right.setStyle('position', 'absolute');
48380             this.maskEl.right.setStyle('z-index', 10000);
48381             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48382             this.maskEl.right.setLeft(box.right + this.padding);
48383             this.maskEl.right.setTop(box.y - this.padding);
48384             this.maskEl.right.show();
48385
48386             this.intervalID = window.setInterval(function() {
48387                 Roo.form.BasicForm.popover.unmask();
48388             }, 10000);
48389
48390             window.onwheel = function(){ return false;};
48391             
48392             (function(){ this.isMasked = true; }).defer(500, this);
48393             
48394         },
48395         
48396         unmask : function()
48397         {
48398             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48399                 return;
48400             }
48401             
48402             this.maskEl.top.setStyle('position', 'absolute');
48403             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48404             this.maskEl.top.hide();
48405
48406             this.maskEl.left.setStyle('position', 'absolute');
48407             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48408             this.maskEl.left.hide();
48409
48410             this.maskEl.bottom.setStyle('position', 'absolute');
48411             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48412             this.maskEl.bottom.hide();
48413
48414             this.maskEl.right.setStyle('position', 'absolute');
48415             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48416             this.maskEl.right.hide();
48417             
48418             window.onwheel = function(){ return true;};
48419             
48420             if(this.intervalID){
48421                 window.clearInterval(this.intervalID);
48422                 this.intervalID = false;
48423             }
48424             
48425             this.isMasked = false;
48426             
48427         }
48428         
48429     }
48430     
48431 });/*
48432  * Based on:
48433  * Ext JS Library 1.1.1
48434  * Copyright(c) 2006-2007, Ext JS, LLC.
48435  *
48436  * Originally Released Under LGPL - original licence link has changed is not relivant.
48437  *
48438  * Fork - LGPL
48439  * <script type="text/javascript">
48440  */
48441
48442 /**
48443  * @class Roo.form.Form
48444  * @extends Roo.form.BasicForm
48445  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48446  * @constructor
48447  * @param {Object} config Configuration options
48448  */
48449 Roo.form.Form = function(config){
48450     var xitems =  [];
48451     if (config.items) {
48452         xitems = config.items;
48453         delete config.items;
48454     }
48455    
48456     
48457     Roo.form.Form.superclass.constructor.call(this, null, config);
48458     this.url = this.url || this.action;
48459     if(!this.root){
48460         this.root = new Roo.form.Layout(Roo.applyIf({
48461             id: Roo.id()
48462         }, config));
48463     }
48464     this.active = this.root;
48465     /**
48466      * Array of all the buttons that have been added to this form via {@link addButton}
48467      * @type Array
48468      */
48469     this.buttons = [];
48470     this.allItems = [];
48471     this.addEvents({
48472         /**
48473          * @event clientvalidation
48474          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48475          * @param {Form} this
48476          * @param {Boolean} valid true if the form has passed client-side validation
48477          */
48478         clientvalidation: true,
48479         /**
48480          * @event rendered
48481          * Fires when the form is rendered
48482          * @param {Roo.form.Form} form
48483          */
48484         rendered : true
48485     });
48486     
48487     if (this.progressUrl) {
48488             // push a hidden field onto the list of fields..
48489             this.addxtype( {
48490                     xns: Roo.form, 
48491                     xtype : 'Hidden', 
48492                     name : 'UPLOAD_IDENTIFIER' 
48493             });
48494         }
48495         
48496     
48497     Roo.each(xitems, this.addxtype, this);
48498     
48499 };
48500
48501 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48502     /**
48503      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48504      */
48505     /**
48506      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48507      */
48508     /**
48509      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48510      */
48511     buttonAlign:'center',
48512
48513     /**
48514      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48515      */
48516     minButtonWidth:75,
48517
48518     /**
48519      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48520      * This property cascades to child containers if not set.
48521      */
48522     labelAlign:'left',
48523
48524     /**
48525      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48526      * fires a looping event with that state. This is required to bind buttons to the valid
48527      * state using the config value formBind:true on the button.
48528      */
48529     monitorValid : false,
48530
48531     /**
48532      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48533      */
48534     monitorPoll : 200,
48535     
48536     /**
48537      * @cfg {String} progressUrl - Url to return progress data 
48538      */
48539     
48540     progressUrl : false,
48541     /**
48542      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48543      * sending a formdata with extra parameters - eg uploaded elements.
48544      */
48545     
48546     formData : false,
48547     
48548     /**
48549      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48550      * fields are added and the column is closed. If no fields are passed the column remains open
48551      * until end() is called.
48552      * @param {Object} config The config to pass to the column
48553      * @param {Field} field1 (optional)
48554      * @param {Field} field2 (optional)
48555      * @param {Field} etc (optional)
48556      * @return Column The column container object
48557      */
48558     column : function(c){
48559         var col = new Roo.form.Column(c);
48560         this.start(col);
48561         if(arguments.length > 1){ // duplicate code required because of Opera
48562             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48563             this.end();
48564         }
48565         return col;
48566     },
48567
48568     /**
48569      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48570      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48571      * until end() is called.
48572      * @param {Object} config The config to pass to the fieldset
48573      * @param {Field} field1 (optional)
48574      * @param {Field} field2 (optional)
48575      * @param {Field} etc (optional)
48576      * @return FieldSet The fieldset container object
48577      */
48578     fieldset : function(c){
48579         var fs = new Roo.form.FieldSet(c);
48580         this.start(fs);
48581         if(arguments.length > 1){ // duplicate code required because of Opera
48582             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48583             this.end();
48584         }
48585         return fs;
48586     },
48587
48588     /**
48589      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48590      * fields are added and the container is closed. If no fields are passed the container remains open
48591      * until end() is called.
48592      * @param {Object} config The config to pass to the Layout
48593      * @param {Field} field1 (optional)
48594      * @param {Field} field2 (optional)
48595      * @param {Field} etc (optional)
48596      * @return Layout The container object
48597      */
48598     container : function(c){
48599         var l = new Roo.form.Layout(c);
48600         this.start(l);
48601         if(arguments.length > 1){ // duplicate code required because of Opera
48602             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48603             this.end();
48604         }
48605         return l;
48606     },
48607
48608     /**
48609      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48610      * @param {Object} container A Roo.form.Layout or subclass of Layout
48611      * @return {Form} this
48612      */
48613     start : function(c){
48614         // cascade label info
48615         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48616         this.active.stack.push(c);
48617         c.ownerCt = this.active;
48618         this.active = c;
48619         return this;
48620     },
48621
48622     /**
48623      * Closes the current open container
48624      * @return {Form} this
48625      */
48626     end : function(){
48627         if(this.active == this.root){
48628             return this;
48629         }
48630         this.active = this.active.ownerCt;
48631         return this;
48632     },
48633
48634     /**
48635      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48636      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48637      * as the label of the field.
48638      * @param {Field} field1
48639      * @param {Field} field2 (optional)
48640      * @param {Field} etc. (optional)
48641      * @return {Form} this
48642      */
48643     add : function(){
48644         this.active.stack.push.apply(this.active.stack, arguments);
48645         this.allItems.push.apply(this.allItems,arguments);
48646         var r = [];
48647         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48648             if(a[i].isFormField){
48649                 r.push(a[i]);
48650             }
48651         }
48652         if(r.length > 0){
48653             Roo.form.Form.superclass.add.apply(this, r);
48654         }
48655         return this;
48656     },
48657     
48658
48659     
48660     
48661     
48662      /**
48663      * Find any element that has been added to a form, using it's ID or name
48664      * This can include framesets, columns etc. along with regular fields..
48665      * @param {String} id - id or name to find.
48666      
48667      * @return {Element} e - or false if nothing found.
48668      */
48669     findbyId : function(id)
48670     {
48671         var ret = false;
48672         if (!id) {
48673             return ret;
48674         }
48675         Roo.each(this.allItems, function(f){
48676             if (f.id == id || f.name == id ){
48677                 ret = f;
48678                 return false;
48679             }
48680         });
48681         return ret;
48682     },
48683
48684     
48685     
48686     /**
48687      * Render this form into the passed container. This should only be called once!
48688      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48689      * @return {Form} this
48690      */
48691     render : function(ct)
48692     {
48693         
48694         
48695         
48696         ct = Roo.get(ct);
48697         var o = this.autoCreate || {
48698             tag: 'form',
48699             method : this.method || 'POST',
48700             id : this.id || Roo.id()
48701         };
48702         this.initEl(ct.createChild(o));
48703
48704         this.root.render(this.el);
48705         
48706        
48707              
48708         this.items.each(function(f){
48709             f.render('x-form-el-'+f.id);
48710         });
48711
48712         if(this.buttons.length > 0){
48713             // tables are required to maintain order and for correct IE layout
48714             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48715                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48716                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48717             }}, null, true);
48718             var tr = tb.getElementsByTagName('tr')[0];
48719             for(var i = 0, len = this.buttons.length; i < len; i++) {
48720                 var b = this.buttons[i];
48721                 var td = document.createElement('td');
48722                 td.className = 'x-form-btn-td';
48723                 b.render(tr.appendChild(td));
48724             }
48725         }
48726         if(this.monitorValid){ // initialize after render
48727             this.startMonitoring();
48728         }
48729         this.fireEvent('rendered', this);
48730         return this;
48731     },
48732
48733     /**
48734      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48735      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48736      * object or a valid Roo.DomHelper element config
48737      * @param {Function} handler The function called when the button is clicked
48738      * @param {Object} scope (optional) The scope of the handler function
48739      * @return {Roo.Button}
48740      */
48741     addButton : function(config, handler, scope){
48742         var bc = {
48743             handler: handler,
48744             scope: scope,
48745             minWidth: this.minButtonWidth,
48746             hideParent:true
48747         };
48748         if(typeof config == "string"){
48749             bc.text = config;
48750         }else{
48751             Roo.apply(bc, config);
48752         }
48753         var btn = new Roo.Button(null, bc);
48754         this.buttons.push(btn);
48755         return btn;
48756     },
48757
48758      /**
48759      * Adds a series of form elements (using the xtype property as the factory method.
48760      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48761      * @param {Object} config 
48762      */
48763     
48764     addxtype : function()
48765     {
48766         var ar = Array.prototype.slice.call(arguments, 0);
48767         var ret = false;
48768         for(var i = 0; i < ar.length; i++) {
48769             if (!ar[i]) {
48770                 continue; // skip -- if this happends something invalid got sent, we 
48771                 // should ignore it, as basically that interface element will not show up
48772                 // and that should be pretty obvious!!
48773             }
48774             
48775             if (Roo.form[ar[i].xtype]) {
48776                 ar[i].form = this;
48777                 var fe = Roo.factory(ar[i], Roo.form);
48778                 if (!ret) {
48779                     ret = fe;
48780                 }
48781                 fe.form = this;
48782                 if (fe.store) {
48783                     fe.store.form = this;
48784                 }
48785                 if (fe.isLayout) {  
48786                          
48787                     this.start(fe);
48788                     this.allItems.push(fe);
48789                     if (fe.items && fe.addxtype) {
48790                         fe.addxtype.apply(fe, fe.items);
48791                         delete fe.items;
48792                     }
48793                      this.end();
48794                     continue;
48795                 }
48796                 
48797                 
48798                  
48799                 this.add(fe);
48800               //  console.log('adding ' + ar[i].xtype);
48801             }
48802             if (ar[i].xtype == 'Button') {  
48803                 //console.log('adding button');
48804                 //console.log(ar[i]);
48805                 this.addButton(ar[i]);
48806                 this.allItems.push(fe);
48807                 continue;
48808             }
48809             
48810             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48811                 alert('end is not supported on xtype any more, use items');
48812             //    this.end();
48813             //    //console.log('adding end');
48814             }
48815             
48816         }
48817         return ret;
48818     },
48819     
48820     /**
48821      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48822      * option "monitorValid"
48823      */
48824     startMonitoring : function(){
48825         if(!this.bound){
48826             this.bound = true;
48827             Roo.TaskMgr.start({
48828                 run : this.bindHandler,
48829                 interval : this.monitorPoll || 200,
48830                 scope: this
48831             });
48832         }
48833     },
48834
48835     /**
48836      * Stops monitoring of the valid state of this form
48837      */
48838     stopMonitoring : function(){
48839         this.bound = false;
48840     },
48841
48842     // private
48843     bindHandler : function(){
48844         if(!this.bound){
48845             return false; // stops binding
48846         }
48847         var valid = true;
48848         this.items.each(function(f){
48849             if(!f.isValid(true)){
48850                 valid = false;
48851                 return false;
48852             }
48853         });
48854         for(var i = 0, len = this.buttons.length; i < len; i++){
48855             var btn = this.buttons[i];
48856             if(btn.formBind === true && btn.disabled === valid){
48857                 btn.setDisabled(!valid);
48858             }
48859         }
48860         this.fireEvent('clientvalidation', this, valid);
48861     }
48862     
48863     
48864     
48865     
48866     
48867     
48868     
48869     
48870 });
48871
48872
48873 // back compat
48874 Roo.Form = Roo.form.Form;
48875 /*
48876  * Based on:
48877  * Ext JS Library 1.1.1
48878  * Copyright(c) 2006-2007, Ext JS, LLC.
48879  *
48880  * Originally Released Under LGPL - original licence link has changed is not relivant.
48881  *
48882  * Fork - LGPL
48883  * <script type="text/javascript">
48884  */
48885
48886 // as we use this in bootstrap.
48887 Roo.namespace('Roo.form');
48888  /**
48889  * @class Roo.form.Action
48890  * Internal Class used to handle form actions
48891  * @constructor
48892  * @param {Roo.form.BasicForm} el The form element or its id
48893  * @param {Object} config Configuration options
48894  */
48895
48896  
48897  
48898 // define the action interface
48899 Roo.form.Action = function(form, options){
48900     this.form = form;
48901     this.options = options || {};
48902 };
48903 /**
48904  * Client Validation Failed
48905  * @const 
48906  */
48907 Roo.form.Action.CLIENT_INVALID = 'client';
48908 /**
48909  * Server Validation Failed
48910  * @const 
48911  */
48912 Roo.form.Action.SERVER_INVALID = 'server';
48913  /**
48914  * Connect to Server Failed
48915  * @const 
48916  */
48917 Roo.form.Action.CONNECT_FAILURE = 'connect';
48918 /**
48919  * Reading Data from Server Failed
48920  * @const 
48921  */
48922 Roo.form.Action.LOAD_FAILURE = 'load';
48923
48924 Roo.form.Action.prototype = {
48925     type : 'default',
48926     failureType : undefined,
48927     response : undefined,
48928     result : undefined,
48929
48930     // interface method
48931     run : function(options){
48932
48933     },
48934
48935     // interface method
48936     success : function(response){
48937
48938     },
48939
48940     // interface method
48941     handleResponse : function(response){
48942
48943     },
48944
48945     // default connection failure
48946     failure : function(response){
48947         
48948         this.response = response;
48949         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48950         this.form.afterAction(this, false);
48951     },
48952
48953     processResponse : function(response){
48954         this.response = response;
48955         if(!response.responseText){
48956             return true;
48957         }
48958         this.result = this.handleResponse(response);
48959         return this.result;
48960     },
48961
48962     // utility functions used internally
48963     getUrl : function(appendParams){
48964         var url = this.options.url || this.form.url || this.form.el.dom.action;
48965         if(appendParams){
48966             var p = this.getParams();
48967             if(p){
48968                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48969             }
48970         }
48971         return url;
48972     },
48973
48974     getMethod : function(){
48975         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48976     },
48977
48978     getParams : function(){
48979         var bp = this.form.baseParams;
48980         var p = this.options.params;
48981         if(p){
48982             if(typeof p == "object"){
48983                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48984             }else if(typeof p == 'string' && bp){
48985                 p += '&' + Roo.urlEncode(bp);
48986             }
48987         }else if(bp){
48988             p = Roo.urlEncode(bp);
48989         }
48990         return p;
48991     },
48992
48993     createCallback : function(){
48994         return {
48995             success: this.success,
48996             failure: this.failure,
48997             scope: this,
48998             timeout: (this.form.timeout*1000),
48999             upload: this.form.fileUpload ? this.success : undefined
49000         };
49001     }
49002 };
49003
49004 Roo.form.Action.Submit = function(form, options){
49005     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49006 };
49007
49008 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49009     type : 'submit',
49010
49011     haveProgress : false,
49012     uploadComplete : false,
49013     
49014     // uploadProgress indicator.
49015     uploadProgress : function()
49016     {
49017         if (!this.form.progressUrl) {
49018             return;
49019         }
49020         
49021         if (!this.haveProgress) {
49022             Roo.MessageBox.progress("Uploading", "Uploading");
49023         }
49024         if (this.uploadComplete) {
49025            Roo.MessageBox.hide();
49026            return;
49027         }
49028         
49029         this.haveProgress = true;
49030    
49031         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49032         
49033         var c = new Roo.data.Connection();
49034         c.request({
49035             url : this.form.progressUrl,
49036             params: {
49037                 id : uid
49038             },
49039             method: 'GET',
49040             success : function(req){
49041                //console.log(data);
49042                 var rdata = false;
49043                 var edata;
49044                 try  {
49045                    rdata = Roo.decode(req.responseText)
49046                 } catch (e) {
49047                     Roo.log("Invalid data from server..");
49048                     Roo.log(edata);
49049                     return;
49050                 }
49051                 if (!rdata || !rdata.success) {
49052                     Roo.log(rdata);
49053                     Roo.MessageBox.alert(Roo.encode(rdata));
49054                     return;
49055                 }
49056                 var data = rdata.data;
49057                 
49058                 if (this.uploadComplete) {
49059                    Roo.MessageBox.hide();
49060                    return;
49061                 }
49062                    
49063                 if (data){
49064                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49065                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49066                     );
49067                 }
49068                 this.uploadProgress.defer(2000,this);
49069             },
49070        
49071             failure: function(data) {
49072                 Roo.log('progress url failed ');
49073                 Roo.log(data);
49074             },
49075             scope : this
49076         });
49077            
49078     },
49079     
49080     
49081     run : function()
49082     {
49083         // run get Values on the form, so it syncs any secondary forms.
49084         this.form.getValues();
49085         
49086         var o = this.options;
49087         var method = this.getMethod();
49088         var isPost = method == 'POST';
49089         if(o.clientValidation === false || this.form.isValid()){
49090             
49091             if (this.form.progressUrl) {
49092                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49093                     (new Date() * 1) + '' + Math.random());
49094                     
49095             } 
49096             
49097             
49098             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49099                 form:this.form.el.dom,
49100                 url:this.getUrl(!isPost),
49101                 method: method,
49102                 params:isPost ? this.getParams() : null,
49103                 isUpload: this.form.fileUpload,
49104                 formData : this.form.formData
49105             }));
49106             
49107             this.uploadProgress();
49108
49109         }else if (o.clientValidation !== false){ // client validation failed
49110             this.failureType = Roo.form.Action.CLIENT_INVALID;
49111             this.form.afterAction(this, false);
49112         }
49113     },
49114
49115     success : function(response)
49116     {
49117         this.uploadComplete= true;
49118         if (this.haveProgress) {
49119             Roo.MessageBox.hide();
49120         }
49121         
49122         
49123         var result = this.processResponse(response);
49124         if(result === true || result.success){
49125             this.form.afterAction(this, true);
49126             return;
49127         }
49128         if(result.errors){
49129             this.form.markInvalid(result.errors);
49130             this.failureType = Roo.form.Action.SERVER_INVALID;
49131         }
49132         this.form.afterAction(this, false);
49133     },
49134     failure : function(response)
49135     {
49136         this.uploadComplete= true;
49137         if (this.haveProgress) {
49138             Roo.MessageBox.hide();
49139         }
49140         
49141         this.response = response;
49142         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49143         this.form.afterAction(this, false);
49144     },
49145     
49146     handleResponse : function(response){
49147         if(this.form.errorReader){
49148             var rs = this.form.errorReader.read(response);
49149             var errors = [];
49150             if(rs.records){
49151                 for(var i = 0, len = rs.records.length; i < len; i++) {
49152                     var r = rs.records[i];
49153                     errors[i] = r.data;
49154                 }
49155             }
49156             if(errors.length < 1){
49157                 errors = null;
49158             }
49159             return {
49160                 success : rs.success,
49161                 errors : errors
49162             };
49163         }
49164         var ret = false;
49165         try {
49166             ret = Roo.decode(response.responseText);
49167         } catch (e) {
49168             ret = {
49169                 success: false,
49170                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49171                 errors : []
49172             };
49173         }
49174         return ret;
49175         
49176     }
49177 });
49178
49179
49180 Roo.form.Action.Load = function(form, options){
49181     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49182     this.reader = this.form.reader;
49183 };
49184
49185 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49186     type : 'load',
49187
49188     run : function(){
49189         
49190         Roo.Ajax.request(Roo.apply(
49191                 this.createCallback(), {
49192                     method:this.getMethod(),
49193                     url:this.getUrl(false),
49194                     params:this.getParams()
49195         }));
49196     },
49197
49198     success : function(response){
49199         
49200         var result = this.processResponse(response);
49201         if(result === true || !result.success || !result.data){
49202             this.failureType = Roo.form.Action.LOAD_FAILURE;
49203             this.form.afterAction(this, false);
49204             return;
49205         }
49206         this.form.clearInvalid();
49207         this.form.setValues(result.data);
49208         this.form.afterAction(this, true);
49209     },
49210
49211     handleResponse : function(response){
49212         if(this.form.reader){
49213             var rs = this.form.reader.read(response);
49214             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49215             return {
49216                 success : rs.success,
49217                 data : data
49218             };
49219         }
49220         return Roo.decode(response.responseText);
49221     }
49222 });
49223
49224 Roo.form.Action.ACTION_TYPES = {
49225     'load' : Roo.form.Action.Load,
49226     'submit' : Roo.form.Action.Submit
49227 };/*
49228  * Based on:
49229  * Ext JS Library 1.1.1
49230  * Copyright(c) 2006-2007, Ext JS, LLC.
49231  *
49232  * Originally Released Under LGPL - original licence link has changed is not relivant.
49233  *
49234  * Fork - LGPL
49235  * <script type="text/javascript">
49236  */
49237  
49238 /**
49239  * @class Roo.form.Layout
49240  * @extends Roo.Component
49241  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49242  * @constructor
49243  * @param {Object} config Configuration options
49244  */
49245 Roo.form.Layout = function(config){
49246     var xitems = [];
49247     if (config.items) {
49248         xitems = config.items;
49249         delete config.items;
49250     }
49251     Roo.form.Layout.superclass.constructor.call(this, config);
49252     this.stack = [];
49253     Roo.each(xitems, this.addxtype, this);
49254      
49255 };
49256
49257 Roo.extend(Roo.form.Layout, Roo.Component, {
49258     /**
49259      * @cfg {String/Object} autoCreate
49260      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49261      */
49262     /**
49263      * @cfg {String/Object/Function} style
49264      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49265      * a function which returns such a specification.
49266      */
49267     /**
49268      * @cfg {String} labelAlign
49269      * Valid values are "left," "top" and "right" (defaults to "left")
49270      */
49271     /**
49272      * @cfg {Number} labelWidth
49273      * Fixed width in pixels of all field labels (defaults to undefined)
49274      */
49275     /**
49276      * @cfg {Boolean} clear
49277      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49278      */
49279     clear : true,
49280     /**
49281      * @cfg {String} labelSeparator
49282      * The separator to use after field labels (defaults to ':')
49283      */
49284     labelSeparator : ':',
49285     /**
49286      * @cfg {Boolean} hideLabels
49287      * True to suppress the display of field labels in this layout (defaults to false)
49288      */
49289     hideLabels : false,
49290
49291     // private
49292     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49293     
49294     isLayout : true,
49295     
49296     // private
49297     onRender : function(ct, position){
49298         if(this.el){ // from markup
49299             this.el = Roo.get(this.el);
49300         }else {  // generate
49301             var cfg = this.getAutoCreate();
49302             this.el = ct.createChild(cfg, position);
49303         }
49304         if(this.style){
49305             this.el.applyStyles(this.style);
49306         }
49307         if(this.labelAlign){
49308             this.el.addClass('x-form-label-'+this.labelAlign);
49309         }
49310         if(this.hideLabels){
49311             this.labelStyle = "display:none";
49312             this.elementStyle = "padding-left:0;";
49313         }else{
49314             if(typeof this.labelWidth == 'number'){
49315                 this.labelStyle = "width:"+this.labelWidth+"px;";
49316                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49317             }
49318             if(this.labelAlign == 'top'){
49319                 this.labelStyle = "width:auto;";
49320                 this.elementStyle = "padding-left:0;";
49321             }
49322         }
49323         var stack = this.stack;
49324         var slen = stack.length;
49325         if(slen > 0){
49326             if(!this.fieldTpl){
49327                 var t = new Roo.Template(
49328                     '<div class="x-form-item {5}">',
49329                         '<label for="{0}" style="{2}">{1}{4}</label>',
49330                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49331                         '</div>',
49332                     '</div><div class="x-form-clear-left"></div>'
49333                 );
49334                 t.disableFormats = true;
49335                 t.compile();
49336                 Roo.form.Layout.prototype.fieldTpl = t;
49337             }
49338             for(var i = 0; i < slen; i++) {
49339                 if(stack[i].isFormField){
49340                     this.renderField(stack[i]);
49341                 }else{
49342                     this.renderComponent(stack[i]);
49343                 }
49344             }
49345         }
49346         if(this.clear){
49347             this.el.createChild({cls:'x-form-clear'});
49348         }
49349     },
49350
49351     // private
49352     renderField : function(f){
49353         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49354                f.id, //0
49355                f.fieldLabel, //1
49356                f.labelStyle||this.labelStyle||'', //2
49357                this.elementStyle||'', //3
49358                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49359                f.itemCls||this.itemCls||''  //5
49360        ], true).getPrevSibling());
49361     },
49362
49363     // private
49364     renderComponent : function(c){
49365         c.render(c.isLayout ? this.el : this.el.createChild());    
49366     },
49367     /**
49368      * Adds a object form elements (using the xtype property as the factory method.)
49369      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49370      * @param {Object} config 
49371      */
49372     addxtype : function(o)
49373     {
49374         // create the lement.
49375         o.form = this.form;
49376         var fe = Roo.factory(o, Roo.form);
49377         this.form.allItems.push(fe);
49378         this.stack.push(fe);
49379         
49380         if (fe.isFormField) {
49381             this.form.items.add(fe);
49382         }
49383          
49384         return fe;
49385     }
49386 });
49387
49388 /**
49389  * @class Roo.form.Column
49390  * @extends Roo.form.Layout
49391  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49392  * @constructor
49393  * @param {Object} config Configuration options
49394  */
49395 Roo.form.Column = function(config){
49396     Roo.form.Column.superclass.constructor.call(this, config);
49397 };
49398
49399 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49400     /**
49401      * @cfg {Number/String} width
49402      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49403      */
49404     /**
49405      * @cfg {String/Object} autoCreate
49406      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49407      */
49408
49409     // private
49410     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49411
49412     // private
49413     onRender : function(ct, position){
49414         Roo.form.Column.superclass.onRender.call(this, ct, position);
49415         if(this.width){
49416             this.el.setWidth(this.width);
49417         }
49418     }
49419 });
49420
49421
49422 /**
49423  * @class Roo.form.Row
49424  * @extends Roo.form.Layout
49425  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49426  * @constructor
49427  * @param {Object} config Configuration options
49428  */
49429
49430  
49431 Roo.form.Row = function(config){
49432     Roo.form.Row.superclass.constructor.call(this, config);
49433 };
49434  
49435 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49436       /**
49437      * @cfg {Number/String} width
49438      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49439      */
49440     /**
49441      * @cfg {Number/String} height
49442      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49443      */
49444     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49445     
49446     padWidth : 20,
49447     // private
49448     onRender : function(ct, position){
49449         //console.log('row render');
49450         if(!this.rowTpl){
49451             var t = new Roo.Template(
49452                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49453                     '<label for="{0}" style="{2}">{1}{4}</label>',
49454                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49455                     '</div>',
49456                 '</div>'
49457             );
49458             t.disableFormats = true;
49459             t.compile();
49460             Roo.form.Layout.prototype.rowTpl = t;
49461         }
49462         this.fieldTpl = this.rowTpl;
49463         
49464         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49465         var labelWidth = 100;
49466         
49467         if ((this.labelAlign != 'top')) {
49468             if (typeof this.labelWidth == 'number') {
49469                 labelWidth = this.labelWidth
49470             }
49471             this.padWidth =  20 + labelWidth;
49472             
49473         }
49474         
49475         Roo.form.Column.superclass.onRender.call(this, ct, position);
49476         if(this.width){
49477             this.el.setWidth(this.width);
49478         }
49479         if(this.height){
49480             this.el.setHeight(this.height);
49481         }
49482     },
49483     
49484     // private
49485     renderField : function(f){
49486         f.fieldEl = this.fieldTpl.append(this.el, [
49487                f.id, f.fieldLabel,
49488                f.labelStyle||this.labelStyle||'',
49489                this.elementStyle||'',
49490                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49491                f.itemCls||this.itemCls||'',
49492                f.width ? f.width + this.padWidth : 160 + this.padWidth
49493        ],true);
49494     }
49495 });
49496  
49497
49498 /**
49499  * @class Roo.form.FieldSet
49500  * @extends Roo.form.Layout
49501  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49502  * @constructor
49503  * @param {Object} config Configuration options
49504  */
49505 Roo.form.FieldSet = function(config){
49506     Roo.form.FieldSet.superclass.constructor.call(this, config);
49507 };
49508
49509 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49510     /**
49511      * @cfg {String} legend
49512      * The text to display as the legend for the FieldSet (defaults to '')
49513      */
49514     /**
49515      * @cfg {String/Object} autoCreate
49516      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49517      */
49518
49519     // private
49520     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49521
49522     // private
49523     onRender : function(ct, position){
49524         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49525         if(this.legend){
49526             this.setLegend(this.legend);
49527         }
49528     },
49529
49530     // private
49531     setLegend : function(text){
49532         if(this.rendered){
49533             this.el.child('legend').update(text);
49534         }
49535     }
49536 });/*
49537  * Based on:
49538  * Ext JS Library 1.1.1
49539  * Copyright(c) 2006-2007, Ext JS, LLC.
49540  *
49541  * Originally Released Under LGPL - original licence link has changed is not relivant.
49542  *
49543  * Fork - LGPL
49544  * <script type="text/javascript">
49545  */
49546 /**
49547  * @class Roo.form.VTypes
49548  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49549  * @singleton
49550  */
49551 Roo.form.VTypes = function(){
49552     // closure these in so they are only created once.
49553     var alpha = /^[a-zA-Z_]+$/;
49554     var alphanum = /^[a-zA-Z0-9_]+$/;
49555     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49556     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49557
49558     // All these messages and functions are configurable
49559     return {
49560         /**
49561          * The function used to validate email addresses
49562          * @param {String} value The email address
49563          */
49564         'email' : function(v){
49565             return email.test(v);
49566         },
49567         /**
49568          * The error text to display when the email validation function returns false
49569          * @type String
49570          */
49571         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49572         /**
49573          * The keystroke filter mask to be applied on email input
49574          * @type RegExp
49575          */
49576         'emailMask' : /[a-z0-9_\.\-@]/i,
49577
49578         /**
49579          * The function used to validate URLs
49580          * @param {String} value The URL
49581          */
49582         'url' : function(v){
49583             return url.test(v);
49584         },
49585         /**
49586          * The error text to display when the url validation function returns false
49587          * @type String
49588          */
49589         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49590         
49591         /**
49592          * The function used to validate alpha values
49593          * @param {String} value The value
49594          */
49595         'alpha' : function(v){
49596             return alpha.test(v);
49597         },
49598         /**
49599          * The error text to display when the alpha validation function returns false
49600          * @type String
49601          */
49602         'alphaText' : 'This field should only contain letters and _',
49603         /**
49604          * The keystroke filter mask to be applied on alpha input
49605          * @type RegExp
49606          */
49607         'alphaMask' : /[a-z_]/i,
49608
49609         /**
49610          * The function used to validate alphanumeric values
49611          * @param {String} value The value
49612          */
49613         'alphanum' : function(v){
49614             return alphanum.test(v);
49615         },
49616         /**
49617          * The error text to display when the alphanumeric validation function returns false
49618          * @type String
49619          */
49620         'alphanumText' : 'This field should only contain letters, numbers and _',
49621         /**
49622          * The keystroke filter mask to be applied on alphanumeric input
49623          * @type RegExp
49624          */
49625         'alphanumMask' : /[a-z0-9_]/i
49626     };
49627 }();//<script type="text/javascript">
49628
49629 /**
49630  * @class Roo.form.FCKeditor
49631  * @extends Roo.form.TextArea
49632  * Wrapper around the FCKEditor http://www.fckeditor.net
49633  * @constructor
49634  * Creates a new FCKeditor
49635  * @param {Object} config Configuration options
49636  */
49637 Roo.form.FCKeditor = function(config){
49638     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49639     this.addEvents({
49640          /**
49641          * @event editorinit
49642          * Fired when the editor is initialized - you can add extra handlers here..
49643          * @param {FCKeditor} this
49644          * @param {Object} the FCK object.
49645          */
49646         editorinit : true
49647     });
49648     
49649     
49650 };
49651 Roo.form.FCKeditor.editors = { };
49652 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49653 {
49654     //defaultAutoCreate : {
49655     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49656     //},
49657     // private
49658     /**
49659      * @cfg {Object} fck options - see fck manual for details.
49660      */
49661     fckconfig : false,
49662     
49663     /**
49664      * @cfg {Object} fck toolbar set (Basic or Default)
49665      */
49666     toolbarSet : 'Basic',
49667     /**
49668      * @cfg {Object} fck BasePath
49669      */ 
49670     basePath : '/fckeditor/',
49671     
49672     
49673     frame : false,
49674     
49675     value : '',
49676     
49677    
49678     onRender : function(ct, position)
49679     {
49680         if(!this.el){
49681             this.defaultAutoCreate = {
49682                 tag: "textarea",
49683                 style:"width:300px;height:60px;",
49684                 autocomplete: "new-password"
49685             };
49686         }
49687         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49688         /*
49689         if(this.grow){
49690             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49691             if(this.preventScrollbars){
49692                 this.el.setStyle("overflow", "hidden");
49693             }
49694             this.el.setHeight(this.growMin);
49695         }
49696         */
49697         //console.log('onrender' + this.getId() );
49698         Roo.form.FCKeditor.editors[this.getId()] = this;
49699          
49700
49701         this.replaceTextarea() ;
49702         
49703     },
49704     
49705     getEditor : function() {
49706         return this.fckEditor;
49707     },
49708     /**
49709      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49710      * @param {Mixed} value The value to set
49711      */
49712     
49713     
49714     setValue : function(value)
49715     {
49716         //console.log('setValue: ' + value);
49717         
49718         if(typeof(value) == 'undefined') { // not sure why this is happending...
49719             return;
49720         }
49721         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49722         
49723         //if(!this.el || !this.getEditor()) {
49724         //    this.value = value;
49725             //this.setValue.defer(100,this,[value]);    
49726         //    return;
49727         //} 
49728         
49729         if(!this.getEditor()) {
49730             return;
49731         }
49732         
49733         this.getEditor().SetData(value);
49734         
49735         //
49736
49737     },
49738
49739     /**
49740      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49741      * @return {Mixed} value The field value
49742      */
49743     getValue : function()
49744     {
49745         
49746         if (this.frame && this.frame.dom.style.display == 'none') {
49747             return Roo.form.FCKeditor.superclass.getValue.call(this);
49748         }
49749         
49750         if(!this.el || !this.getEditor()) {
49751            
49752            // this.getValue.defer(100,this); 
49753             return this.value;
49754         }
49755        
49756         
49757         var value=this.getEditor().GetData();
49758         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49759         return Roo.form.FCKeditor.superclass.getValue.call(this);
49760         
49761
49762     },
49763
49764     /**
49765      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49766      * @return {Mixed} value The field value
49767      */
49768     getRawValue : function()
49769     {
49770         if (this.frame && this.frame.dom.style.display == 'none') {
49771             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49772         }
49773         
49774         if(!this.el || !this.getEditor()) {
49775             //this.getRawValue.defer(100,this); 
49776             return this.value;
49777             return;
49778         }
49779         
49780         
49781         
49782         var value=this.getEditor().GetData();
49783         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49784         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49785          
49786     },
49787     
49788     setSize : function(w,h) {
49789         
49790         
49791         
49792         //if (this.frame && this.frame.dom.style.display == 'none') {
49793         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49794         //    return;
49795         //}
49796         //if(!this.el || !this.getEditor()) {
49797         //    this.setSize.defer(100,this, [w,h]); 
49798         //    return;
49799         //}
49800         
49801         
49802         
49803         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49804         
49805         this.frame.dom.setAttribute('width', w);
49806         this.frame.dom.setAttribute('height', h);
49807         this.frame.setSize(w,h);
49808         
49809     },
49810     
49811     toggleSourceEdit : function(value) {
49812         
49813       
49814          
49815         this.el.dom.style.display = value ? '' : 'none';
49816         this.frame.dom.style.display = value ?  'none' : '';
49817         
49818     },
49819     
49820     
49821     focus: function(tag)
49822     {
49823         if (this.frame.dom.style.display == 'none') {
49824             return Roo.form.FCKeditor.superclass.focus.call(this);
49825         }
49826         if(!this.el || !this.getEditor()) {
49827             this.focus.defer(100,this, [tag]); 
49828             return;
49829         }
49830         
49831         
49832         
49833         
49834         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49835         this.getEditor().Focus();
49836         if (tgs.length) {
49837             if (!this.getEditor().Selection.GetSelection()) {
49838                 this.focus.defer(100,this, [tag]); 
49839                 return;
49840             }
49841             
49842             
49843             var r = this.getEditor().EditorDocument.createRange();
49844             r.setStart(tgs[0],0);
49845             r.setEnd(tgs[0],0);
49846             this.getEditor().Selection.GetSelection().removeAllRanges();
49847             this.getEditor().Selection.GetSelection().addRange(r);
49848             this.getEditor().Focus();
49849         }
49850         
49851     },
49852     
49853     
49854     
49855     replaceTextarea : function()
49856     {
49857         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49858             return ;
49859         }
49860         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49861         //{
49862             // We must check the elements firstly using the Id and then the name.
49863         var oTextarea = document.getElementById( this.getId() );
49864         
49865         var colElementsByName = document.getElementsByName( this.getId() ) ;
49866          
49867         oTextarea.style.display = 'none' ;
49868
49869         if ( oTextarea.tabIndex ) {            
49870             this.TabIndex = oTextarea.tabIndex ;
49871         }
49872         
49873         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49874         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49875         this.frame = Roo.get(this.getId() + '___Frame')
49876     },
49877     
49878     _getConfigHtml : function()
49879     {
49880         var sConfig = '' ;
49881
49882         for ( var o in this.fckconfig ) {
49883             sConfig += sConfig.length > 0  ? '&amp;' : '';
49884             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49885         }
49886
49887         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49888     },
49889     
49890     
49891     _getIFrameHtml : function()
49892     {
49893         var sFile = 'fckeditor.html' ;
49894         /* no idea what this is about..
49895         try
49896         {
49897             if ( (/fcksource=true/i).test( window.top.location.search ) )
49898                 sFile = 'fckeditor.original.html' ;
49899         }
49900         catch (e) { 
49901         */
49902
49903         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49904         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49905         
49906         
49907         var html = '<iframe id="' + this.getId() +
49908             '___Frame" src="' + sLink +
49909             '" width="' + this.width +
49910             '" height="' + this.height + '"' +
49911             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49912             ' frameborder="0" scrolling="no"></iframe>' ;
49913
49914         return html ;
49915     },
49916     
49917     _insertHtmlBefore : function( html, element )
49918     {
49919         if ( element.insertAdjacentHTML )       {
49920             // IE
49921             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49922         } else { // Gecko
49923             var oRange = document.createRange() ;
49924             oRange.setStartBefore( element ) ;
49925             var oFragment = oRange.createContextualFragment( html );
49926             element.parentNode.insertBefore( oFragment, element ) ;
49927         }
49928     }
49929     
49930     
49931   
49932     
49933     
49934     
49935     
49936
49937 });
49938
49939 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49940
49941 function FCKeditor_OnComplete(editorInstance){
49942     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49943     f.fckEditor = editorInstance;
49944     //console.log("loaded");
49945     f.fireEvent('editorinit', f, editorInstance);
49946
49947   
49948
49949  
49950
49951
49952
49953
49954
49955
49956
49957
49958
49959
49960
49961
49962
49963
49964
49965 //<script type="text/javascript">
49966 /**
49967  * @class Roo.form.GridField
49968  * @extends Roo.form.Field
49969  * Embed a grid (or editable grid into a form)
49970  * STATUS ALPHA
49971  * 
49972  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49973  * it needs 
49974  * xgrid.store = Roo.data.Store
49975  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49976  * xgrid.store.reader = Roo.data.JsonReader 
49977  * 
49978  * 
49979  * @constructor
49980  * Creates a new GridField
49981  * @param {Object} config Configuration options
49982  */
49983 Roo.form.GridField = function(config){
49984     Roo.form.GridField.superclass.constructor.call(this, config);
49985      
49986 };
49987
49988 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49989     /**
49990      * @cfg {Number} width  - used to restrict width of grid..
49991      */
49992     width : 100,
49993     /**
49994      * @cfg {Number} height - used to restrict height of grid..
49995      */
49996     height : 50,
49997      /**
49998      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49999          * 
50000          *}
50001      */
50002     xgrid : false, 
50003     /**
50004      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50005      * {tag: "input", type: "checkbox", autocomplete: "off"})
50006      */
50007    // defaultAutoCreate : { tag: 'div' },
50008     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50009     /**
50010      * @cfg {String} addTitle Text to include for adding a title.
50011      */
50012     addTitle : false,
50013     //
50014     onResize : function(){
50015         Roo.form.Field.superclass.onResize.apply(this, arguments);
50016     },
50017
50018     initEvents : function(){
50019         // Roo.form.Checkbox.superclass.initEvents.call(this);
50020         // has no events...
50021        
50022     },
50023
50024
50025     getResizeEl : function(){
50026         return this.wrap;
50027     },
50028
50029     getPositionEl : function(){
50030         return this.wrap;
50031     },
50032
50033     // private
50034     onRender : function(ct, position){
50035         
50036         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50037         var style = this.style;
50038         delete this.style;
50039         
50040         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50041         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50042         this.viewEl = this.wrap.createChild({ tag: 'div' });
50043         if (style) {
50044             this.viewEl.applyStyles(style);
50045         }
50046         if (this.width) {
50047             this.viewEl.setWidth(this.width);
50048         }
50049         if (this.height) {
50050             this.viewEl.setHeight(this.height);
50051         }
50052         //if(this.inputValue !== undefined){
50053         //this.setValue(this.value);
50054         
50055         
50056         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50057         
50058         
50059         this.grid.render();
50060         this.grid.getDataSource().on('remove', this.refreshValue, this);
50061         this.grid.getDataSource().on('update', this.refreshValue, this);
50062         this.grid.on('afteredit', this.refreshValue, this);
50063  
50064     },
50065      
50066     
50067     /**
50068      * Sets the value of the item. 
50069      * @param {String} either an object  or a string..
50070      */
50071     setValue : function(v){
50072         //this.value = v;
50073         v = v || []; // empty set..
50074         // this does not seem smart - it really only affects memoryproxy grids..
50075         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50076             var ds = this.grid.getDataSource();
50077             // assumes a json reader..
50078             var data = {}
50079             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50080             ds.loadData( data);
50081         }
50082         // clear selection so it does not get stale.
50083         if (this.grid.sm) { 
50084             this.grid.sm.clearSelections();
50085         }
50086         
50087         Roo.form.GridField.superclass.setValue.call(this, v);
50088         this.refreshValue();
50089         // should load data in the grid really....
50090     },
50091     
50092     // private
50093     refreshValue: function() {
50094          var val = [];
50095         this.grid.getDataSource().each(function(r) {
50096             val.push(r.data);
50097         });
50098         this.el.dom.value = Roo.encode(val);
50099     }
50100     
50101      
50102     
50103     
50104 });/*
50105  * Based on:
50106  * Ext JS Library 1.1.1
50107  * Copyright(c) 2006-2007, Ext JS, LLC.
50108  *
50109  * Originally Released Under LGPL - original licence link has changed is not relivant.
50110  *
50111  * Fork - LGPL
50112  * <script type="text/javascript">
50113  */
50114 /**
50115  * @class Roo.form.DisplayField
50116  * @extends Roo.form.Field
50117  * A generic Field to display non-editable data.
50118  * @cfg {Boolean} closable (true|false) default false
50119  * @constructor
50120  * Creates a new Display Field item.
50121  * @param {Object} config Configuration options
50122  */
50123 Roo.form.DisplayField = function(config){
50124     Roo.form.DisplayField.superclass.constructor.call(this, config);
50125     
50126     this.addEvents({
50127         /**
50128          * @event close
50129          * Fires after the click the close btn
50130              * @param {Roo.form.DisplayField} this
50131              */
50132         close : true
50133     });
50134 };
50135
50136 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50137     inputType:      'hidden',
50138     allowBlank:     true,
50139     readOnly:         true,
50140     
50141  
50142     /**
50143      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50144      */
50145     focusClass : undefined,
50146     /**
50147      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50148      */
50149     fieldClass: 'x-form-field',
50150     
50151      /**
50152      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50153      */
50154     valueRenderer: undefined,
50155     
50156     width: 100,
50157     /**
50158      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50159      * {tag: "input", type: "checkbox", autocomplete: "off"})
50160      */
50161      
50162  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50163  
50164     closable : false,
50165     
50166     onResize : function(){
50167         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50168         
50169     },
50170
50171     initEvents : function(){
50172         // Roo.form.Checkbox.superclass.initEvents.call(this);
50173         // has no events...
50174         
50175         if(this.closable){
50176             this.closeEl.on('click', this.onClose, this);
50177         }
50178        
50179     },
50180
50181
50182     getResizeEl : function(){
50183         return this.wrap;
50184     },
50185
50186     getPositionEl : function(){
50187         return this.wrap;
50188     },
50189
50190     // private
50191     onRender : function(ct, position){
50192         
50193         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50194         //if(this.inputValue !== undefined){
50195         this.wrap = this.el.wrap();
50196         
50197         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50198         
50199         if(this.closable){
50200             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50201         }
50202         
50203         if (this.bodyStyle) {
50204             this.viewEl.applyStyles(this.bodyStyle);
50205         }
50206         //this.viewEl.setStyle('padding', '2px');
50207         
50208         this.setValue(this.value);
50209         
50210     },
50211 /*
50212     // private
50213     initValue : Roo.emptyFn,
50214
50215   */
50216
50217         // private
50218     onClick : function(){
50219         
50220     },
50221
50222     /**
50223      * Sets the checked state of the checkbox.
50224      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50225      */
50226     setValue : function(v){
50227         this.value = v;
50228         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50229         // this might be called before we have a dom element..
50230         if (!this.viewEl) {
50231             return;
50232         }
50233         this.viewEl.dom.innerHTML = html;
50234         Roo.form.DisplayField.superclass.setValue.call(this, v);
50235
50236     },
50237     
50238     onClose : function(e)
50239     {
50240         e.preventDefault();
50241         
50242         this.fireEvent('close', this);
50243     }
50244 });/*
50245  * 
50246  * Licence- LGPL
50247  * 
50248  */
50249
50250 /**
50251  * @class Roo.form.DayPicker
50252  * @extends Roo.form.Field
50253  * A Day picker show [M] [T] [W] ....
50254  * @constructor
50255  * Creates a new Day Picker
50256  * @param {Object} config Configuration options
50257  */
50258 Roo.form.DayPicker= function(config){
50259     Roo.form.DayPicker.superclass.constructor.call(this, config);
50260      
50261 };
50262
50263 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50264     /**
50265      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50266      */
50267     focusClass : undefined,
50268     /**
50269      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50270      */
50271     fieldClass: "x-form-field",
50272    
50273     /**
50274      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50275      * {tag: "input", type: "checkbox", autocomplete: "off"})
50276      */
50277     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50278     
50279    
50280     actionMode : 'viewEl', 
50281     //
50282     // private
50283  
50284     inputType : 'hidden',
50285     
50286      
50287     inputElement: false, // real input element?
50288     basedOn: false, // ????
50289     
50290     isFormField: true, // not sure where this is needed!!!!
50291
50292     onResize : function(){
50293         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50294         if(!this.boxLabel){
50295             this.el.alignTo(this.wrap, 'c-c');
50296         }
50297     },
50298
50299     initEvents : function(){
50300         Roo.form.Checkbox.superclass.initEvents.call(this);
50301         this.el.on("click", this.onClick,  this);
50302         this.el.on("change", this.onClick,  this);
50303     },
50304
50305
50306     getResizeEl : function(){
50307         return this.wrap;
50308     },
50309
50310     getPositionEl : function(){
50311         return this.wrap;
50312     },
50313
50314     
50315     // private
50316     onRender : function(ct, position){
50317         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50318        
50319         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50320         
50321         var r1 = '<table><tr>';
50322         var r2 = '<tr class="x-form-daypick-icons">';
50323         for (var i=0; i < 7; i++) {
50324             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50325             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50326         }
50327         
50328         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50329         viewEl.select('img').on('click', this.onClick, this);
50330         this.viewEl = viewEl;   
50331         
50332         
50333         // this will not work on Chrome!!!
50334         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50335         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50336         
50337         
50338           
50339
50340     },
50341
50342     // private
50343     initValue : Roo.emptyFn,
50344
50345     /**
50346      * Returns the checked state of the checkbox.
50347      * @return {Boolean} True if checked, else false
50348      */
50349     getValue : function(){
50350         return this.el.dom.value;
50351         
50352     },
50353
50354         // private
50355     onClick : function(e){ 
50356         //this.setChecked(!this.checked);
50357         Roo.get(e.target).toggleClass('x-menu-item-checked');
50358         this.refreshValue();
50359         //if(this.el.dom.checked != this.checked){
50360         //    this.setValue(this.el.dom.checked);
50361        // }
50362     },
50363     
50364     // private
50365     refreshValue : function()
50366     {
50367         var val = '';
50368         this.viewEl.select('img',true).each(function(e,i,n)  {
50369             val += e.is(".x-menu-item-checked") ? String(n) : '';
50370         });
50371         this.setValue(val, true);
50372     },
50373
50374     /**
50375      * Sets the checked state of the checkbox.
50376      * On is always based on a string comparison between inputValue and the param.
50377      * @param {Boolean/String} value - the value to set 
50378      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50379      */
50380     setValue : function(v,suppressEvent){
50381         if (!this.el.dom) {
50382             return;
50383         }
50384         var old = this.el.dom.value ;
50385         this.el.dom.value = v;
50386         if (suppressEvent) {
50387             return ;
50388         }
50389          
50390         // update display..
50391         this.viewEl.select('img',true).each(function(e,i,n)  {
50392             
50393             var on = e.is(".x-menu-item-checked");
50394             var newv = v.indexOf(String(n)) > -1;
50395             if (on != newv) {
50396                 e.toggleClass('x-menu-item-checked');
50397             }
50398             
50399         });
50400         
50401         
50402         this.fireEvent('change', this, v, old);
50403         
50404         
50405     },
50406    
50407     // handle setting of hidden value by some other method!!?!?
50408     setFromHidden: function()
50409     {
50410         if(!this.el){
50411             return;
50412         }
50413         //console.log("SET FROM HIDDEN");
50414         //alert('setFrom hidden');
50415         this.setValue(this.el.dom.value);
50416     },
50417     
50418     onDestroy : function()
50419     {
50420         if(this.viewEl){
50421             Roo.get(this.viewEl).remove();
50422         }
50423          
50424         Roo.form.DayPicker.superclass.onDestroy.call(this);
50425     }
50426
50427 });/*
50428  * RooJS Library 1.1.1
50429  * Copyright(c) 2008-2011  Alan Knowles
50430  *
50431  * License - LGPL
50432  */
50433  
50434
50435 /**
50436  * @class Roo.form.ComboCheck
50437  * @extends Roo.form.ComboBox
50438  * A combobox for multiple select items.
50439  *
50440  * FIXME - could do with a reset button..
50441  * 
50442  * @constructor
50443  * Create a new ComboCheck
50444  * @param {Object} config Configuration options
50445  */
50446 Roo.form.ComboCheck = function(config){
50447     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50448     // should verify some data...
50449     // like
50450     // hiddenName = required..
50451     // displayField = required
50452     // valudField == required
50453     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50454     var _t = this;
50455     Roo.each(req, function(e) {
50456         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50457             throw "Roo.form.ComboCheck : missing value for: " + e;
50458         }
50459     });
50460     
50461     
50462 };
50463
50464 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50465      
50466      
50467     editable : false,
50468      
50469     selectedClass: 'x-menu-item-checked', 
50470     
50471     // private
50472     onRender : function(ct, position){
50473         var _t = this;
50474         
50475         
50476         
50477         if(!this.tpl){
50478             var cls = 'x-combo-list';
50479
50480             
50481             this.tpl =  new Roo.Template({
50482                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50483                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50484                    '<span>{' + this.displayField + '}</span>' +
50485                     '</div>' 
50486                 
50487             });
50488         }
50489  
50490         
50491         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50492         this.view.singleSelect = false;
50493         this.view.multiSelect = true;
50494         this.view.toggleSelect = true;
50495         this.pageTb.add(new Roo.Toolbar.Fill(), {
50496             
50497             text: 'Done',
50498             handler: function()
50499             {
50500                 _t.collapse();
50501             }
50502         });
50503     },
50504     
50505     onViewOver : function(e, t){
50506         // do nothing...
50507         return;
50508         
50509     },
50510     
50511     onViewClick : function(doFocus,index){
50512         return;
50513         
50514     },
50515     select: function () {
50516         //Roo.log("SELECT CALLED");
50517     },
50518      
50519     selectByValue : function(xv, scrollIntoView){
50520         var ar = this.getValueArray();
50521         var sels = [];
50522         
50523         Roo.each(ar, function(v) {
50524             if(v === undefined || v === null){
50525                 return;
50526             }
50527             var r = this.findRecord(this.valueField, v);
50528             if(r){
50529                 sels.push(this.store.indexOf(r))
50530                 
50531             }
50532         },this);
50533         this.view.select(sels);
50534         return false;
50535     },
50536     
50537     
50538     
50539     onSelect : function(record, index){
50540        // Roo.log("onselect Called");
50541        // this is only called by the clear button now..
50542         this.view.clearSelections();
50543         this.setValue('[]');
50544         if (this.value != this.valueBefore) {
50545             this.fireEvent('change', this, this.value, this.valueBefore);
50546             this.valueBefore = this.value;
50547         }
50548     },
50549     getValueArray : function()
50550     {
50551         var ar = [] ;
50552         
50553         try {
50554             //Roo.log(this.value);
50555             if (typeof(this.value) == 'undefined') {
50556                 return [];
50557             }
50558             var ar = Roo.decode(this.value);
50559             return  ar instanceof Array ? ar : []; //?? valid?
50560             
50561         } catch(e) {
50562             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50563             return [];
50564         }
50565          
50566     },
50567     expand : function ()
50568     {
50569         
50570         Roo.form.ComboCheck.superclass.expand.call(this);
50571         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50572         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50573         
50574
50575     },
50576     
50577     collapse : function(){
50578         Roo.form.ComboCheck.superclass.collapse.call(this);
50579         var sl = this.view.getSelectedIndexes();
50580         var st = this.store;
50581         var nv = [];
50582         var tv = [];
50583         var r;
50584         Roo.each(sl, function(i) {
50585             r = st.getAt(i);
50586             nv.push(r.get(this.valueField));
50587         },this);
50588         this.setValue(Roo.encode(nv));
50589         if (this.value != this.valueBefore) {
50590
50591             this.fireEvent('change', this, this.value, this.valueBefore);
50592             this.valueBefore = this.value;
50593         }
50594         
50595     },
50596     
50597     setValue : function(v){
50598         // Roo.log(v);
50599         this.value = v;
50600         
50601         var vals = this.getValueArray();
50602         var tv = [];
50603         Roo.each(vals, function(k) {
50604             var r = this.findRecord(this.valueField, k);
50605             if(r){
50606                 tv.push(r.data[this.displayField]);
50607             }else if(this.valueNotFoundText !== undefined){
50608                 tv.push( this.valueNotFoundText );
50609             }
50610         },this);
50611        // Roo.log(tv);
50612         
50613         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50614         this.hiddenField.value = v;
50615         this.value = v;
50616     }
50617     
50618 });/*
50619  * Based on:
50620  * Ext JS Library 1.1.1
50621  * Copyright(c) 2006-2007, Ext JS, LLC.
50622  *
50623  * Originally Released Under LGPL - original licence link has changed is not relivant.
50624  *
50625  * Fork - LGPL
50626  * <script type="text/javascript">
50627  */
50628  
50629 /**
50630  * @class Roo.form.Signature
50631  * @extends Roo.form.Field
50632  * Signature field.  
50633  * @constructor
50634  * 
50635  * @param {Object} config Configuration options
50636  */
50637
50638 Roo.form.Signature = function(config){
50639     Roo.form.Signature.superclass.constructor.call(this, config);
50640     
50641     this.addEvents({// not in used??
50642          /**
50643          * @event confirm
50644          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50645              * @param {Roo.form.Signature} combo This combo box
50646              */
50647         'confirm' : true,
50648         /**
50649          * @event reset
50650          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50651              * @param {Roo.form.ComboBox} combo This combo box
50652              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50653              */
50654         'reset' : true
50655     });
50656 };
50657
50658 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50659     /**
50660      * @cfg {Object} labels Label to use when rendering a form.
50661      * defaults to 
50662      * labels : { 
50663      *      clear : "Clear",
50664      *      confirm : "Confirm"
50665      *  }
50666      */
50667     labels : { 
50668         clear : "Clear",
50669         confirm : "Confirm"
50670     },
50671     /**
50672      * @cfg {Number} width The signature panel width (defaults to 300)
50673      */
50674     width: 300,
50675     /**
50676      * @cfg {Number} height The signature panel height (defaults to 100)
50677      */
50678     height : 100,
50679     /**
50680      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50681      */
50682     allowBlank : false,
50683     
50684     //private
50685     // {Object} signPanel The signature SVG panel element (defaults to {})
50686     signPanel : {},
50687     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50688     isMouseDown : false,
50689     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50690     isConfirmed : false,
50691     // {String} signatureTmp SVG mapping string (defaults to empty string)
50692     signatureTmp : '',
50693     
50694     
50695     defaultAutoCreate : { // modified by initCompnoent..
50696         tag: "input",
50697         type:"hidden"
50698     },
50699
50700     // private
50701     onRender : function(ct, position){
50702         
50703         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50704         
50705         this.wrap = this.el.wrap({
50706             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50707         });
50708         
50709         this.createToolbar(this);
50710         this.signPanel = this.wrap.createChild({
50711                 tag: 'div',
50712                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50713             }, this.el
50714         );
50715             
50716         this.svgID = Roo.id();
50717         this.svgEl = this.signPanel.createChild({
50718               xmlns : 'http://www.w3.org/2000/svg',
50719               tag : 'svg',
50720               id : this.svgID + "-svg",
50721               width: this.width,
50722               height: this.height,
50723               viewBox: '0 0 '+this.width+' '+this.height,
50724               cn : [
50725                 {
50726                     tag: "rect",
50727                     id: this.svgID + "-svg-r",
50728                     width: this.width,
50729                     height: this.height,
50730                     fill: "#ffa"
50731                 },
50732                 {
50733                     tag: "line",
50734                     id: this.svgID + "-svg-l",
50735                     x1: "0", // start
50736                     y1: (this.height*0.8), // start set the line in 80% of height
50737                     x2: this.width, // end
50738                     y2: (this.height*0.8), // end set the line in 80% of height
50739                     'stroke': "#666",
50740                     'stroke-width': "1",
50741                     'stroke-dasharray': "3",
50742                     'shape-rendering': "crispEdges",
50743                     'pointer-events': "none"
50744                 },
50745                 {
50746                     tag: "path",
50747                     id: this.svgID + "-svg-p",
50748                     'stroke': "navy",
50749                     'stroke-width': "3",
50750                     'fill': "none",
50751                     'pointer-events': 'none'
50752                 }
50753               ]
50754         });
50755         this.createSVG();
50756         this.svgBox = this.svgEl.dom.getScreenCTM();
50757     },
50758     createSVG : function(){ 
50759         var svg = this.signPanel;
50760         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50761         var t = this;
50762
50763         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50764         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50765         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50766         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50767         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50768         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50769         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50770         
50771     },
50772     isTouchEvent : function(e){
50773         return e.type.match(/^touch/);
50774     },
50775     getCoords : function (e) {
50776         var pt    = this.svgEl.dom.createSVGPoint();
50777         pt.x = e.clientX; 
50778         pt.y = e.clientY;
50779         if (this.isTouchEvent(e)) {
50780             pt.x =  e.targetTouches[0].clientX;
50781             pt.y = e.targetTouches[0].clientY;
50782         }
50783         var a = this.svgEl.dom.getScreenCTM();
50784         var b = a.inverse();
50785         var mx = pt.matrixTransform(b);
50786         return mx.x + ',' + mx.y;
50787     },
50788     //mouse event headler 
50789     down : function (e) {
50790         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50791         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50792         
50793         this.isMouseDown = true;
50794         
50795         e.preventDefault();
50796     },
50797     move : function (e) {
50798         if (this.isMouseDown) {
50799             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50800             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50801         }
50802         
50803         e.preventDefault();
50804     },
50805     up : function (e) {
50806         this.isMouseDown = false;
50807         var sp = this.signatureTmp.split(' ');
50808         
50809         if(sp.length > 1){
50810             if(!sp[sp.length-2].match(/^L/)){
50811                 sp.pop();
50812                 sp.pop();
50813                 sp.push("");
50814                 this.signatureTmp = sp.join(" ");
50815             }
50816         }
50817         if(this.getValue() != this.signatureTmp){
50818             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50819             this.isConfirmed = false;
50820         }
50821         e.preventDefault();
50822     },
50823     
50824     /**
50825      * Protected method that will not generally be called directly. It
50826      * is called when the editor creates its toolbar. Override this method if you need to
50827      * add custom toolbar buttons.
50828      * @param {HtmlEditor} editor
50829      */
50830     createToolbar : function(editor){
50831          function btn(id, toggle, handler){
50832             var xid = fid + '-'+ id ;
50833             return {
50834                 id : xid,
50835                 cmd : id,
50836                 cls : 'x-btn-icon x-edit-'+id,
50837                 enableToggle:toggle !== false,
50838                 scope: editor, // was editor...
50839                 handler:handler||editor.relayBtnCmd,
50840                 clickEvent:'mousedown',
50841                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50842                 tabIndex:-1
50843             };
50844         }
50845         
50846         
50847         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50848         this.tb = tb;
50849         this.tb.add(
50850            {
50851                 cls : ' x-signature-btn x-signature-'+id,
50852                 scope: editor, // was editor...
50853                 handler: this.reset,
50854                 clickEvent:'mousedown',
50855                 text: this.labels.clear
50856             },
50857             {
50858                  xtype : 'Fill',
50859                  xns: Roo.Toolbar
50860             }, 
50861             {
50862                 cls : '  x-signature-btn x-signature-'+id,
50863                 scope: editor, // was editor...
50864                 handler: this.confirmHandler,
50865                 clickEvent:'mousedown',
50866                 text: this.labels.confirm
50867             }
50868         );
50869     
50870     },
50871     //public
50872     /**
50873      * when user is clicked confirm then show this image.....
50874      * 
50875      * @return {String} Image Data URI
50876      */
50877     getImageDataURI : function(){
50878         var svg = this.svgEl.dom.parentNode.innerHTML;
50879         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50880         return src; 
50881     },
50882     /**
50883      * 
50884      * @return {Boolean} this.isConfirmed
50885      */
50886     getConfirmed : function(){
50887         return this.isConfirmed;
50888     },
50889     /**
50890      * 
50891      * @return {Number} this.width
50892      */
50893     getWidth : function(){
50894         return this.width;
50895     },
50896     /**
50897      * 
50898      * @return {Number} this.height
50899      */
50900     getHeight : function(){
50901         return this.height;
50902     },
50903     // private
50904     getSignature : function(){
50905         return this.signatureTmp;
50906     },
50907     // private
50908     reset : function(){
50909         this.signatureTmp = '';
50910         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50911         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50912         this.isConfirmed = false;
50913         Roo.form.Signature.superclass.reset.call(this);
50914     },
50915     setSignature : function(s){
50916         this.signatureTmp = s;
50917         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50918         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50919         this.setValue(s);
50920         this.isConfirmed = false;
50921         Roo.form.Signature.superclass.reset.call(this);
50922     }, 
50923     test : function(){
50924 //        Roo.log(this.signPanel.dom.contentWindow.up())
50925     },
50926     //private
50927     setConfirmed : function(){
50928         
50929         
50930         
50931 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50932     },
50933     // private
50934     confirmHandler : function(){
50935         if(!this.getSignature()){
50936             return;
50937         }
50938         
50939         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50940         this.setValue(this.getSignature());
50941         this.isConfirmed = true;
50942         
50943         this.fireEvent('confirm', this);
50944     },
50945     // private
50946     // Subclasses should provide the validation implementation by overriding this
50947     validateValue : function(value){
50948         if(this.allowBlank){
50949             return true;
50950         }
50951         
50952         if(this.isConfirmed){
50953             return true;
50954         }
50955         return false;
50956     }
50957 });/*
50958  * Based on:
50959  * Ext JS Library 1.1.1
50960  * Copyright(c) 2006-2007, Ext JS, LLC.
50961  *
50962  * Originally Released Under LGPL - original licence link has changed is not relivant.
50963  *
50964  * Fork - LGPL
50965  * <script type="text/javascript">
50966  */
50967  
50968
50969 /**
50970  * @class Roo.form.ComboBox
50971  * @extends Roo.form.TriggerField
50972  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50973  * @constructor
50974  * Create a new ComboBox.
50975  * @param {Object} config Configuration options
50976  */
50977 Roo.form.Select = function(config){
50978     Roo.form.Select.superclass.constructor.call(this, config);
50979      
50980 };
50981
50982 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50983     /**
50984      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50985      */
50986     /**
50987      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50988      * rendering into an Roo.Editor, defaults to false)
50989      */
50990     /**
50991      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50992      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50993      */
50994     /**
50995      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50996      */
50997     /**
50998      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50999      * the dropdown list (defaults to undefined, with no header element)
51000      */
51001
51002      /**
51003      * @cfg {String/Roo.Template} tpl The template to use to render the output
51004      */
51005      
51006     // private
51007     defaultAutoCreate : {tag: "select"  },
51008     /**
51009      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51010      */
51011     listWidth: undefined,
51012     /**
51013      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51014      * mode = 'remote' or 'text' if mode = 'local')
51015      */
51016     displayField: undefined,
51017     /**
51018      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51019      * mode = 'remote' or 'value' if mode = 'local'). 
51020      * Note: use of a valueField requires the user make a selection
51021      * in order for a value to be mapped.
51022      */
51023     valueField: undefined,
51024     
51025     
51026     /**
51027      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51028      * field's data value (defaults to the underlying DOM element's name)
51029      */
51030     hiddenName: undefined,
51031     /**
51032      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51033      */
51034     listClass: '',
51035     /**
51036      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51037      */
51038     selectedClass: 'x-combo-selected',
51039     /**
51040      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51041      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51042      * which displays a downward arrow icon).
51043      */
51044     triggerClass : 'x-form-arrow-trigger',
51045     /**
51046      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51047      */
51048     shadow:'sides',
51049     /**
51050      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51051      * anchor positions (defaults to 'tl-bl')
51052      */
51053     listAlign: 'tl-bl?',
51054     /**
51055      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51056      */
51057     maxHeight: 300,
51058     /**
51059      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51060      * query specified by the allQuery config option (defaults to 'query')
51061      */
51062     triggerAction: 'query',
51063     /**
51064      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51065      * (defaults to 4, does not apply if editable = false)
51066      */
51067     minChars : 4,
51068     /**
51069      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51070      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51071      */
51072     typeAhead: false,
51073     /**
51074      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51075      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51076      */
51077     queryDelay: 500,
51078     /**
51079      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51080      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51081      */
51082     pageSize: 0,
51083     /**
51084      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51085      * when editable = true (defaults to false)
51086      */
51087     selectOnFocus:false,
51088     /**
51089      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51090      */
51091     queryParam: 'query',
51092     /**
51093      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51094      * when mode = 'remote' (defaults to 'Loading...')
51095      */
51096     loadingText: 'Loading...',
51097     /**
51098      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51099      */
51100     resizable: false,
51101     /**
51102      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51103      */
51104     handleHeight : 8,
51105     /**
51106      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51107      * traditional select (defaults to true)
51108      */
51109     editable: true,
51110     /**
51111      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51112      */
51113     allQuery: '',
51114     /**
51115      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51116      */
51117     mode: 'remote',
51118     /**
51119      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51120      * listWidth has a higher value)
51121      */
51122     minListWidth : 70,
51123     /**
51124      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51125      * allow the user to set arbitrary text into the field (defaults to false)
51126      */
51127     forceSelection:false,
51128     /**
51129      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51130      * if typeAhead = true (defaults to 250)
51131      */
51132     typeAheadDelay : 250,
51133     /**
51134      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51135      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51136      */
51137     valueNotFoundText : undefined,
51138     
51139     /**
51140      * @cfg {String} defaultValue The value displayed after loading the store.
51141      */
51142     defaultValue: '',
51143     
51144     /**
51145      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51146      */
51147     blockFocus : false,
51148     
51149     /**
51150      * @cfg {Boolean} disableClear Disable showing of clear button.
51151      */
51152     disableClear : false,
51153     /**
51154      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51155      */
51156     alwaysQuery : false,
51157     
51158     //private
51159     addicon : false,
51160     editicon: false,
51161     
51162     // element that contains real text value.. (when hidden is used..)
51163      
51164     // private
51165     onRender : function(ct, position){
51166         Roo.form.Field.prototype.onRender.call(this, ct, position);
51167         
51168         if(this.store){
51169             this.store.on('beforeload', this.onBeforeLoad, this);
51170             this.store.on('load', this.onLoad, this);
51171             this.store.on('loadexception', this.onLoadException, this);
51172             this.store.load({});
51173         }
51174         
51175         
51176         
51177     },
51178
51179     // private
51180     initEvents : function(){
51181         //Roo.form.ComboBox.superclass.initEvents.call(this);
51182  
51183     },
51184
51185     onDestroy : function(){
51186        
51187         if(this.store){
51188             this.store.un('beforeload', this.onBeforeLoad, this);
51189             this.store.un('load', this.onLoad, this);
51190             this.store.un('loadexception', this.onLoadException, this);
51191         }
51192         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51193     },
51194
51195     // private
51196     fireKey : function(e){
51197         if(e.isNavKeyPress() && !this.list.isVisible()){
51198             this.fireEvent("specialkey", this, e);
51199         }
51200     },
51201
51202     // private
51203     onResize: function(w, h){
51204         
51205         return; 
51206     
51207         
51208     },
51209
51210     /**
51211      * Allow or prevent the user from directly editing the field text.  If false is passed,
51212      * the user will only be able to select from the items defined in the dropdown list.  This method
51213      * is the runtime equivalent of setting the 'editable' config option at config time.
51214      * @param {Boolean} value True to allow the user to directly edit the field text
51215      */
51216     setEditable : function(value){
51217          
51218     },
51219
51220     // private
51221     onBeforeLoad : function(){
51222         
51223         Roo.log("Select before load");
51224         return;
51225     
51226         this.innerList.update(this.loadingText ?
51227                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51228         //this.restrictHeight();
51229         this.selectedIndex = -1;
51230     },
51231
51232     // private
51233     onLoad : function(){
51234
51235     
51236         var dom = this.el.dom;
51237         dom.innerHTML = '';
51238          var od = dom.ownerDocument;
51239          
51240         if (this.emptyText) {
51241             var op = od.createElement('option');
51242             op.setAttribute('value', '');
51243             op.innerHTML = String.format('{0}', this.emptyText);
51244             dom.appendChild(op);
51245         }
51246         if(this.store.getCount() > 0){
51247            
51248             var vf = this.valueField;
51249             var df = this.displayField;
51250             this.store.data.each(function(r) {
51251                 // which colmsn to use... testing - cdoe / title..
51252                 var op = od.createElement('option');
51253                 op.setAttribute('value', r.data[vf]);
51254                 op.innerHTML = String.format('{0}', r.data[df]);
51255                 dom.appendChild(op);
51256             });
51257             if (typeof(this.defaultValue != 'undefined')) {
51258                 this.setValue(this.defaultValue);
51259             }
51260             
51261              
51262         }else{
51263             //this.onEmptyResults();
51264         }
51265         //this.el.focus();
51266     },
51267     // private
51268     onLoadException : function()
51269     {
51270         dom.innerHTML = '';
51271             
51272         Roo.log("Select on load exception");
51273         return;
51274     
51275         this.collapse();
51276         Roo.log(this.store.reader.jsonData);
51277         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51278             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51279         }
51280         
51281         
51282     },
51283     // private
51284     onTypeAhead : function(){
51285          
51286     },
51287
51288     // private
51289     onSelect : function(record, index){
51290         Roo.log('on select?');
51291         return;
51292         if(this.fireEvent('beforeselect', this, record, index) !== false){
51293             this.setFromData(index > -1 ? record.data : false);
51294             this.collapse();
51295             this.fireEvent('select', this, record, index);
51296         }
51297     },
51298
51299     /**
51300      * Returns the currently selected field value or empty string if no value is set.
51301      * @return {String} value The selected value
51302      */
51303     getValue : function(){
51304         var dom = this.el.dom;
51305         this.value = dom.options[dom.selectedIndex].value;
51306         return this.value;
51307         
51308     },
51309
51310     /**
51311      * Clears any text/value currently set in the field
51312      */
51313     clearValue : function(){
51314         this.value = '';
51315         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51316         
51317     },
51318
51319     /**
51320      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51321      * will be displayed in the field.  If the value does not match the data value of an existing item,
51322      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51323      * Otherwise the field will be blank (although the value will still be set).
51324      * @param {String} value The value to match
51325      */
51326     setValue : function(v){
51327         var d = this.el.dom;
51328         for (var i =0; i < d.options.length;i++) {
51329             if (v == d.options[i].value) {
51330                 d.selectedIndex = i;
51331                 this.value = v;
51332                 return;
51333             }
51334         }
51335         this.clearValue();
51336     },
51337     /**
51338      * @property {Object} the last set data for the element
51339      */
51340     
51341     lastData : false,
51342     /**
51343      * Sets the value of the field based on a object which is related to the record format for the store.
51344      * @param {Object} value the value to set as. or false on reset?
51345      */
51346     setFromData : function(o){
51347         Roo.log('setfrom data?');
51348          
51349         
51350         
51351     },
51352     // private
51353     reset : function(){
51354         this.clearValue();
51355     },
51356     // private
51357     findRecord : function(prop, value){
51358         
51359         return false;
51360     
51361         var record;
51362         if(this.store.getCount() > 0){
51363             this.store.each(function(r){
51364                 if(r.data[prop] == value){
51365                     record = r;
51366                     return false;
51367                 }
51368                 return true;
51369             });
51370         }
51371         return record;
51372     },
51373     
51374     getName: function()
51375     {
51376         // returns hidden if it's set..
51377         if (!this.rendered) {return ''};
51378         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51379         
51380     },
51381      
51382
51383     
51384
51385     // private
51386     onEmptyResults : function(){
51387         Roo.log('empty results');
51388         //this.collapse();
51389     },
51390
51391     /**
51392      * Returns true if the dropdown list is expanded, else false.
51393      */
51394     isExpanded : function(){
51395         return false;
51396     },
51397
51398     /**
51399      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51400      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51401      * @param {String} value The data value of the item to select
51402      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51403      * selected item if it is not currently in view (defaults to true)
51404      * @return {Boolean} True if the value matched an item in the list, else false
51405      */
51406     selectByValue : function(v, scrollIntoView){
51407         Roo.log('select By Value');
51408         return false;
51409     
51410         if(v !== undefined && v !== null){
51411             var r = this.findRecord(this.valueField || this.displayField, v);
51412             if(r){
51413                 this.select(this.store.indexOf(r), scrollIntoView);
51414                 return true;
51415             }
51416         }
51417         return false;
51418     },
51419
51420     /**
51421      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51422      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51423      * @param {Number} index The zero-based index of the list item to select
51424      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51425      * selected item if it is not currently in view (defaults to true)
51426      */
51427     select : function(index, scrollIntoView){
51428         Roo.log('select ');
51429         return  ;
51430         
51431         this.selectedIndex = index;
51432         this.view.select(index);
51433         if(scrollIntoView !== false){
51434             var el = this.view.getNode(index);
51435             if(el){
51436                 this.innerList.scrollChildIntoView(el, false);
51437             }
51438         }
51439     },
51440
51441       
51442
51443     // private
51444     validateBlur : function(){
51445         
51446         return;
51447         
51448     },
51449
51450     // private
51451     initQuery : function(){
51452         this.doQuery(this.getRawValue());
51453     },
51454
51455     // private
51456     doForce : function(){
51457         if(this.el.dom.value.length > 0){
51458             this.el.dom.value =
51459                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51460              
51461         }
51462     },
51463
51464     /**
51465      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51466      * query allowing the query action to be canceled if needed.
51467      * @param {String} query The SQL query to execute
51468      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51469      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51470      * saved in the current store (defaults to false)
51471      */
51472     doQuery : function(q, forceAll){
51473         
51474         Roo.log('doQuery?');
51475         if(q === undefined || q === null){
51476             q = '';
51477         }
51478         var qe = {
51479             query: q,
51480             forceAll: forceAll,
51481             combo: this,
51482             cancel:false
51483         };
51484         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51485             return false;
51486         }
51487         q = qe.query;
51488         forceAll = qe.forceAll;
51489         if(forceAll === true || (q.length >= this.minChars)){
51490             if(this.lastQuery != q || this.alwaysQuery){
51491                 this.lastQuery = q;
51492                 if(this.mode == 'local'){
51493                     this.selectedIndex = -1;
51494                     if(forceAll){
51495                         this.store.clearFilter();
51496                     }else{
51497                         this.store.filter(this.displayField, q);
51498                     }
51499                     this.onLoad();
51500                 }else{
51501                     this.store.baseParams[this.queryParam] = q;
51502                     this.store.load({
51503                         params: this.getParams(q)
51504                     });
51505                     this.expand();
51506                 }
51507             }else{
51508                 this.selectedIndex = -1;
51509                 this.onLoad();   
51510             }
51511         }
51512     },
51513
51514     // private
51515     getParams : function(q){
51516         var p = {};
51517         //p[this.queryParam] = q;
51518         if(this.pageSize){
51519             p.start = 0;
51520             p.limit = this.pageSize;
51521         }
51522         return p;
51523     },
51524
51525     /**
51526      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51527      */
51528     collapse : function(){
51529         
51530     },
51531
51532     // private
51533     collapseIf : function(e){
51534         
51535     },
51536
51537     /**
51538      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51539      */
51540     expand : function(){
51541         
51542     } ,
51543
51544     // private
51545      
51546
51547     /** 
51548     * @cfg {Boolean} grow 
51549     * @hide 
51550     */
51551     /** 
51552     * @cfg {Number} growMin 
51553     * @hide 
51554     */
51555     /** 
51556     * @cfg {Number} growMax 
51557     * @hide 
51558     */
51559     /**
51560      * @hide
51561      * @method autoSize
51562      */
51563     
51564     setWidth : function()
51565     {
51566         
51567     },
51568     getResizeEl : function(){
51569         return this.el;
51570     }
51571 });//<script type="text/javasscript">
51572  
51573
51574 /**
51575  * @class Roo.DDView
51576  * A DnD enabled version of Roo.View.
51577  * @param {Element/String} container The Element in which to create the View.
51578  * @param {String} tpl The template string used to create the markup for each element of the View
51579  * @param {Object} config The configuration properties. These include all the config options of
51580  * {@link Roo.View} plus some specific to this class.<br>
51581  * <p>
51582  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51583  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51584  * <p>
51585  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51586 .x-view-drag-insert-above {
51587         border-top:1px dotted #3366cc;
51588 }
51589 .x-view-drag-insert-below {
51590         border-bottom:1px dotted #3366cc;
51591 }
51592 </code></pre>
51593  * 
51594  */
51595  
51596 Roo.DDView = function(container, tpl, config) {
51597     Roo.DDView.superclass.constructor.apply(this, arguments);
51598     this.getEl().setStyle("outline", "0px none");
51599     this.getEl().unselectable();
51600     if (this.dragGroup) {
51601         this.setDraggable(this.dragGroup.split(","));
51602     }
51603     if (this.dropGroup) {
51604         this.setDroppable(this.dropGroup.split(","));
51605     }
51606     if (this.deletable) {
51607         this.setDeletable();
51608     }
51609     this.isDirtyFlag = false;
51610         this.addEvents({
51611                 "drop" : true
51612         });
51613 };
51614
51615 Roo.extend(Roo.DDView, Roo.View, {
51616 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51617 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51618 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51619 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51620
51621         isFormField: true,
51622
51623         reset: Roo.emptyFn,
51624         
51625         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51626
51627         validate: function() {
51628                 return true;
51629         },
51630         
51631         destroy: function() {
51632                 this.purgeListeners();
51633                 this.getEl.removeAllListeners();
51634                 this.getEl().remove();
51635                 if (this.dragZone) {
51636                         if (this.dragZone.destroy) {
51637                                 this.dragZone.destroy();
51638                         }
51639                 }
51640                 if (this.dropZone) {
51641                         if (this.dropZone.destroy) {
51642                                 this.dropZone.destroy();
51643                         }
51644                 }
51645         },
51646
51647 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51648         getName: function() {
51649                 return this.name;
51650         },
51651
51652 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51653         setValue: function(v) {
51654                 if (!this.store) {
51655                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51656                 }
51657                 var data = {};
51658                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51659                 this.store.proxy = new Roo.data.MemoryProxy(data);
51660                 this.store.load();
51661         },
51662
51663 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51664         getValue: function() {
51665                 var result = '(';
51666                 this.store.each(function(rec) {
51667                         result += rec.id + ',';
51668                 });
51669                 return result.substr(0, result.length - 1) + ')';
51670         },
51671         
51672         getIds: function() {
51673                 var i = 0, result = new Array(this.store.getCount());
51674                 this.store.each(function(rec) {
51675                         result[i++] = rec.id;
51676                 });
51677                 return result;
51678         },
51679         
51680         isDirty: function() {
51681                 return this.isDirtyFlag;
51682         },
51683
51684 /**
51685  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51686  *      whole Element becomes the target, and this causes the drop gesture to append.
51687  */
51688     getTargetFromEvent : function(e) {
51689                 var target = e.getTarget();
51690                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51691                 target = target.parentNode;
51692                 }
51693                 if (!target) {
51694                         target = this.el.dom.lastChild || this.el.dom;
51695                 }
51696                 return target;
51697     },
51698
51699 /**
51700  *      Create the drag data which consists of an object which has the property "ddel" as
51701  *      the drag proxy element. 
51702  */
51703     getDragData : function(e) {
51704         var target = this.findItemFromChild(e.getTarget());
51705                 if(target) {
51706                         this.handleSelection(e);
51707                         var selNodes = this.getSelectedNodes();
51708             var dragData = {
51709                 source: this,
51710                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51711                 nodes: selNodes,
51712                 records: []
51713                         };
51714                         var selectedIndices = this.getSelectedIndexes();
51715                         for (var i = 0; i < selectedIndices.length; i++) {
51716                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51717                         }
51718                         if (selNodes.length == 1) {
51719                                 dragData.ddel = target.cloneNode(true); // the div element
51720                         } else {
51721                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51722                                 div.className = 'multi-proxy';
51723                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51724                                         div.appendChild(selNodes[i].cloneNode(true));
51725                                 }
51726                                 dragData.ddel = div;
51727                         }
51728             //console.log(dragData)
51729             //console.log(dragData.ddel.innerHTML)
51730                         return dragData;
51731                 }
51732         //console.log('nodragData')
51733                 return false;
51734     },
51735     
51736 /**     Specify to which ddGroup items in this DDView may be dragged. */
51737     setDraggable: function(ddGroup) {
51738         if (ddGroup instanceof Array) {
51739                 Roo.each(ddGroup, this.setDraggable, this);
51740                 return;
51741         }
51742         if (this.dragZone) {
51743                 this.dragZone.addToGroup(ddGroup);
51744         } else {
51745                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51746                                 containerScroll: true,
51747                                 ddGroup: ddGroup 
51748
51749                         });
51750 //                      Draggability implies selection. DragZone's mousedown selects the element.
51751                         if (!this.multiSelect) { this.singleSelect = true; }
51752
51753 //                      Wire the DragZone's handlers up to methods in *this*
51754                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51755                 }
51756     },
51757
51758 /**     Specify from which ddGroup this DDView accepts drops. */
51759     setDroppable: function(ddGroup) {
51760         if (ddGroup instanceof Array) {
51761                 Roo.each(ddGroup, this.setDroppable, this);
51762                 return;
51763         }
51764         if (this.dropZone) {
51765                 this.dropZone.addToGroup(ddGroup);
51766         } else {
51767                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51768                                 containerScroll: true,
51769                                 ddGroup: ddGroup
51770                         });
51771
51772 //                      Wire the DropZone's handlers up to methods in *this*
51773                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51774                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51775                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51776                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51777                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51778                 }
51779     },
51780
51781 /**     Decide whether to drop above or below a View node. */
51782     getDropPoint : function(e, n, dd){
51783         if (n == this.el.dom) { return "above"; }
51784                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51785                 var c = t + (b - t) / 2;
51786                 var y = Roo.lib.Event.getPageY(e);
51787                 if(y <= c) {
51788                         return "above";
51789                 }else{
51790                         return "below";
51791                 }
51792     },
51793
51794     onNodeEnter : function(n, dd, e, data){
51795                 return false;
51796     },
51797     
51798     onNodeOver : function(n, dd, e, data){
51799                 var pt = this.getDropPoint(e, n, dd);
51800                 // set the insert point style on the target node
51801                 var dragElClass = this.dropNotAllowed;
51802                 if (pt) {
51803                         var targetElClass;
51804                         if (pt == "above"){
51805                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51806                                 targetElClass = "x-view-drag-insert-above";
51807                         } else {
51808                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51809                                 targetElClass = "x-view-drag-insert-below";
51810                         }
51811                         if (this.lastInsertClass != targetElClass){
51812                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51813                                 this.lastInsertClass = targetElClass;
51814                         }
51815                 }
51816                 return dragElClass;
51817         },
51818
51819     onNodeOut : function(n, dd, e, data){
51820                 this.removeDropIndicators(n);
51821     },
51822
51823     onNodeDrop : function(n, dd, e, data){
51824         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51825                 return false;
51826         }
51827         var pt = this.getDropPoint(e, n, dd);
51828                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51829                 if (pt == "below") { insertAt++; }
51830                 for (var i = 0; i < data.records.length; i++) {
51831                         var r = data.records[i];
51832                         var dup = this.store.getById(r.id);
51833                         if (dup && (dd != this.dragZone)) {
51834                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51835                         } else {
51836                                 if (data.copy) {
51837                                         this.store.insert(insertAt++, r.copy());
51838                                 } else {
51839                                         data.source.isDirtyFlag = true;
51840                                         r.store.remove(r);
51841                                         this.store.insert(insertAt++, r);
51842                                 }
51843                                 this.isDirtyFlag = true;
51844                         }
51845                 }
51846                 this.dragZone.cachedTarget = null;
51847                 return true;
51848     },
51849
51850     removeDropIndicators : function(n){
51851                 if(n){
51852                         Roo.fly(n).removeClass([
51853                                 "x-view-drag-insert-above",
51854                                 "x-view-drag-insert-below"]);
51855                         this.lastInsertClass = "_noclass";
51856                 }
51857     },
51858
51859 /**
51860  *      Utility method. Add a delete option to the DDView's context menu.
51861  *      @param {String} imageUrl The URL of the "delete" icon image.
51862  */
51863         setDeletable: function(imageUrl) {
51864                 if (!this.singleSelect && !this.multiSelect) {
51865                         this.singleSelect = true;
51866                 }
51867                 var c = this.getContextMenu();
51868                 this.contextMenu.on("itemclick", function(item) {
51869                         switch (item.id) {
51870                                 case "delete":
51871                                         this.remove(this.getSelectedIndexes());
51872                                         break;
51873                         }
51874                 }, this);
51875                 this.contextMenu.add({
51876                         icon: imageUrl,
51877                         id: "delete",
51878                         text: 'Delete'
51879                 });
51880         },
51881         
51882 /**     Return the context menu for this DDView. */
51883         getContextMenu: function() {
51884                 if (!this.contextMenu) {
51885 //                      Create the View's context menu
51886                         this.contextMenu = new Roo.menu.Menu({
51887                                 id: this.id + "-contextmenu"
51888                         });
51889                         this.el.on("contextmenu", this.showContextMenu, this);
51890                 }
51891                 return this.contextMenu;
51892         },
51893         
51894         disableContextMenu: function() {
51895                 if (this.contextMenu) {
51896                         this.el.un("contextmenu", this.showContextMenu, this);
51897                 }
51898         },
51899
51900         showContextMenu: function(e, item) {
51901         item = this.findItemFromChild(e.getTarget());
51902                 if (item) {
51903                         e.stopEvent();
51904                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51905                         this.contextMenu.showAt(e.getXY());
51906             }
51907     },
51908
51909 /**
51910  *      Remove {@link Roo.data.Record}s at the specified indices.
51911  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51912  */
51913     remove: function(selectedIndices) {
51914                 selectedIndices = [].concat(selectedIndices);
51915                 for (var i = 0; i < selectedIndices.length; i++) {
51916                         var rec = this.store.getAt(selectedIndices[i]);
51917                         this.store.remove(rec);
51918                 }
51919     },
51920
51921 /**
51922  *      Double click fires the event, but also, if this is draggable, and there is only one other
51923  *      related DropZone, it transfers the selected node.
51924  */
51925     onDblClick : function(e){
51926         var item = this.findItemFromChild(e.getTarget());
51927         if(item){
51928             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51929                 return false;
51930             }
51931             if (this.dragGroup) {
51932                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51933                     while (targets.indexOf(this.dropZone) > -1) {
51934                             targets.remove(this.dropZone);
51935                                 }
51936                     if (targets.length == 1) {
51937                                         this.dragZone.cachedTarget = null;
51938                         var el = Roo.get(targets[0].getEl());
51939                         var box = el.getBox(true);
51940                         targets[0].onNodeDrop(el.dom, {
51941                                 target: el.dom,
51942                                 xy: [box.x, box.y + box.height - 1]
51943                         }, null, this.getDragData(e));
51944                     }
51945                 }
51946         }
51947     },
51948     
51949     handleSelection: function(e) {
51950                 this.dragZone.cachedTarget = null;
51951         var item = this.findItemFromChild(e.getTarget());
51952         if (!item) {
51953                 this.clearSelections(true);
51954                 return;
51955         }
51956                 if (item && (this.multiSelect || this.singleSelect)){
51957                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51958                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51959                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51960                                 this.unselect(item);
51961                         } else {
51962                                 this.select(item, this.multiSelect && e.ctrlKey);
51963                                 this.lastSelection = item;
51964                         }
51965                 }
51966     },
51967
51968     onItemClick : function(item, index, e){
51969                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51970                         return false;
51971                 }
51972                 return true;
51973     },
51974
51975     unselect : function(nodeInfo, suppressEvent){
51976                 var node = this.getNode(nodeInfo);
51977                 if(node && this.isSelected(node)){
51978                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51979                                 Roo.fly(node).removeClass(this.selectedClass);
51980                                 this.selections.remove(node);
51981                                 if(!suppressEvent){
51982                                         this.fireEvent("selectionchange", this, this.selections);
51983                                 }
51984                         }
51985                 }
51986     }
51987 });
51988 /*
51989  * Based on:
51990  * Ext JS Library 1.1.1
51991  * Copyright(c) 2006-2007, Ext JS, LLC.
51992  *
51993  * Originally Released Under LGPL - original licence link has changed is not relivant.
51994  *
51995  * Fork - LGPL
51996  * <script type="text/javascript">
51997  */
51998  
51999 /**
52000  * @class Roo.LayoutManager
52001  * @extends Roo.util.Observable
52002  * Base class for layout managers.
52003  */
52004 Roo.LayoutManager = function(container, config){
52005     Roo.LayoutManager.superclass.constructor.call(this);
52006     this.el = Roo.get(container);
52007     // ie scrollbar fix
52008     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52009         document.body.scroll = "no";
52010     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52011         this.el.position('relative');
52012     }
52013     this.id = this.el.id;
52014     this.el.addClass("x-layout-container");
52015     /** false to disable window resize monitoring @type Boolean */
52016     this.monitorWindowResize = true;
52017     this.regions = {};
52018     this.addEvents({
52019         /**
52020          * @event layout
52021          * Fires when a layout is performed. 
52022          * @param {Roo.LayoutManager} this
52023          */
52024         "layout" : true,
52025         /**
52026          * @event regionresized
52027          * Fires when the user resizes a region. 
52028          * @param {Roo.LayoutRegion} region The resized region
52029          * @param {Number} newSize The new size (width for east/west, height for north/south)
52030          */
52031         "regionresized" : true,
52032         /**
52033          * @event regioncollapsed
52034          * Fires when a region is collapsed. 
52035          * @param {Roo.LayoutRegion} region The collapsed region
52036          */
52037         "regioncollapsed" : true,
52038         /**
52039          * @event regionexpanded
52040          * Fires when a region is expanded.  
52041          * @param {Roo.LayoutRegion} region The expanded region
52042          */
52043         "regionexpanded" : true
52044     });
52045     this.updating = false;
52046     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52047 };
52048
52049 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52050     /**
52051      * Returns true if this layout is currently being updated
52052      * @return {Boolean}
52053      */
52054     isUpdating : function(){
52055         return this.updating; 
52056     },
52057     
52058     /**
52059      * Suspend the LayoutManager from doing auto-layouts while
52060      * making multiple add or remove calls
52061      */
52062     beginUpdate : function(){
52063         this.updating = true;    
52064     },
52065     
52066     /**
52067      * Restore auto-layouts and optionally disable the manager from performing a layout
52068      * @param {Boolean} noLayout true to disable a layout update 
52069      */
52070     endUpdate : function(noLayout){
52071         this.updating = false;
52072         if(!noLayout){
52073             this.layout();
52074         }    
52075     },
52076     
52077     layout: function(){
52078         
52079     },
52080     
52081     onRegionResized : function(region, newSize){
52082         this.fireEvent("regionresized", region, newSize);
52083         this.layout();
52084     },
52085     
52086     onRegionCollapsed : function(region){
52087         this.fireEvent("regioncollapsed", region);
52088     },
52089     
52090     onRegionExpanded : function(region){
52091         this.fireEvent("regionexpanded", region);
52092     },
52093         
52094     /**
52095      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52096      * performs box-model adjustments.
52097      * @return {Object} The size as an object {width: (the width), height: (the height)}
52098      */
52099     getViewSize : function(){
52100         var size;
52101         if(this.el.dom != document.body){
52102             size = this.el.getSize();
52103         }else{
52104             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52105         }
52106         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52107         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52108         return size;
52109     },
52110     
52111     /**
52112      * Returns the Element this layout is bound to.
52113      * @return {Roo.Element}
52114      */
52115     getEl : function(){
52116         return this.el;
52117     },
52118     
52119     /**
52120      * Returns the specified region.
52121      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52122      * @return {Roo.LayoutRegion}
52123      */
52124     getRegion : function(target){
52125         return this.regions[target.toLowerCase()];
52126     },
52127     
52128     onWindowResize : function(){
52129         if(this.monitorWindowResize){
52130             this.layout();
52131         }
52132     }
52133 });/*
52134  * Based on:
52135  * Ext JS Library 1.1.1
52136  * Copyright(c) 2006-2007, Ext JS, LLC.
52137  *
52138  * Originally Released Under LGPL - original licence link has changed is not relivant.
52139  *
52140  * Fork - LGPL
52141  * <script type="text/javascript">
52142  */
52143 /**
52144  * @class Roo.BorderLayout
52145  * @extends Roo.LayoutManager
52146  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52147  * please see: <br><br>
52148  * <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>
52149  * <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>
52150  * Example:
52151  <pre><code>
52152  var layout = new Roo.BorderLayout(document.body, {
52153     north: {
52154         initialSize: 25,
52155         titlebar: false
52156     },
52157     west: {
52158         split:true,
52159         initialSize: 200,
52160         minSize: 175,
52161         maxSize: 400,
52162         titlebar: true,
52163         collapsible: true
52164     },
52165     east: {
52166         split:true,
52167         initialSize: 202,
52168         minSize: 175,
52169         maxSize: 400,
52170         titlebar: true,
52171         collapsible: true
52172     },
52173     south: {
52174         split:true,
52175         initialSize: 100,
52176         minSize: 100,
52177         maxSize: 200,
52178         titlebar: true,
52179         collapsible: true
52180     },
52181     center: {
52182         titlebar: true,
52183         autoScroll:true,
52184         resizeTabs: true,
52185         minTabWidth: 50,
52186         preferredTabWidth: 150
52187     }
52188 });
52189
52190 // shorthand
52191 var CP = Roo.ContentPanel;
52192
52193 layout.beginUpdate();
52194 layout.add("north", new CP("north", "North"));
52195 layout.add("south", new CP("south", {title: "South", closable: true}));
52196 layout.add("west", new CP("west", {title: "West"}));
52197 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52198 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52199 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52200 layout.getRegion("center").showPanel("center1");
52201 layout.endUpdate();
52202 </code></pre>
52203
52204 <b>The container the layout is rendered into can be either the body element or any other element.
52205 If it is not the body element, the container needs to either be an absolute positioned element,
52206 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52207 the container size if it is not the body element.</b>
52208
52209 * @constructor
52210 * Create a new BorderLayout
52211 * @param {String/HTMLElement/Element} container The container this layout is bound to
52212 * @param {Object} config Configuration options
52213  */
52214 Roo.BorderLayout = function(container, config){
52215     config = config || {};
52216     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52217     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52218     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52219         var target = this.factory.validRegions[i];
52220         if(config[target]){
52221             this.addRegion(target, config[target]);
52222         }
52223     }
52224 };
52225
52226 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52227     /**
52228      * Creates and adds a new region if it doesn't already exist.
52229      * @param {String} target The target region key (north, south, east, west or center).
52230      * @param {Object} config The regions config object
52231      * @return {BorderLayoutRegion} The new region
52232      */
52233     addRegion : function(target, config){
52234         if(!this.regions[target]){
52235             var r = this.factory.create(target, this, config);
52236             this.bindRegion(target, r);
52237         }
52238         return this.regions[target];
52239     },
52240
52241     // private (kinda)
52242     bindRegion : function(name, r){
52243         this.regions[name] = r;
52244         r.on("visibilitychange", this.layout, this);
52245         r.on("paneladded", this.layout, this);
52246         r.on("panelremoved", this.layout, this);
52247         r.on("invalidated", this.layout, this);
52248         r.on("resized", this.onRegionResized, this);
52249         r.on("collapsed", this.onRegionCollapsed, this);
52250         r.on("expanded", this.onRegionExpanded, this);
52251     },
52252
52253     /**
52254      * Performs a layout update.
52255      */
52256     layout : function(){
52257         if(this.updating) {
52258             return;
52259         }
52260         var size = this.getViewSize();
52261         var w = size.width;
52262         var h = size.height;
52263         var centerW = w;
52264         var centerH = h;
52265         var centerY = 0;
52266         var centerX = 0;
52267         //var x = 0, y = 0;
52268
52269         var rs = this.regions;
52270         var north = rs["north"];
52271         var south = rs["south"]; 
52272         var west = rs["west"];
52273         var east = rs["east"];
52274         var center = rs["center"];
52275         //if(this.hideOnLayout){ // not supported anymore
52276             //c.el.setStyle("display", "none");
52277         //}
52278         if(north && north.isVisible()){
52279             var b = north.getBox();
52280             var m = north.getMargins();
52281             b.width = w - (m.left+m.right);
52282             b.x = m.left;
52283             b.y = m.top;
52284             centerY = b.height + b.y + m.bottom;
52285             centerH -= centerY;
52286             north.updateBox(this.safeBox(b));
52287         }
52288         if(south && south.isVisible()){
52289             var b = south.getBox();
52290             var m = south.getMargins();
52291             b.width = w - (m.left+m.right);
52292             b.x = m.left;
52293             var totalHeight = (b.height + m.top + m.bottom);
52294             b.y = h - totalHeight + m.top;
52295             centerH -= totalHeight;
52296             south.updateBox(this.safeBox(b));
52297         }
52298         if(west && west.isVisible()){
52299             var b = west.getBox();
52300             var m = west.getMargins();
52301             b.height = centerH - (m.top+m.bottom);
52302             b.x = m.left;
52303             b.y = centerY + m.top;
52304             var totalWidth = (b.width + m.left + m.right);
52305             centerX += totalWidth;
52306             centerW -= totalWidth;
52307             west.updateBox(this.safeBox(b));
52308         }
52309         if(east && east.isVisible()){
52310             var b = east.getBox();
52311             var m = east.getMargins();
52312             b.height = centerH - (m.top+m.bottom);
52313             var totalWidth = (b.width + m.left + m.right);
52314             b.x = w - totalWidth + m.left;
52315             b.y = centerY + m.top;
52316             centerW -= totalWidth;
52317             east.updateBox(this.safeBox(b));
52318         }
52319         if(center){
52320             var m = center.getMargins();
52321             var centerBox = {
52322                 x: centerX + m.left,
52323                 y: centerY + m.top,
52324                 width: centerW - (m.left+m.right),
52325                 height: centerH - (m.top+m.bottom)
52326             };
52327             //if(this.hideOnLayout){
52328                 //center.el.setStyle("display", "block");
52329             //}
52330             center.updateBox(this.safeBox(centerBox));
52331         }
52332         this.el.repaint();
52333         this.fireEvent("layout", this);
52334     },
52335
52336     // private
52337     safeBox : function(box){
52338         box.width = Math.max(0, box.width);
52339         box.height = Math.max(0, box.height);
52340         return box;
52341     },
52342
52343     /**
52344      * Adds a ContentPanel (or subclass) to this layout.
52345      * @param {String} target The target region key (north, south, east, west or center).
52346      * @param {Roo.ContentPanel} panel The panel to add
52347      * @return {Roo.ContentPanel} The added panel
52348      */
52349     add : function(target, panel){
52350          
52351         target = target.toLowerCase();
52352         return this.regions[target].add(panel);
52353     },
52354
52355     /**
52356      * Remove a ContentPanel (or subclass) to this layout.
52357      * @param {String} target The target region key (north, south, east, west or center).
52358      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52359      * @return {Roo.ContentPanel} The removed panel
52360      */
52361     remove : function(target, panel){
52362         target = target.toLowerCase();
52363         return this.regions[target].remove(panel);
52364     },
52365
52366     /**
52367      * Searches all regions for a panel with the specified id
52368      * @param {String} panelId
52369      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52370      */
52371     findPanel : function(panelId){
52372         var rs = this.regions;
52373         for(var target in rs){
52374             if(typeof rs[target] != "function"){
52375                 var p = rs[target].getPanel(panelId);
52376                 if(p){
52377                     return p;
52378                 }
52379             }
52380         }
52381         return null;
52382     },
52383
52384     /**
52385      * Searches all regions for a panel with the specified id and activates (shows) it.
52386      * @param {String/ContentPanel} panelId The panels id or the panel itself
52387      * @return {Roo.ContentPanel} The shown panel or null
52388      */
52389     showPanel : function(panelId) {
52390       var rs = this.regions;
52391       for(var target in rs){
52392          var r = rs[target];
52393          if(typeof r != "function"){
52394             if(r.hasPanel(panelId)){
52395                return r.showPanel(panelId);
52396             }
52397          }
52398       }
52399       return null;
52400    },
52401
52402    /**
52403      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52404      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52405      */
52406     restoreState : function(provider){
52407         if(!provider){
52408             provider = Roo.state.Manager;
52409         }
52410         var sm = new Roo.LayoutStateManager();
52411         sm.init(this, provider);
52412     },
52413
52414     /**
52415      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52416      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52417      * a valid ContentPanel config object.  Example:
52418      * <pre><code>
52419 // Create the main layout
52420 var layout = new Roo.BorderLayout('main-ct', {
52421     west: {
52422         split:true,
52423         minSize: 175,
52424         titlebar: true
52425     },
52426     center: {
52427         title:'Components'
52428     }
52429 }, 'main-ct');
52430
52431 // Create and add multiple ContentPanels at once via configs
52432 layout.batchAdd({
52433    west: {
52434        id: 'source-files',
52435        autoCreate:true,
52436        title:'Ext Source Files',
52437        autoScroll:true,
52438        fitToFrame:true
52439    },
52440    center : {
52441        el: cview,
52442        autoScroll:true,
52443        fitToFrame:true,
52444        toolbar: tb,
52445        resizeEl:'cbody'
52446    }
52447 });
52448 </code></pre>
52449      * @param {Object} regions An object containing ContentPanel configs by region name
52450      */
52451     batchAdd : function(regions){
52452         this.beginUpdate();
52453         for(var rname in regions){
52454             var lr = this.regions[rname];
52455             if(lr){
52456                 this.addTypedPanels(lr, regions[rname]);
52457             }
52458         }
52459         this.endUpdate();
52460     },
52461
52462     // private
52463     addTypedPanels : function(lr, ps){
52464         if(typeof ps == 'string'){
52465             lr.add(new Roo.ContentPanel(ps));
52466         }
52467         else if(ps instanceof Array){
52468             for(var i =0, len = ps.length; i < len; i++){
52469                 this.addTypedPanels(lr, ps[i]);
52470             }
52471         }
52472         else if(!ps.events){ // raw config?
52473             var el = ps.el;
52474             delete ps.el; // prevent conflict
52475             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52476         }
52477         else {  // panel object assumed!
52478             lr.add(ps);
52479         }
52480     },
52481     /**
52482      * Adds a xtype elements to the layout.
52483      * <pre><code>
52484
52485 layout.addxtype({
52486        xtype : 'ContentPanel',
52487        region: 'west',
52488        items: [ .... ]
52489    }
52490 );
52491
52492 layout.addxtype({
52493         xtype : 'NestedLayoutPanel',
52494         region: 'west',
52495         layout: {
52496            center: { },
52497            west: { }   
52498         },
52499         items : [ ... list of content panels or nested layout panels.. ]
52500    }
52501 );
52502 </code></pre>
52503      * @param {Object} cfg Xtype definition of item to add.
52504      */
52505     addxtype : function(cfg)
52506     {
52507         // basically accepts a pannel...
52508         // can accept a layout region..!?!?
52509         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52510         
52511         if (!cfg.xtype.match(/Panel$/)) {
52512             return false;
52513         }
52514         var ret = false;
52515         
52516         if (typeof(cfg.region) == 'undefined') {
52517             Roo.log("Failed to add Panel, region was not set");
52518             Roo.log(cfg);
52519             return false;
52520         }
52521         var region = cfg.region;
52522         delete cfg.region;
52523         
52524           
52525         var xitems = [];
52526         if (cfg.items) {
52527             xitems = cfg.items;
52528             delete cfg.items;
52529         }
52530         var nb = false;
52531         
52532         switch(cfg.xtype) 
52533         {
52534             case 'ContentPanel':  // ContentPanel (el, cfg)
52535             case 'ScrollPanel':  // ContentPanel (el, cfg)
52536             case 'ViewPanel': 
52537                 if(cfg.autoCreate) {
52538                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52539                 } else {
52540                     var el = this.el.createChild();
52541                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52542                 }
52543                 
52544                 this.add(region, ret);
52545                 break;
52546             
52547             
52548             case 'TreePanel': // our new panel!
52549                 cfg.el = this.el.createChild();
52550                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52551                 this.add(region, ret);
52552                 break;
52553             
52554             case 'NestedLayoutPanel': 
52555                 // create a new Layout (which is  a Border Layout...
52556                 var el = this.el.createChild();
52557                 var clayout = cfg.layout;
52558                 delete cfg.layout;
52559                 clayout.items   = clayout.items  || [];
52560                 // replace this exitems with the clayout ones..
52561                 xitems = clayout.items;
52562                  
52563                 
52564                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52565                     cfg.background = false;
52566                 }
52567                 var layout = new Roo.BorderLayout(el, clayout);
52568                 
52569                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52570                 //console.log('adding nested layout panel '  + cfg.toSource());
52571                 this.add(region, ret);
52572                 nb = {}; /// find first...
52573                 break;
52574                 
52575             case 'GridPanel': 
52576             
52577                 // needs grid and region
52578                 
52579                 //var el = this.getRegion(region).el.createChild();
52580                 var el = this.el.createChild();
52581                 // create the grid first...
52582                 
52583                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52584                 delete cfg.grid;
52585                 if (region == 'center' && this.active ) {
52586                     cfg.background = false;
52587                 }
52588                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52589                 
52590                 this.add(region, ret);
52591                 if (cfg.background) {
52592                     ret.on('activate', function(gp) {
52593                         if (!gp.grid.rendered) {
52594                             gp.grid.render();
52595                         }
52596                     });
52597                 } else {
52598                     grid.render();
52599                 }
52600                 break;
52601            
52602            
52603            
52604                 
52605                 
52606                 
52607             default:
52608                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52609                     
52610                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52611                     this.add(region, ret);
52612                 } else {
52613                 
52614                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52615                     return null;
52616                 }
52617                 
52618              // GridPanel (grid, cfg)
52619             
52620         }
52621         this.beginUpdate();
52622         // add children..
52623         var region = '';
52624         var abn = {};
52625         Roo.each(xitems, function(i)  {
52626             region = nb && i.region ? i.region : false;
52627             
52628             var add = ret.addxtype(i);
52629            
52630             if (region) {
52631                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52632                 if (!i.background) {
52633                     abn[region] = nb[region] ;
52634                 }
52635             }
52636             
52637         });
52638         this.endUpdate();
52639
52640         // make the last non-background panel active..
52641         //if (nb) { Roo.log(abn); }
52642         if (nb) {
52643             
52644             for(var r in abn) {
52645                 region = this.getRegion(r);
52646                 if (region) {
52647                     // tried using nb[r], but it does not work..
52648                      
52649                     region.showPanel(abn[r]);
52650                    
52651                 }
52652             }
52653         }
52654         return ret;
52655         
52656     }
52657 });
52658
52659 /**
52660  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52661  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52662  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52663  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52664  * <pre><code>
52665 // shorthand
52666 var CP = Roo.ContentPanel;
52667
52668 var layout = Roo.BorderLayout.create({
52669     north: {
52670         initialSize: 25,
52671         titlebar: false,
52672         panels: [new CP("north", "North")]
52673     },
52674     west: {
52675         split:true,
52676         initialSize: 200,
52677         minSize: 175,
52678         maxSize: 400,
52679         titlebar: true,
52680         collapsible: true,
52681         panels: [new CP("west", {title: "West"})]
52682     },
52683     east: {
52684         split:true,
52685         initialSize: 202,
52686         minSize: 175,
52687         maxSize: 400,
52688         titlebar: true,
52689         collapsible: true,
52690         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52691     },
52692     south: {
52693         split:true,
52694         initialSize: 100,
52695         minSize: 100,
52696         maxSize: 200,
52697         titlebar: true,
52698         collapsible: true,
52699         panels: [new CP("south", {title: "South", closable: true})]
52700     },
52701     center: {
52702         titlebar: true,
52703         autoScroll:true,
52704         resizeTabs: true,
52705         minTabWidth: 50,
52706         preferredTabWidth: 150,
52707         panels: [
52708             new CP("center1", {title: "Close Me", closable: true}),
52709             new CP("center2", {title: "Center Panel", closable: false})
52710         ]
52711     }
52712 }, document.body);
52713
52714 layout.getRegion("center").showPanel("center1");
52715 </code></pre>
52716  * @param config
52717  * @param targetEl
52718  */
52719 Roo.BorderLayout.create = function(config, targetEl){
52720     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52721     layout.beginUpdate();
52722     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52723     for(var j = 0, jlen = regions.length; j < jlen; j++){
52724         var lr = regions[j];
52725         if(layout.regions[lr] && config[lr].panels){
52726             var r = layout.regions[lr];
52727             var ps = config[lr].panels;
52728             layout.addTypedPanels(r, ps);
52729         }
52730     }
52731     layout.endUpdate();
52732     return layout;
52733 };
52734
52735 // private
52736 Roo.BorderLayout.RegionFactory = {
52737     // private
52738     validRegions : ["north","south","east","west","center"],
52739
52740     // private
52741     create : function(target, mgr, config){
52742         target = target.toLowerCase();
52743         if(config.lightweight || config.basic){
52744             return new Roo.BasicLayoutRegion(mgr, config, target);
52745         }
52746         switch(target){
52747             case "north":
52748                 return new Roo.NorthLayoutRegion(mgr, config);
52749             case "south":
52750                 return new Roo.SouthLayoutRegion(mgr, config);
52751             case "east":
52752                 return new Roo.EastLayoutRegion(mgr, config);
52753             case "west":
52754                 return new Roo.WestLayoutRegion(mgr, config);
52755             case "center":
52756                 return new Roo.CenterLayoutRegion(mgr, config);
52757         }
52758         throw 'Layout region "'+target+'" not supported.';
52759     }
52760 };/*
52761  * Based on:
52762  * Ext JS Library 1.1.1
52763  * Copyright(c) 2006-2007, Ext JS, LLC.
52764  *
52765  * Originally Released Under LGPL - original licence link has changed is not relivant.
52766  *
52767  * Fork - LGPL
52768  * <script type="text/javascript">
52769  */
52770  
52771 /**
52772  * @class Roo.BasicLayoutRegion
52773  * @extends Roo.util.Observable
52774  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52775  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52776  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52777  */
52778 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52779     this.mgr = mgr;
52780     this.position  = pos;
52781     this.events = {
52782         /**
52783          * @scope Roo.BasicLayoutRegion
52784          */
52785         
52786         /**
52787          * @event beforeremove
52788          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52789          * @param {Roo.LayoutRegion} this
52790          * @param {Roo.ContentPanel} panel The panel
52791          * @param {Object} e The cancel event object
52792          */
52793         "beforeremove" : true,
52794         /**
52795          * @event invalidated
52796          * Fires when the layout for this region is changed.
52797          * @param {Roo.LayoutRegion} this
52798          */
52799         "invalidated" : true,
52800         /**
52801          * @event visibilitychange
52802          * Fires when this region is shown or hidden 
52803          * @param {Roo.LayoutRegion} this
52804          * @param {Boolean} visibility true or false
52805          */
52806         "visibilitychange" : true,
52807         /**
52808          * @event paneladded
52809          * Fires when a panel is added. 
52810          * @param {Roo.LayoutRegion} this
52811          * @param {Roo.ContentPanel} panel The panel
52812          */
52813         "paneladded" : true,
52814         /**
52815          * @event panelremoved
52816          * Fires when a panel is removed. 
52817          * @param {Roo.LayoutRegion} this
52818          * @param {Roo.ContentPanel} panel The panel
52819          */
52820         "panelremoved" : true,
52821         /**
52822          * @event beforecollapse
52823          * Fires when this region before collapse.
52824          * @param {Roo.LayoutRegion} this
52825          */
52826         "beforecollapse" : true,
52827         /**
52828          * @event collapsed
52829          * Fires when this region is collapsed.
52830          * @param {Roo.LayoutRegion} this
52831          */
52832         "collapsed" : true,
52833         /**
52834          * @event expanded
52835          * Fires when this region is expanded.
52836          * @param {Roo.LayoutRegion} this
52837          */
52838         "expanded" : true,
52839         /**
52840          * @event slideshow
52841          * Fires when this region is slid into view.
52842          * @param {Roo.LayoutRegion} this
52843          */
52844         "slideshow" : true,
52845         /**
52846          * @event slidehide
52847          * Fires when this region slides out of view. 
52848          * @param {Roo.LayoutRegion} this
52849          */
52850         "slidehide" : true,
52851         /**
52852          * @event panelactivated
52853          * Fires when a panel is activated. 
52854          * @param {Roo.LayoutRegion} this
52855          * @param {Roo.ContentPanel} panel The activated panel
52856          */
52857         "panelactivated" : true,
52858         /**
52859          * @event resized
52860          * Fires when the user resizes this region. 
52861          * @param {Roo.LayoutRegion} this
52862          * @param {Number} newSize The new size (width for east/west, height for north/south)
52863          */
52864         "resized" : true
52865     };
52866     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52867     this.panels = new Roo.util.MixedCollection();
52868     this.panels.getKey = this.getPanelId.createDelegate(this);
52869     this.box = null;
52870     this.activePanel = null;
52871     // ensure listeners are added...
52872     
52873     if (config.listeners || config.events) {
52874         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52875             listeners : config.listeners || {},
52876             events : config.events || {}
52877         });
52878     }
52879     
52880     if(skipConfig !== true){
52881         this.applyConfig(config);
52882     }
52883 };
52884
52885 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52886     getPanelId : function(p){
52887         return p.getId();
52888     },
52889     
52890     applyConfig : function(config){
52891         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52892         this.config = config;
52893         
52894     },
52895     
52896     /**
52897      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52898      * the width, for horizontal (north, south) the height.
52899      * @param {Number} newSize The new width or height
52900      */
52901     resizeTo : function(newSize){
52902         var el = this.el ? this.el :
52903                  (this.activePanel ? this.activePanel.getEl() : null);
52904         if(el){
52905             switch(this.position){
52906                 case "east":
52907                 case "west":
52908                     el.setWidth(newSize);
52909                     this.fireEvent("resized", this, newSize);
52910                 break;
52911                 case "north":
52912                 case "south":
52913                     el.setHeight(newSize);
52914                     this.fireEvent("resized", this, newSize);
52915                 break;                
52916             }
52917         }
52918     },
52919     
52920     getBox : function(){
52921         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52922     },
52923     
52924     getMargins : function(){
52925         return this.margins;
52926     },
52927     
52928     updateBox : function(box){
52929         this.box = box;
52930         var el = this.activePanel.getEl();
52931         el.dom.style.left = box.x + "px";
52932         el.dom.style.top = box.y + "px";
52933         this.activePanel.setSize(box.width, box.height);
52934     },
52935     
52936     /**
52937      * Returns the container element for this region.
52938      * @return {Roo.Element}
52939      */
52940     getEl : function(){
52941         return this.activePanel;
52942     },
52943     
52944     /**
52945      * Returns true if this region is currently visible.
52946      * @return {Boolean}
52947      */
52948     isVisible : function(){
52949         return this.activePanel ? true : false;
52950     },
52951     
52952     setActivePanel : function(panel){
52953         panel = this.getPanel(panel);
52954         if(this.activePanel && this.activePanel != panel){
52955             this.activePanel.setActiveState(false);
52956             this.activePanel.getEl().setLeftTop(-10000,-10000);
52957         }
52958         this.activePanel = panel;
52959         panel.setActiveState(true);
52960         if(this.box){
52961             panel.setSize(this.box.width, this.box.height);
52962         }
52963         this.fireEvent("panelactivated", this, panel);
52964         this.fireEvent("invalidated");
52965     },
52966     
52967     /**
52968      * Show the specified panel.
52969      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52970      * @return {Roo.ContentPanel} The shown panel or null
52971      */
52972     showPanel : function(panel){
52973         if(panel = this.getPanel(panel)){
52974             this.setActivePanel(panel);
52975         }
52976         return panel;
52977     },
52978     
52979     /**
52980      * Get the active panel for this region.
52981      * @return {Roo.ContentPanel} The active panel or null
52982      */
52983     getActivePanel : function(){
52984         return this.activePanel;
52985     },
52986     
52987     /**
52988      * Add the passed ContentPanel(s)
52989      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52990      * @return {Roo.ContentPanel} The panel added (if only one was added)
52991      */
52992     add : function(panel){
52993         if(arguments.length > 1){
52994             for(var i = 0, len = arguments.length; i < len; i++) {
52995                 this.add(arguments[i]);
52996             }
52997             return null;
52998         }
52999         if(this.hasPanel(panel)){
53000             this.showPanel(panel);
53001             return panel;
53002         }
53003         var el = panel.getEl();
53004         if(el.dom.parentNode != this.mgr.el.dom){
53005             this.mgr.el.dom.appendChild(el.dom);
53006         }
53007         if(panel.setRegion){
53008             panel.setRegion(this);
53009         }
53010         this.panels.add(panel);
53011         el.setStyle("position", "absolute");
53012         if(!panel.background){
53013             this.setActivePanel(panel);
53014             if(this.config.initialSize && this.panels.getCount()==1){
53015                 this.resizeTo(this.config.initialSize);
53016             }
53017         }
53018         this.fireEvent("paneladded", this, panel);
53019         return panel;
53020     },
53021     
53022     /**
53023      * Returns true if the panel is in this region.
53024      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53025      * @return {Boolean}
53026      */
53027     hasPanel : function(panel){
53028         if(typeof panel == "object"){ // must be panel obj
53029             panel = panel.getId();
53030         }
53031         return this.getPanel(panel) ? true : false;
53032     },
53033     
53034     /**
53035      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53036      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53037      * @param {Boolean} preservePanel Overrides the config preservePanel option
53038      * @return {Roo.ContentPanel} The panel that was removed
53039      */
53040     remove : function(panel, preservePanel){
53041         panel = this.getPanel(panel);
53042         if(!panel){
53043             return null;
53044         }
53045         var e = {};
53046         this.fireEvent("beforeremove", this, panel, e);
53047         if(e.cancel === true){
53048             return null;
53049         }
53050         var panelId = panel.getId();
53051         this.panels.removeKey(panelId);
53052         return panel;
53053     },
53054     
53055     /**
53056      * Returns the panel specified or null if it's not in this region.
53057      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53058      * @return {Roo.ContentPanel}
53059      */
53060     getPanel : function(id){
53061         if(typeof id == "object"){ // must be panel obj
53062             return id;
53063         }
53064         return this.panels.get(id);
53065     },
53066     
53067     /**
53068      * Returns this regions position (north/south/east/west/center).
53069      * @return {String} 
53070      */
53071     getPosition: function(){
53072         return this.position;    
53073     }
53074 });/*
53075  * Based on:
53076  * Ext JS Library 1.1.1
53077  * Copyright(c) 2006-2007, Ext JS, LLC.
53078  *
53079  * Originally Released Under LGPL - original licence link has changed is not relivant.
53080  *
53081  * Fork - LGPL
53082  * <script type="text/javascript">
53083  */
53084  
53085 /**
53086  * @class Roo.LayoutRegion
53087  * @extends Roo.BasicLayoutRegion
53088  * This class represents a region in a layout manager.
53089  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53090  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53091  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53092  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53093  * @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})
53094  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53095  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53096  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53097  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53098  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53099  * @cfg {String}    title           The title for the region (overrides panel titles)
53100  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53101  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53102  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53103  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53104  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53105  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53106  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53107  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53108  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53109  * @cfg {Boolean}   showPin         True to show a pin button
53110  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53111  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53112  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53113  * @cfg {Number}    width           For East/West panels
53114  * @cfg {Number}    height          For North/South panels
53115  * @cfg {Boolean}   split           To show the splitter
53116  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53117  */
53118 Roo.LayoutRegion = function(mgr, config, pos){
53119     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53120     var dh = Roo.DomHelper;
53121     /** This region's container element 
53122     * @type Roo.Element */
53123     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53124     /** This region's title element 
53125     * @type Roo.Element */
53126
53127     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53128         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53129         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53130     ]}, true);
53131     this.titleEl.enableDisplayMode();
53132     /** This region's title text element 
53133     * @type HTMLElement */
53134     this.titleTextEl = this.titleEl.dom.firstChild;
53135     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53136     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53137     this.closeBtn.enableDisplayMode();
53138     this.closeBtn.on("click", this.closeClicked, this);
53139     this.closeBtn.hide();
53140
53141     this.createBody(config);
53142     this.visible = true;
53143     this.collapsed = false;
53144
53145     if(config.hideWhenEmpty){
53146         this.hide();
53147         this.on("paneladded", this.validateVisibility, this);
53148         this.on("panelremoved", this.validateVisibility, this);
53149     }
53150     this.applyConfig(config);
53151 };
53152
53153 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53154
53155     createBody : function(){
53156         /** This region's body element 
53157         * @type Roo.Element */
53158         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53159     },
53160
53161     applyConfig : function(c){
53162         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53163             var dh = Roo.DomHelper;
53164             if(c.titlebar !== false){
53165                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53166                 this.collapseBtn.on("click", this.collapse, this);
53167                 this.collapseBtn.enableDisplayMode();
53168
53169                 if(c.showPin === true || this.showPin){
53170                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53171                     this.stickBtn.enableDisplayMode();
53172                     this.stickBtn.on("click", this.expand, this);
53173                     this.stickBtn.hide();
53174                 }
53175             }
53176             /** This region's collapsed element
53177             * @type Roo.Element */
53178             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53179                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53180             ]}, true);
53181             if(c.floatable !== false){
53182                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53183                this.collapsedEl.on("click", this.collapseClick, this);
53184             }
53185
53186             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53187                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53188                    id: "message", unselectable: "on", style:{"float":"left"}});
53189                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53190              }
53191             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53192             this.expandBtn.on("click", this.expand, this);
53193         }
53194         if(this.collapseBtn){
53195             this.collapseBtn.setVisible(c.collapsible == true);
53196         }
53197         this.cmargins = c.cmargins || this.cmargins ||
53198                          (this.position == "west" || this.position == "east" ?
53199                              {top: 0, left: 2, right:2, bottom: 0} :
53200                              {top: 2, left: 0, right:0, bottom: 2});
53201         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53202         this.bottomTabs = c.tabPosition != "top";
53203         this.autoScroll = c.autoScroll || false;
53204         if(this.autoScroll){
53205             this.bodyEl.setStyle("overflow", "auto");
53206         }else{
53207             this.bodyEl.setStyle("overflow", "hidden");
53208         }
53209         //if(c.titlebar !== false){
53210             if((!c.titlebar && !c.title) || c.titlebar === false){
53211                 this.titleEl.hide();
53212             }else{
53213                 this.titleEl.show();
53214                 if(c.title){
53215                     this.titleTextEl.innerHTML = c.title;
53216                 }
53217             }
53218         //}
53219         this.duration = c.duration || .30;
53220         this.slideDuration = c.slideDuration || .45;
53221         this.config = c;
53222         if(c.collapsed){
53223             this.collapse(true);
53224         }
53225         if(c.hidden){
53226             this.hide();
53227         }
53228     },
53229     /**
53230      * Returns true if this region is currently visible.
53231      * @return {Boolean}
53232      */
53233     isVisible : function(){
53234         return this.visible;
53235     },
53236
53237     /**
53238      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53239      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53240      */
53241     setCollapsedTitle : function(title){
53242         title = title || "&#160;";
53243         if(this.collapsedTitleTextEl){
53244             this.collapsedTitleTextEl.innerHTML = title;
53245         }
53246     },
53247
53248     getBox : function(){
53249         var b;
53250         if(!this.collapsed){
53251             b = this.el.getBox(false, true);
53252         }else{
53253             b = this.collapsedEl.getBox(false, true);
53254         }
53255         return b;
53256     },
53257
53258     getMargins : function(){
53259         return this.collapsed ? this.cmargins : this.margins;
53260     },
53261
53262     highlight : function(){
53263         this.el.addClass("x-layout-panel-dragover");
53264     },
53265
53266     unhighlight : function(){
53267         this.el.removeClass("x-layout-panel-dragover");
53268     },
53269
53270     updateBox : function(box){
53271         this.box = box;
53272         if(!this.collapsed){
53273             this.el.dom.style.left = box.x + "px";
53274             this.el.dom.style.top = box.y + "px";
53275             this.updateBody(box.width, box.height);
53276         }else{
53277             this.collapsedEl.dom.style.left = box.x + "px";
53278             this.collapsedEl.dom.style.top = box.y + "px";
53279             this.collapsedEl.setSize(box.width, box.height);
53280         }
53281         if(this.tabs){
53282             this.tabs.autoSizeTabs();
53283         }
53284     },
53285
53286     updateBody : function(w, h){
53287         if(w !== null){
53288             this.el.setWidth(w);
53289             w -= this.el.getBorderWidth("rl");
53290             if(this.config.adjustments){
53291                 w += this.config.adjustments[0];
53292             }
53293         }
53294         if(h !== null){
53295             this.el.setHeight(h);
53296             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53297             h -= this.el.getBorderWidth("tb");
53298             if(this.config.adjustments){
53299                 h += this.config.adjustments[1];
53300             }
53301             this.bodyEl.setHeight(h);
53302             if(this.tabs){
53303                 h = this.tabs.syncHeight(h);
53304             }
53305         }
53306         if(this.panelSize){
53307             w = w !== null ? w : this.panelSize.width;
53308             h = h !== null ? h : this.panelSize.height;
53309         }
53310         if(this.activePanel){
53311             var el = this.activePanel.getEl();
53312             w = w !== null ? w : el.getWidth();
53313             h = h !== null ? h : el.getHeight();
53314             this.panelSize = {width: w, height: h};
53315             this.activePanel.setSize(w, h);
53316         }
53317         if(Roo.isIE && this.tabs){
53318             this.tabs.el.repaint();
53319         }
53320     },
53321
53322     /**
53323      * Returns the container element for this region.
53324      * @return {Roo.Element}
53325      */
53326     getEl : function(){
53327         return this.el;
53328     },
53329
53330     /**
53331      * Hides this region.
53332      */
53333     hide : function(){
53334         if(!this.collapsed){
53335             this.el.dom.style.left = "-2000px";
53336             this.el.hide();
53337         }else{
53338             this.collapsedEl.dom.style.left = "-2000px";
53339             this.collapsedEl.hide();
53340         }
53341         this.visible = false;
53342         this.fireEvent("visibilitychange", this, false);
53343     },
53344
53345     /**
53346      * Shows this region if it was previously hidden.
53347      */
53348     show : function(){
53349         if(!this.collapsed){
53350             this.el.show();
53351         }else{
53352             this.collapsedEl.show();
53353         }
53354         this.visible = true;
53355         this.fireEvent("visibilitychange", this, true);
53356     },
53357
53358     closeClicked : function(){
53359         if(this.activePanel){
53360             this.remove(this.activePanel);
53361         }
53362     },
53363
53364     collapseClick : function(e){
53365         if(this.isSlid){
53366            e.stopPropagation();
53367            this.slideIn();
53368         }else{
53369            e.stopPropagation();
53370            this.slideOut();
53371         }
53372     },
53373
53374     /**
53375      * Collapses this region.
53376      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53377      */
53378     collapse : function(skipAnim, skipCheck){
53379         if(this.collapsed) {
53380             return;
53381         }
53382         
53383         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53384             
53385             this.collapsed = true;
53386             if(this.split){
53387                 this.split.el.hide();
53388             }
53389             if(this.config.animate && skipAnim !== true){
53390                 this.fireEvent("invalidated", this);
53391                 this.animateCollapse();
53392             }else{
53393                 this.el.setLocation(-20000,-20000);
53394                 this.el.hide();
53395                 this.collapsedEl.show();
53396                 this.fireEvent("collapsed", this);
53397                 this.fireEvent("invalidated", this);
53398             }
53399         }
53400         
53401     },
53402
53403     animateCollapse : function(){
53404         // overridden
53405     },
53406
53407     /**
53408      * Expands this region if it was previously collapsed.
53409      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53410      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53411      */
53412     expand : function(e, skipAnim){
53413         if(e) {
53414             e.stopPropagation();
53415         }
53416         if(!this.collapsed || this.el.hasActiveFx()) {
53417             return;
53418         }
53419         if(this.isSlid){
53420             this.afterSlideIn();
53421             skipAnim = true;
53422         }
53423         this.collapsed = false;
53424         if(this.config.animate && skipAnim !== true){
53425             this.animateExpand();
53426         }else{
53427             this.el.show();
53428             if(this.split){
53429                 this.split.el.show();
53430             }
53431             this.collapsedEl.setLocation(-2000,-2000);
53432             this.collapsedEl.hide();
53433             this.fireEvent("invalidated", this);
53434             this.fireEvent("expanded", this);
53435         }
53436     },
53437
53438     animateExpand : function(){
53439         // overridden
53440     },
53441
53442     initTabs : function()
53443     {
53444         this.bodyEl.setStyle("overflow", "hidden");
53445         var ts = new Roo.TabPanel(
53446                 this.bodyEl.dom,
53447                 {
53448                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53449                     disableTooltips: this.config.disableTabTips,
53450                     toolbar : this.config.toolbar
53451                 }
53452         );
53453         if(this.config.hideTabs){
53454             ts.stripWrap.setDisplayed(false);
53455         }
53456         this.tabs = ts;
53457         ts.resizeTabs = this.config.resizeTabs === true;
53458         ts.minTabWidth = this.config.minTabWidth || 40;
53459         ts.maxTabWidth = this.config.maxTabWidth || 250;
53460         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53461         ts.monitorResize = false;
53462         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53463         ts.bodyEl.addClass('x-layout-tabs-body');
53464         this.panels.each(this.initPanelAsTab, this);
53465     },
53466
53467     initPanelAsTab : function(panel){
53468         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53469                     this.config.closeOnTab && panel.isClosable());
53470         if(panel.tabTip !== undefined){
53471             ti.setTooltip(panel.tabTip);
53472         }
53473         ti.on("activate", function(){
53474               this.setActivePanel(panel);
53475         }, this);
53476         if(this.config.closeOnTab){
53477             ti.on("beforeclose", function(t, e){
53478                 e.cancel = true;
53479                 this.remove(panel);
53480             }, this);
53481         }
53482         return ti;
53483     },
53484
53485     updatePanelTitle : function(panel, title){
53486         if(this.activePanel == panel){
53487             this.updateTitle(title);
53488         }
53489         if(this.tabs){
53490             var ti = this.tabs.getTab(panel.getEl().id);
53491             ti.setText(title);
53492             if(panel.tabTip !== undefined){
53493                 ti.setTooltip(panel.tabTip);
53494             }
53495         }
53496     },
53497
53498     updateTitle : function(title){
53499         if(this.titleTextEl && !this.config.title){
53500             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53501         }
53502     },
53503
53504     setActivePanel : function(panel){
53505         panel = this.getPanel(panel);
53506         if(this.activePanel && this.activePanel != panel){
53507             this.activePanel.setActiveState(false);
53508         }
53509         this.activePanel = panel;
53510         panel.setActiveState(true);
53511         if(this.panelSize){
53512             panel.setSize(this.panelSize.width, this.panelSize.height);
53513         }
53514         if(this.closeBtn){
53515             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53516         }
53517         this.updateTitle(panel.getTitle());
53518         if(this.tabs){
53519             this.fireEvent("invalidated", this);
53520         }
53521         this.fireEvent("panelactivated", this, panel);
53522     },
53523
53524     /**
53525      * Shows the specified panel.
53526      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53527      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53528      */
53529     showPanel : function(panel)
53530     {
53531         panel = this.getPanel(panel);
53532         if(panel){
53533             if(this.tabs){
53534                 var tab = this.tabs.getTab(panel.getEl().id);
53535                 if(tab.isHidden()){
53536                     this.tabs.unhideTab(tab.id);
53537                 }
53538                 tab.activate();
53539             }else{
53540                 this.setActivePanel(panel);
53541             }
53542         }
53543         return panel;
53544     },
53545
53546     /**
53547      * Get the active panel for this region.
53548      * @return {Roo.ContentPanel} The active panel or null
53549      */
53550     getActivePanel : function(){
53551         return this.activePanel;
53552     },
53553
53554     validateVisibility : function(){
53555         if(this.panels.getCount() < 1){
53556             this.updateTitle("&#160;");
53557             this.closeBtn.hide();
53558             this.hide();
53559         }else{
53560             if(!this.isVisible()){
53561                 this.show();
53562             }
53563         }
53564     },
53565
53566     /**
53567      * Adds the passed ContentPanel(s) to this region.
53568      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53569      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53570      */
53571     add : function(panel){
53572         if(arguments.length > 1){
53573             for(var i = 0, len = arguments.length; i < len; i++) {
53574                 this.add(arguments[i]);
53575             }
53576             return null;
53577         }
53578         if(this.hasPanel(panel)){
53579             this.showPanel(panel);
53580             return panel;
53581         }
53582         panel.setRegion(this);
53583         this.panels.add(panel);
53584         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53585             this.bodyEl.dom.appendChild(panel.getEl().dom);
53586             if(panel.background !== true){
53587                 this.setActivePanel(panel);
53588             }
53589             this.fireEvent("paneladded", this, panel);
53590             return panel;
53591         }
53592         if(!this.tabs){
53593             this.initTabs();
53594         }else{
53595             this.initPanelAsTab(panel);
53596         }
53597         if(panel.background !== true){
53598             this.tabs.activate(panel.getEl().id);
53599         }
53600         this.fireEvent("paneladded", this, panel);
53601         return panel;
53602     },
53603
53604     /**
53605      * Hides the tab for the specified panel.
53606      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53607      */
53608     hidePanel : function(panel){
53609         if(this.tabs && (panel = this.getPanel(panel))){
53610             this.tabs.hideTab(panel.getEl().id);
53611         }
53612     },
53613
53614     /**
53615      * Unhides the tab for a previously hidden panel.
53616      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53617      */
53618     unhidePanel : function(panel){
53619         if(this.tabs && (panel = this.getPanel(panel))){
53620             this.tabs.unhideTab(panel.getEl().id);
53621         }
53622     },
53623
53624     clearPanels : function(){
53625         while(this.panels.getCount() > 0){
53626              this.remove(this.panels.first());
53627         }
53628     },
53629
53630     /**
53631      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53632      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53633      * @param {Boolean} preservePanel Overrides the config preservePanel option
53634      * @return {Roo.ContentPanel} The panel that was removed
53635      */
53636     remove : function(panel, preservePanel){
53637         panel = this.getPanel(panel);
53638         if(!panel){
53639             return null;
53640         }
53641         var e = {};
53642         this.fireEvent("beforeremove", this, panel, e);
53643         if(e.cancel === true){
53644             return null;
53645         }
53646         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53647         var panelId = panel.getId();
53648         this.panels.removeKey(panelId);
53649         if(preservePanel){
53650             document.body.appendChild(panel.getEl().dom);
53651         }
53652         if(this.tabs){
53653             this.tabs.removeTab(panel.getEl().id);
53654         }else if (!preservePanel){
53655             this.bodyEl.dom.removeChild(panel.getEl().dom);
53656         }
53657         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53658             var p = this.panels.first();
53659             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53660             tempEl.appendChild(p.getEl().dom);
53661             this.bodyEl.update("");
53662             this.bodyEl.dom.appendChild(p.getEl().dom);
53663             tempEl = null;
53664             this.updateTitle(p.getTitle());
53665             this.tabs = null;
53666             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53667             this.setActivePanel(p);
53668         }
53669         panel.setRegion(null);
53670         if(this.activePanel == panel){
53671             this.activePanel = null;
53672         }
53673         if(this.config.autoDestroy !== false && preservePanel !== true){
53674             try{panel.destroy();}catch(e){}
53675         }
53676         this.fireEvent("panelremoved", this, panel);
53677         return panel;
53678     },
53679
53680     /**
53681      * Returns the TabPanel component used by this region
53682      * @return {Roo.TabPanel}
53683      */
53684     getTabs : function(){
53685         return this.tabs;
53686     },
53687
53688     createTool : function(parentEl, className){
53689         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53690             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53691         btn.addClassOnOver("x-layout-tools-button-over");
53692         return btn;
53693     }
53694 });/*
53695  * Based on:
53696  * Ext JS Library 1.1.1
53697  * Copyright(c) 2006-2007, Ext JS, LLC.
53698  *
53699  * Originally Released Under LGPL - original licence link has changed is not relivant.
53700  *
53701  * Fork - LGPL
53702  * <script type="text/javascript">
53703  */
53704  
53705
53706
53707 /**
53708  * @class Roo.SplitLayoutRegion
53709  * @extends Roo.LayoutRegion
53710  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53711  */
53712 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53713     this.cursor = cursor;
53714     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53715 };
53716
53717 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53718     splitTip : "Drag to resize.",
53719     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53720     useSplitTips : false,
53721
53722     applyConfig : function(config){
53723         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53724         if(config.split){
53725             if(!this.split){
53726                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53727                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53728                 /** The SplitBar for this region 
53729                 * @type Roo.SplitBar */
53730                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53731                 this.split.on("moved", this.onSplitMove, this);
53732                 this.split.useShim = config.useShim === true;
53733                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53734                 if(this.useSplitTips){
53735                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53736                 }
53737                 if(config.collapsible){
53738                     this.split.el.on("dblclick", this.collapse,  this);
53739                 }
53740             }
53741             if(typeof config.minSize != "undefined"){
53742                 this.split.minSize = config.minSize;
53743             }
53744             if(typeof config.maxSize != "undefined"){
53745                 this.split.maxSize = config.maxSize;
53746             }
53747             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53748                 this.hideSplitter();
53749             }
53750         }
53751     },
53752
53753     getHMaxSize : function(){
53754          var cmax = this.config.maxSize || 10000;
53755          var center = this.mgr.getRegion("center");
53756          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53757     },
53758
53759     getVMaxSize : function(){
53760          var cmax = this.config.maxSize || 10000;
53761          var center = this.mgr.getRegion("center");
53762          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53763     },
53764
53765     onSplitMove : function(split, newSize){
53766         this.fireEvent("resized", this, newSize);
53767     },
53768     
53769     /** 
53770      * Returns the {@link Roo.SplitBar} for this region.
53771      * @return {Roo.SplitBar}
53772      */
53773     getSplitBar : function(){
53774         return this.split;
53775     },
53776     
53777     hide : function(){
53778         this.hideSplitter();
53779         Roo.SplitLayoutRegion.superclass.hide.call(this);
53780     },
53781
53782     hideSplitter : function(){
53783         if(this.split){
53784             this.split.el.setLocation(-2000,-2000);
53785             this.split.el.hide();
53786         }
53787     },
53788
53789     show : function(){
53790         if(this.split){
53791             this.split.el.show();
53792         }
53793         Roo.SplitLayoutRegion.superclass.show.call(this);
53794     },
53795     
53796     beforeSlide: function(){
53797         if(Roo.isGecko){// firefox overflow auto bug workaround
53798             this.bodyEl.clip();
53799             if(this.tabs) {
53800                 this.tabs.bodyEl.clip();
53801             }
53802             if(this.activePanel){
53803                 this.activePanel.getEl().clip();
53804                 
53805                 if(this.activePanel.beforeSlide){
53806                     this.activePanel.beforeSlide();
53807                 }
53808             }
53809         }
53810     },
53811     
53812     afterSlide : function(){
53813         if(Roo.isGecko){// firefox overflow auto bug workaround
53814             this.bodyEl.unclip();
53815             if(this.tabs) {
53816                 this.tabs.bodyEl.unclip();
53817             }
53818             if(this.activePanel){
53819                 this.activePanel.getEl().unclip();
53820                 if(this.activePanel.afterSlide){
53821                     this.activePanel.afterSlide();
53822                 }
53823             }
53824         }
53825     },
53826
53827     initAutoHide : function(){
53828         if(this.autoHide !== false){
53829             if(!this.autoHideHd){
53830                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53831                 this.autoHideHd = {
53832                     "mouseout": function(e){
53833                         if(!e.within(this.el, true)){
53834                             st.delay(500);
53835                         }
53836                     },
53837                     "mouseover" : function(e){
53838                         st.cancel();
53839                     },
53840                     scope : this
53841                 };
53842             }
53843             this.el.on(this.autoHideHd);
53844         }
53845     },
53846
53847     clearAutoHide : function(){
53848         if(this.autoHide !== false){
53849             this.el.un("mouseout", this.autoHideHd.mouseout);
53850             this.el.un("mouseover", this.autoHideHd.mouseover);
53851         }
53852     },
53853
53854     clearMonitor : function(){
53855         Roo.get(document).un("click", this.slideInIf, this);
53856     },
53857
53858     // these names are backwards but not changed for compat
53859     slideOut : function(){
53860         if(this.isSlid || this.el.hasActiveFx()){
53861             return;
53862         }
53863         this.isSlid = true;
53864         if(this.collapseBtn){
53865             this.collapseBtn.hide();
53866         }
53867         this.closeBtnState = this.closeBtn.getStyle('display');
53868         this.closeBtn.hide();
53869         if(this.stickBtn){
53870             this.stickBtn.show();
53871         }
53872         this.el.show();
53873         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53874         this.beforeSlide();
53875         this.el.setStyle("z-index", 10001);
53876         this.el.slideIn(this.getSlideAnchor(), {
53877             callback: function(){
53878                 this.afterSlide();
53879                 this.initAutoHide();
53880                 Roo.get(document).on("click", this.slideInIf, this);
53881                 this.fireEvent("slideshow", this);
53882             },
53883             scope: this,
53884             block: true
53885         });
53886     },
53887
53888     afterSlideIn : function(){
53889         this.clearAutoHide();
53890         this.isSlid = false;
53891         this.clearMonitor();
53892         this.el.setStyle("z-index", "");
53893         if(this.collapseBtn){
53894             this.collapseBtn.show();
53895         }
53896         this.closeBtn.setStyle('display', this.closeBtnState);
53897         if(this.stickBtn){
53898             this.stickBtn.hide();
53899         }
53900         this.fireEvent("slidehide", this);
53901     },
53902
53903     slideIn : function(cb){
53904         if(!this.isSlid || this.el.hasActiveFx()){
53905             Roo.callback(cb);
53906             return;
53907         }
53908         this.isSlid = false;
53909         this.beforeSlide();
53910         this.el.slideOut(this.getSlideAnchor(), {
53911             callback: function(){
53912                 this.el.setLeftTop(-10000, -10000);
53913                 this.afterSlide();
53914                 this.afterSlideIn();
53915                 Roo.callback(cb);
53916             },
53917             scope: this,
53918             block: true
53919         });
53920     },
53921     
53922     slideInIf : function(e){
53923         if(!e.within(this.el)){
53924             this.slideIn();
53925         }
53926     },
53927
53928     animateCollapse : function(){
53929         this.beforeSlide();
53930         this.el.setStyle("z-index", 20000);
53931         var anchor = this.getSlideAnchor();
53932         this.el.slideOut(anchor, {
53933             callback : function(){
53934                 this.el.setStyle("z-index", "");
53935                 this.collapsedEl.slideIn(anchor, {duration:.3});
53936                 this.afterSlide();
53937                 this.el.setLocation(-10000,-10000);
53938                 this.el.hide();
53939                 this.fireEvent("collapsed", this);
53940             },
53941             scope: this,
53942             block: true
53943         });
53944     },
53945
53946     animateExpand : function(){
53947         this.beforeSlide();
53948         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53949         this.el.setStyle("z-index", 20000);
53950         this.collapsedEl.hide({
53951             duration:.1
53952         });
53953         this.el.slideIn(this.getSlideAnchor(), {
53954             callback : function(){
53955                 this.el.setStyle("z-index", "");
53956                 this.afterSlide();
53957                 if(this.split){
53958                     this.split.el.show();
53959                 }
53960                 this.fireEvent("invalidated", this);
53961                 this.fireEvent("expanded", this);
53962             },
53963             scope: this,
53964             block: true
53965         });
53966     },
53967
53968     anchors : {
53969         "west" : "left",
53970         "east" : "right",
53971         "north" : "top",
53972         "south" : "bottom"
53973     },
53974
53975     sanchors : {
53976         "west" : "l",
53977         "east" : "r",
53978         "north" : "t",
53979         "south" : "b"
53980     },
53981
53982     canchors : {
53983         "west" : "tl-tr",
53984         "east" : "tr-tl",
53985         "north" : "tl-bl",
53986         "south" : "bl-tl"
53987     },
53988
53989     getAnchor : function(){
53990         return this.anchors[this.position];
53991     },
53992
53993     getCollapseAnchor : function(){
53994         return this.canchors[this.position];
53995     },
53996
53997     getSlideAnchor : function(){
53998         return this.sanchors[this.position];
53999     },
54000
54001     getAlignAdj : function(){
54002         var cm = this.cmargins;
54003         switch(this.position){
54004             case "west":
54005                 return [0, 0];
54006             break;
54007             case "east":
54008                 return [0, 0];
54009             break;
54010             case "north":
54011                 return [0, 0];
54012             break;
54013             case "south":
54014                 return [0, 0];
54015             break;
54016         }
54017     },
54018
54019     getExpandAdj : function(){
54020         var c = this.collapsedEl, cm = this.cmargins;
54021         switch(this.position){
54022             case "west":
54023                 return [-(cm.right+c.getWidth()+cm.left), 0];
54024             break;
54025             case "east":
54026                 return [cm.right+c.getWidth()+cm.left, 0];
54027             break;
54028             case "north":
54029                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54030             break;
54031             case "south":
54032                 return [0, cm.top+cm.bottom+c.getHeight()];
54033             break;
54034         }
54035     }
54036 });/*
54037  * Based on:
54038  * Ext JS Library 1.1.1
54039  * Copyright(c) 2006-2007, Ext JS, LLC.
54040  *
54041  * Originally Released Under LGPL - original licence link has changed is not relivant.
54042  *
54043  * Fork - LGPL
54044  * <script type="text/javascript">
54045  */
54046 /*
54047  * These classes are private internal classes
54048  */
54049 Roo.CenterLayoutRegion = function(mgr, config){
54050     Roo.LayoutRegion.call(this, mgr, config, "center");
54051     this.visible = true;
54052     this.minWidth = config.minWidth || 20;
54053     this.minHeight = config.minHeight || 20;
54054 };
54055
54056 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54057     hide : function(){
54058         // center panel can't be hidden
54059     },
54060     
54061     show : function(){
54062         // center panel can't be hidden
54063     },
54064     
54065     getMinWidth: function(){
54066         return this.minWidth;
54067     },
54068     
54069     getMinHeight: function(){
54070         return this.minHeight;
54071     }
54072 });
54073
54074
54075 Roo.NorthLayoutRegion = function(mgr, config){
54076     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54077     if(this.split){
54078         this.split.placement = Roo.SplitBar.TOP;
54079         this.split.orientation = Roo.SplitBar.VERTICAL;
54080         this.split.el.addClass("x-layout-split-v");
54081     }
54082     var size = config.initialSize || config.height;
54083     if(typeof size != "undefined"){
54084         this.el.setHeight(size);
54085     }
54086 };
54087 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54088     orientation: Roo.SplitBar.VERTICAL,
54089     getBox : function(){
54090         if(this.collapsed){
54091             return this.collapsedEl.getBox();
54092         }
54093         var box = this.el.getBox();
54094         if(this.split){
54095             box.height += this.split.el.getHeight();
54096         }
54097         return box;
54098     },
54099     
54100     updateBox : function(box){
54101         if(this.split && !this.collapsed){
54102             box.height -= this.split.el.getHeight();
54103             this.split.el.setLeft(box.x);
54104             this.split.el.setTop(box.y+box.height);
54105             this.split.el.setWidth(box.width);
54106         }
54107         if(this.collapsed){
54108             this.updateBody(box.width, null);
54109         }
54110         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54111     }
54112 });
54113
54114 Roo.SouthLayoutRegion = function(mgr, config){
54115     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54116     if(this.split){
54117         this.split.placement = Roo.SplitBar.BOTTOM;
54118         this.split.orientation = Roo.SplitBar.VERTICAL;
54119         this.split.el.addClass("x-layout-split-v");
54120     }
54121     var size = config.initialSize || config.height;
54122     if(typeof size != "undefined"){
54123         this.el.setHeight(size);
54124     }
54125 };
54126 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54127     orientation: Roo.SplitBar.VERTICAL,
54128     getBox : function(){
54129         if(this.collapsed){
54130             return this.collapsedEl.getBox();
54131         }
54132         var box = this.el.getBox();
54133         if(this.split){
54134             var sh = this.split.el.getHeight();
54135             box.height += sh;
54136             box.y -= sh;
54137         }
54138         return box;
54139     },
54140     
54141     updateBox : function(box){
54142         if(this.split && !this.collapsed){
54143             var sh = this.split.el.getHeight();
54144             box.height -= sh;
54145             box.y += sh;
54146             this.split.el.setLeft(box.x);
54147             this.split.el.setTop(box.y-sh);
54148             this.split.el.setWidth(box.width);
54149         }
54150         if(this.collapsed){
54151             this.updateBody(box.width, null);
54152         }
54153         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54154     }
54155 });
54156
54157 Roo.EastLayoutRegion = function(mgr, config){
54158     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54159     if(this.split){
54160         this.split.placement = Roo.SplitBar.RIGHT;
54161         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54162         this.split.el.addClass("x-layout-split-h");
54163     }
54164     var size = config.initialSize || config.width;
54165     if(typeof size != "undefined"){
54166         this.el.setWidth(size);
54167     }
54168 };
54169 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54170     orientation: Roo.SplitBar.HORIZONTAL,
54171     getBox : function(){
54172         if(this.collapsed){
54173             return this.collapsedEl.getBox();
54174         }
54175         var box = this.el.getBox();
54176         if(this.split){
54177             var sw = this.split.el.getWidth();
54178             box.width += sw;
54179             box.x -= sw;
54180         }
54181         return box;
54182     },
54183
54184     updateBox : function(box){
54185         if(this.split && !this.collapsed){
54186             var sw = this.split.el.getWidth();
54187             box.width -= sw;
54188             this.split.el.setLeft(box.x);
54189             this.split.el.setTop(box.y);
54190             this.split.el.setHeight(box.height);
54191             box.x += sw;
54192         }
54193         if(this.collapsed){
54194             this.updateBody(null, box.height);
54195         }
54196         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54197     }
54198 });
54199
54200 Roo.WestLayoutRegion = function(mgr, config){
54201     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54202     if(this.split){
54203         this.split.placement = Roo.SplitBar.LEFT;
54204         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54205         this.split.el.addClass("x-layout-split-h");
54206     }
54207     var size = config.initialSize || config.width;
54208     if(typeof size != "undefined"){
54209         this.el.setWidth(size);
54210     }
54211 };
54212 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54213     orientation: Roo.SplitBar.HORIZONTAL,
54214     getBox : function(){
54215         if(this.collapsed){
54216             return this.collapsedEl.getBox();
54217         }
54218         var box = this.el.getBox();
54219         if(this.split){
54220             box.width += this.split.el.getWidth();
54221         }
54222         return box;
54223     },
54224     
54225     updateBox : function(box){
54226         if(this.split && !this.collapsed){
54227             var sw = this.split.el.getWidth();
54228             box.width -= sw;
54229             this.split.el.setLeft(box.x+box.width);
54230             this.split.el.setTop(box.y);
54231             this.split.el.setHeight(box.height);
54232         }
54233         if(this.collapsed){
54234             this.updateBody(null, box.height);
54235         }
54236         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54237     }
54238 });
54239 /*
54240  * Based on:
54241  * Ext JS Library 1.1.1
54242  * Copyright(c) 2006-2007, Ext JS, LLC.
54243  *
54244  * Originally Released Under LGPL - original licence link has changed is not relivant.
54245  *
54246  * Fork - LGPL
54247  * <script type="text/javascript">
54248  */
54249  
54250  
54251 /*
54252  * Private internal class for reading and applying state
54253  */
54254 Roo.LayoutStateManager = function(layout){
54255      // default empty state
54256      this.state = {
54257         north: {},
54258         south: {},
54259         east: {},
54260         west: {}       
54261     };
54262 };
54263
54264 Roo.LayoutStateManager.prototype = {
54265     init : function(layout, provider){
54266         this.provider = provider;
54267         var state = provider.get(layout.id+"-layout-state");
54268         if(state){
54269             var wasUpdating = layout.isUpdating();
54270             if(!wasUpdating){
54271                 layout.beginUpdate();
54272             }
54273             for(var key in state){
54274                 if(typeof state[key] != "function"){
54275                     var rstate = state[key];
54276                     var r = layout.getRegion(key);
54277                     if(r && rstate){
54278                         if(rstate.size){
54279                             r.resizeTo(rstate.size);
54280                         }
54281                         if(rstate.collapsed == true){
54282                             r.collapse(true);
54283                         }else{
54284                             r.expand(null, true);
54285                         }
54286                     }
54287                 }
54288             }
54289             if(!wasUpdating){
54290                 layout.endUpdate();
54291             }
54292             this.state = state; 
54293         }
54294         this.layout = layout;
54295         layout.on("regionresized", this.onRegionResized, this);
54296         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54297         layout.on("regionexpanded", this.onRegionExpanded, this);
54298     },
54299     
54300     storeState : function(){
54301         this.provider.set(this.layout.id+"-layout-state", this.state);
54302     },
54303     
54304     onRegionResized : function(region, newSize){
54305         this.state[region.getPosition()].size = newSize;
54306         this.storeState();
54307     },
54308     
54309     onRegionCollapsed : function(region){
54310         this.state[region.getPosition()].collapsed = true;
54311         this.storeState();
54312     },
54313     
54314     onRegionExpanded : function(region){
54315         this.state[region.getPosition()].collapsed = false;
54316         this.storeState();
54317     }
54318 };/*
54319  * Based on:
54320  * Ext JS Library 1.1.1
54321  * Copyright(c) 2006-2007, Ext JS, LLC.
54322  *
54323  * Originally Released Under LGPL - original licence link has changed is not relivant.
54324  *
54325  * Fork - LGPL
54326  * <script type="text/javascript">
54327  */
54328 /**
54329  * @class Roo.ContentPanel
54330  * @extends Roo.util.Observable
54331  * A basic ContentPanel element.
54332  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54333  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54334  * @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
54335  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54336  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54337  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54338  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54339  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54340  * @cfg {String} title          The title for this panel
54341  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54342  * @cfg {String} url            Calls {@link #setUrl} with this value
54343  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54344  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54345  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54346  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54347  * @cfg {String}    style  Extra style to add to the content panel 
54348
54349  * @constructor
54350  * Create a new ContentPanel.
54351  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54352  * @param {String/Object} config A string to set only the title or a config object
54353  * @param {String} content (optional) Set the HTML content for this panel
54354  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54355  */
54356 Roo.ContentPanel = function(el, config, content){
54357     
54358      
54359     /*
54360     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54361         config = el;
54362         el = Roo.id();
54363     }
54364     if (config && config.parentLayout) { 
54365         el = config.parentLayout.el.createChild(); 
54366     }
54367     */
54368     if(el.autoCreate){ // xtype is available if this is called from factory
54369         config = el;
54370         el = Roo.id();
54371     }
54372     this.el = Roo.get(el);
54373     if(!this.el && config && config.autoCreate){
54374         if(typeof config.autoCreate == "object"){
54375             if(!config.autoCreate.id){
54376                 config.autoCreate.id = config.id||el;
54377             }
54378             this.el = Roo.DomHelper.append(document.body,
54379                         config.autoCreate, true);
54380         }else{
54381             this.el = Roo.DomHelper.append(document.body,
54382                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54383         }
54384     }
54385     
54386     
54387     this.closable = false;
54388     this.loaded = false;
54389     this.active = false;
54390     if(typeof config == "string"){
54391         this.title = config;
54392     }else{
54393         Roo.apply(this, config);
54394     }
54395     
54396     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54397         this.wrapEl = this.el.wrap();
54398         this.toolbar.container = this.el.insertSibling(false, 'before');
54399         this.toolbar = new Roo.Toolbar(this.toolbar);
54400     }
54401     
54402     // xtype created footer. - not sure if will work as we normally have to render first..
54403     if (this.footer && !this.footer.el && this.footer.xtype) {
54404         if (!this.wrapEl) {
54405             this.wrapEl = this.el.wrap();
54406         }
54407     
54408         this.footer.container = this.wrapEl.createChild();
54409          
54410         this.footer = Roo.factory(this.footer, Roo);
54411         
54412     }
54413     
54414     if(this.resizeEl){
54415         this.resizeEl = Roo.get(this.resizeEl, true);
54416     }else{
54417         this.resizeEl = this.el;
54418     }
54419     // handle view.xtype
54420     
54421  
54422     
54423     
54424     this.addEvents({
54425         /**
54426          * @event activate
54427          * Fires when this panel is activated. 
54428          * @param {Roo.ContentPanel} this
54429          */
54430         "activate" : true,
54431         /**
54432          * @event deactivate
54433          * Fires when this panel is activated. 
54434          * @param {Roo.ContentPanel} this
54435          */
54436         "deactivate" : true,
54437
54438         /**
54439          * @event resize
54440          * Fires when this panel is resized if fitToFrame is true.
54441          * @param {Roo.ContentPanel} this
54442          * @param {Number} width The width after any component adjustments
54443          * @param {Number} height The height after any component adjustments
54444          */
54445         "resize" : true,
54446         
54447          /**
54448          * @event render
54449          * Fires when this tab is created
54450          * @param {Roo.ContentPanel} this
54451          */
54452         "render" : true
54453          
54454         
54455     });
54456     
54457
54458     
54459     
54460     if(this.autoScroll){
54461         this.resizeEl.setStyle("overflow", "auto");
54462     } else {
54463         // fix randome scrolling
54464         this.el.on('scroll', function() {
54465             Roo.log('fix random scolling');
54466             this.scrollTo('top',0); 
54467         });
54468     }
54469     content = content || this.content;
54470     if(content){
54471         this.setContent(content);
54472     }
54473     if(config && config.url){
54474         this.setUrl(this.url, this.params, this.loadOnce);
54475     }
54476     
54477     
54478     
54479     Roo.ContentPanel.superclass.constructor.call(this);
54480     
54481     if (this.view && typeof(this.view.xtype) != 'undefined') {
54482         this.view.el = this.el.appendChild(document.createElement("div"));
54483         this.view = Roo.factory(this.view); 
54484         this.view.render  &&  this.view.render(false, '');  
54485     }
54486     
54487     
54488     this.fireEvent('render', this);
54489 };
54490
54491 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54492     tabTip:'',
54493     setRegion : function(region){
54494         this.region = region;
54495         if(region){
54496            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54497         }else{
54498            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54499         } 
54500     },
54501     
54502     /**
54503      * Returns the toolbar for this Panel if one was configured. 
54504      * @return {Roo.Toolbar} 
54505      */
54506     getToolbar : function(){
54507         return this.toolbar;
54508     },
54509     
54510     setActiveState : function(active){
54511         this.active = active;
54512         if(!active){
54513             this.fireEvent("deactivate", this);
54514         }else{
54515             this.fireEvent("activate", this);
54516         }
54517     },
54518     /**
54519      * Updates this panel's element
54520      * @param {String} content The new content
54521      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54522     */
54523     setContent : function(content, loadScripts){
54524         this.el.update(content, loadScripts);
54525     },
54526
54527     ignoreResize : function(w, h){
54528         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54529             return true;
54530         }else{
54531             this.lastSize = {width: w, height: h};
54532             return false;
54533         }
54534     },
54535     /**
54536      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54537      * @return {Roo.UpdateManager} The UpdateManager
54538      */
54539     getUpdateManager : function(){
54540         return this.el.getUpdateManager();
54541     },
54542      /**
54543      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54544      * @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:
54545 <pre><code>
54546 panel.load({
54547     url: "your-url.php",
54548     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54549     callback: yourFunction,
54550     scope: yourObject, //(optional scope)
54551     discardUrl: false,
54552     nocache: false,
54553     text: "Loading...",
54554     timeout: 30,
54555     scripts: false
54556 });
54557 </code></pre>
54558      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54559      * 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.
54560      * @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}
54561      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54562      * @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.
54563      * @return {Roo.ContentPanel} this
54564      */
54565     load : function(){
54566         var um = this.el.getUpdateManager();
54567         um.update.apply(um, arguments);
54568         return this;
54569     },
54570
54571
54572     /**
54573      * 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.
54574      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54575      * @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)
54576      * @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)
54577      * @return {Roo.UpdateManager} The UpdateManager
54578      */
54579     setUrl : function(url, params, loadOnce){
54580         if(this.refreshDelegate){
54581             this.removeListener("activate", this.refreshDelegate);
54582         }
54583         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54584         this.on("activate", this.refreshDelegate);
54585         return this.el.getUpdateManager();
54586     },
54587     
54588     _handleRefresh : function(url, params, loadOnce){
54589         if(!loadOnce || !this.loaded){
54590             var updater = this.el.getUpdateManager();
54591             updater.update(url, params, this._setLoaded.createDelegate(this));
54592         }
54593     },
54594     
54595     _setLoaded : function(){
54596         this.loaded = true;
54597     }, 
54598     
54599     /**
54600      * Returns this panel's id
54601      * @return {String} 
54602      */
54603     getId : function(){
54604         return this.el.id;
54605     },
54606     
54607     /** 
54608      * Returns this panel's element - used by regiosn to add.
54609      * @return {Roo.Element} 
54610      */
54611     getEl : function(){
54612         return this.wrapEl || this.el;
54613     },
54614     
54615     adjustForComponents : function(width, height)
54616     {
54617         //Roo.log('adjustForComponents ');
54618         if(this.resizeEl != this.el){
54619             width -= this.el.getFrameWidth('lr');
54620             height -= this.el.getFrameWidth('tb');
54621         }
54622         if(this.toolbar){
54623             var te = this.toolbar.getEl();
54624             height -= te.getHeight();
54625             te.setWidth(width);
54626         }
54627         if(this.footer){
54628             var te = this.footer.getEl();
54629             //Roo.log("footer:" + te.getHeight());
54630             
54631             height -= te.getHeight();
54632             te.setWidth(width);
54633         }
54634         
54635         
54636         if(this.adjustments){
54637             width += this.adjustments[0];
54638             height += this.adjustments[1];
54639         }
54640         return {"width": width, "height": height};
54641     },
54642     
54643     setSize : function(width, height){
54644         if(this.fitToFrame && !this.ignoreResize(width, height)){
54645             if(this.fitContainer && this.resizeEl != this.el){
54646                 this.el.setSize(width, height);
54647             }
54648             var size = this.adjustForComponents(width, height);
54649             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54650             this.fireEvent('resize', this, size.width, size.height);
54651         }
54652     },
54653     
54654     /**
54655      * Returns this panel's title
54656      * @return {String} 
54657      */
54658     getTitle : function(){
54659         return this.title;
54660     },
54661     
54662     /**
54663      * Set this panel's title
54664      * @param {String} title
54665      */
54666     setTitle : function(title){
54667         this.title = title;
54668         if(this.region){
54669             this.region.updatePanelTitle(this, title);
54670         }
54671     },
54672     
54673     /**
54674      * Returns true is this panel was configured to be closable
54675      * @return {Boolean} 
54676      */
54677     isClosable : function(){
54678         return this.closable;
54679     },
54680     
54681     beforeSlide : function(){
54682         this.el.clip();
54683         this.resizeEl.clip();
54684     },
54685     
54686     afterSlide : function(){
54687         this.el.unclip();
54688         this.resizeEl.unclip();
54689     },
54690     
54691     /**
54692      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54693      *   Will fail silently if the {@link #setUrl} method has not been called.
54694      *   This does not activate the panel, just updates its content.
54695      */
54696     refresh : function(){
54697         if(this.refreshDelegate){
54698            this.loaded = false;
54699            this.refreshDelegate();
54700         }
54701     },
54702     
54703     /**
54704      * Destroys this panel
54705      */
54706     destroy : function(){
54707         this.el.removeAllListeners();
54708         var tempEl = document.createElement("span");
54709         tempEl.appendChild(this.el.dom);
54710         tempEl.innerHTML = "";
54711         this.el.remove();
54712         this.el = null;
54713     },
54714     
54715     /**
54716      * form - if the content panel contains a form - this is a reference to it.
54717      * @type {Roo.form.Form}
54718      */
54719     form : false,
54720     /**
54721      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54722      *    This contains a reference to it.
54723      * @type {Roo.View}
54724      */
54725     view : false,
54726     
54727       /**
54728      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54729      * <pre><code>
54730
54731 layout.addxtype({
54732        xtype : 'Form',
54733        items: [ .... ]
54734    }
54735 );
54736
54737 </code></pre>
54738      * @param {Object} cfg Xtype definition of item to add.
54739      */
54740     
54741     addxtype : function(cfg) {
54742         // add form..
54743         if (cfg.xtype.match(/^Form$/)) {
54744             
54745             var el;
54746             //if (this.footer) {
54747             //    el = this.footer.container.insertSibling(false, 'before');
54748             //} else {
54749                 el = this.el.createChild();
54750             //}
54751
54752             this.form = new  Roo.form.Form(cfg);
54753             
54754             
54755             if ( this.form.allItems.length) {
54756                 this.form.render(el.dom);
54757             }
54758             return this.form;
54759         }
54760         // should only have one of theses..
54761         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54762             // views.. should not be just added - used named prop 'view''
54763             
54764             cfg.el = this.el.appendChild(document.createElement("div"));
54765             // factory?
54766             
54767             var ret = new Roo.factory(cfg);
54768              
54769              ret.render && ret.render(false, ''); // render blank..
54770             this.view = ret;
54771             return ret;
54772         }
54773         return false;
54774     }
54775 });
54776
54777 /**
54778  * @class Roo.GridPanel
54779  * @extends Roo.ContentPanel
54780  * @constructor
54781  * Create a new GridPanel.
54782  * @param {Roo.grid.Grid} grid The grid for this panel
54783  * @param {String/Object} config A string to set only the panel's title, or a config object
54784  */
54785 Roo.GridPanel = function(grid, config){
54786     
54787   
54788     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54789         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54790         
54791     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54792     
54793     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54794     
54795     if(this.toolbar){
54796         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54797     }
54798     // xtype created footer. - not sure if will work as we normally have to render first..
54799     if (this.footer && !this.footer.el && this.footer.xtype) {
54800         
54801         this.footer.container = this.grid.getView().getFooterPanel(true);
54802         this.footer.dataSource = this.grid.dataSource;
54803         this.footer = Roo.factory(this.footer, Roo);
54804         
54805     }
54806     
54807     grid.monitorWindowResize = false; // turn off autosizing
54808     grid.autoHeight = false;
54809     grid.autoWidth = false;
54810     this.grid = grid;
54811     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54812 };
54813
54814 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54815     getId : function(){
54816         return this.grid.id;
54817     },
54818     
54819     /**
54820      * Returns the grid for this panel
54821      * @return {Roo.grid.Grid} 
54822      */
54823     getGrid : function(){
54824         return this.grid;    
54825     },
54826     
54827     setSize : function(width, height){
54828         if(!this.ignoreResize(width, height)){
54829             var grid = this.grid;
54830             var size = this.adjustForComponents(width, height);
54831             grid.getGridEl().setSize(size.width, size.height);
54832             grid.autoSize();
54833         }
54834     },
54835     
54836     beforeSlide : function(){
54837         this.grid.getView().scroller.clip();
54838     },
54839     
54840     afterSlide : function(){
54841         this.grid.getView().scroller.unclip();
54842     },
54843     
54844     destroy : function(){
54845         this.grid.destroy();
54846         delete this.grid;
54847         Roo.GridPanel.superclass.destroy.call(this); 
54848     }
54849 });
54850
54851
54852 /**
54853  * @class Roo.NestedLayoutPanel
54854  * @extends Roo.ContentPanel
54855  * @constructor
54856  * Create a new NestedLayoutPanel.
54857  * 
54858  * 
54859  * @param {Roo.BorderLayout} layout The layout for this panel
54860  * @param {String/Object} config A string to set only the title or a config object
54861  */
54862 Roo.NestedLayoutPanel = function(layout, config)
54863 {
54864     // construct with only one argument..
54865     /* FIXME - implement nicer consturctors
54866     if (layout.layout) {
54867         config = layout;
54868         layout = config.layout;
54869         delete config.layout;
54870     }
54871     if (layout.xtype && !layout.getEl) {
54872         // then layout needs constructing..
54873         layout = Roo.factory(layout, Roo);
54874     }
54875     */
54876     
54877     
54878     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54879     
54880     layout.monitorWindowResize = false; // turn off autosizing
54881     this.layout = layout;
54882     this.layout.getEl().addClass("x-layout-nested-layout");
54883     
54884     
54885     
54886     
54887 };
54888
54889 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54890
54891     setSize : function(width, height){
54892         if(!this.ignoreResize(width, height)){
54893             var size = this.adjustForComponents(width, height);
54894             var el = this.layout.getEl();
54895             el.setSize(size.width, size.height);
54896             var touch = el.dom.offsetWidth;
54897             this.layout.layout();
54898             // ie requires a double layout on the first pass
54899             if(Roo.isIE && !this.initialized){
54900                 this.initialized = true;
54901                 this.layout.layout();
54902             }
54903         }
54904     },
54905     
54906     // activate all subpanels if not currently active..
54907     
54908     setActiveState : function(active){
54909         this.active = active;
54910         if(!active){
54911             this.fireEvent("deactivate", this);
54912             return;
54913         }
54914         
54915         this.fireEvent("activate", this);
54916         // not sure if this should happen before or after..
54917         if (!this.layout) {
54918             return; // should not happen..
54919         }
54920         var reg = false;
54921         for (var r in this.layout.regions) {
54922             reg = this.layout.getRegion(r);
54923             if (reg.getActivePanel()) {
54924                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54925                 reg.setActivePanel(reg.getActivePanel());
54926                 continue;
54927             }
54928             if (!reg.panels.length) {
54929                 continue;
54930             }
54931             reg.showPanel(reg.getPanel(0));
54932         }
54933         
54934         
54935         
54936         
54937     },
54938     
54939     /**
54940      * Returns the nested BorderLayout for this panel
54941      * @return {Roo.BorderLayout} 
54942      */
54943     getLayout : function(){
54944         return this.layout;
54945     },
54946     
54947      /**
54948      * Adds a xtype elements to the layout of the nested panel
54949      * <pre><code>
54950
54951 panel.addxtype({
54952        xtype : 'ContentPanel',
54953        region: 'west',
54954        items: [ .... ]
54955    }
54956 );
54957
54958 panel.addxtype({
54959         xtype : 'NestedLayoutPanel',
54960         region: 'west',
54961         layout: {
54962            center: { },
54963            west: { }   
54964         },
54965         items : [ ... list of content panels or nested layout panels.. ]
54966    }
54967 );
54968 </code></pre>
54969      * @param {Object} cfg Xtype definition of item to add.
54970      */
54971     addxtype : function(cfg) {
54972         return this.layout.addxtype(cfg);
54973     
54974     }
54975 });
54976
54977 Roo.ScrollPanel = function(el, config, content){
54978     config = config || {};
54979     config.fitToFrame = true;
54980     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54981     
54982     this.el.dom.style.overflow = "hidden";
54983     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54984     this.el.removeClass("x-layout-inactive-content");
54985     this.el.on("mousewheel", this.onWheel, this);
54986
54987     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54988     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54989     up.unselectable(); down.unselectable();
54990     up.on("click", this.scrollUp, this);
54991     down.on("click", this.scrollDown, this);
54992     up.addClassOnOver("x-scroller-btn-over");
54993     down.addClassOnOver("x-scroller-btn-over");
54994     up.addClassOnClick("x-scroller-btn-click");
54995     down.addClassOnClick("x-scroller-btn-click");
54996     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54997
54998     this.resizeEl = this.el;
54999     this.el = wrap; this.up = up; this.down = down;
55000 };
55001
55002 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55003     increment : 100,
55004     wheelIncrement : 5,
55005     scrollUp : function(){
55006         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55007     },
55008
55009     scrollDown : function(){
55010         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55011     },
55012
55013     afterScroll : function(){
55014         var el = this.resizeEl;
55015         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55016         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55017         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55018     },
55019
55020     setSize : function(){
55021         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55022         this.afterScroll();
55023     },
55024
55025     onWheel : function(e){
55026         var d = e.getWheelDelta();
55027         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55028         this.afterScroll();
55029         e.stopEvent();
55030     },
55031
55032     setContent : function(content, loadScripts){
55033         this.resizeEl.update(content, loadScripts);
55034     }
55035
55036 });
55037
55038
55039
55040
55041
55042
55043
55044
55045
55046 /**
55047  * @class Roo.TreePanel
55048  * @extends Roo.ContentPanel
55049  * @constructor
55050  * Create a new TreePanel. - defaults to fit/scoll contents.
55051  * @param {String/Object} config A string to set only the panel's title, or a config object
55052  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55053  */
55054 Roo.TreePanel = function(config){
55055     var el = config.el;
55056     var tree = config.tree;
55057     delete config.tree; 
55058     delete config.el; // hopefull!
55059     
55060     // wrapper for IE7 strict & safari scroll issue
55061     
55062     var treeEl = el.createChild();
55063     config.resizeEl = treeEl;
55064     
55065     
55066     
55067     Roo.TreePanel.superclass.constructor.call(this, el, config);
55068  
55069  
55070     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55071     //console.log(tree);
55072     this.on('activate', function()
55073     {
55074         if (this.tree.rendered) {
55075             return;
55076         }
55077         //console.log('render tree');
55078         this.tree.render();
55079     });
55080     // this should not be needed.. - it's actually the 'el' that resizes?
55081     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55082     
55083     //this.on('resize',  function (cp, w, h) {
55084     //        this.tree.innerCt.setWidth(w);
55085     //        this.tree.innerCt.setHeight(h);
55086     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55087     //});
55088
55089         
55090     
55091 };
55092
55093 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55094     fitToFrame : true,
55095     autoScroll : true
55096 });
55097
55098
55099
55100
55101
55102
55103
55104
55105
55106
55107
55108 /*
55109  * Based on:
55110  * Ext JS Library 1.1.1
55111  * Copyright(c) 2006-2007, Ext JS, LLC.
55112  *
55113  * Originally Released Under LGPL - original licence link has changed is not relivant.
55114  *
55115  * Fork - LGPL
55116  * <script type="text/javascript">
55117  */
55118  
55119
55120 /**
55121  * @class Roo.ReaderLayout
55122  * @extends Roo.BorderLayout
55123  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55124  * center region containing two nested regions (a top one for a list view and one for item preview below),
55125  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55126  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55127  * expedites the setup of the overall layout and regions for this common application style.
55128  * Example:
55129  <pre><code>
55130 var reader = new Roo.ReaderLayout();
55131 var CP = Roo.ContentPanel;  // shortcut for adding
55132
55133 reader.beginUpdate();
55134 reader.add("north", new CP("north", "North"));
55135 reader.add("west", new CP("west", {title: "West"}));
55136 reader.add("east", new CP("east", {title: "East"}));
55137
55138 reader.regions.listView.add(new CP("listView", "List"));
55139 reader.regions.preview.add(new CP("preview", "Preview"));
55140 reader.endUpdate();
55141 </code></pre>
55142 * @constructor
55143 * Create a new ReaderLayout
55144 * @param {Object} config Configuration options
55145 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55146 * document.body if omitted)
55147 */
55148 Roo.ReaderLayout = function(config, renderTo){
55149     var c = config || {size:{}};
55150     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55151         north: c.north !== false ? Roo.apply({
55152             split:false,
55153             initialSize: 32,
55154             titlebar: false
55155         }, c.north) : false,
55156         west: c.west !== false ? Roo.apply({
55157             split:true,
55158             initialSize: 200,
55159             minSize: 175,
55160             maxSize: 400,
55161             titlebar: true,
55162             collapsible: true,
55163             animate: true,
55164             margins:{left:5,right:0,bottom:5,top:5},
55165             cmargins:{left:5,right:5,bottom:5,top:5}
55166         }, c.west) : false,
55167         east: c.east !== false ? Roo.apply({
55168             split:true,
55169             initialSize: 200,
55170             minSize: 175,
55171             maxSize: 400,
55172             titlebar: true,
55173             collapsible: true,
55174             animate: true,
55175             margins:{left:0,right:5,bottom:5,top:5},
55176             cmargins:{left:5,right:5,bottom:5,top:5}
55177         }, c.east) : false,
55178         center: Roo.apply({
55179             tabPosition: 'top',
55180             autoScroll:false,
55181             closeOnTab: true,
55182             titlebar:false,
55183             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55184         }, c.center)
55185     });
55186
55187     this.el.addClass('x-reader');
55188
55189     this.beginUpdate();
55190
55191     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55192         south: c.preview !== false ? Roo.apply({
55193             split:true,
55194             initialSize: 200,
55195             minSize: 100,
55196             autoScroll:true,
55197             collapsible:true,
55198             titlebar: true,
55199             cmargins:{top:5,left:0, right:0, bottom:0}
55200         }, c.preview) : false,
55201         center: Roo.apply({
55202             autoScroll:false,
55203             titlebar:false,
55204             minHeight:200
55205         }, c.listView)
55206     });
55207     this.add('center', new Roo.NestedLayoutPanel(inner,
55208             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55209
55210     this.endUpdate();
55211
55212     this.regions.preview = inner.getRegion('south');
55213     this.regions.listView = inner.getRegion('center');
55214 };
55215
55216 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55217  * Based on:
55218  * Ext JS Library 1.1.1
55219  * Copyright(c) 2006-2007, Ext JS, LLC.
55220  *
55221  * Originally Released Under LGPL - original licence link has changed is not relivant.
55222  *
55223  * Fork - LGPL
55224  * <script type="text/javascript">
55225  */
55226  
55227 /**
55228  * @class Roo.grid.Grid
55229  * @extends Roo.util.Observable
55230  * This class represents the primary interface of a component based grid control.
55231  * <br><br>Usage:<pre><code>
55232  var grid = new Roo.grid.Grid("my-container-id", {
55233      ds: myDataStore,
55234      cm: myColModel,
55235      selModel: mySelectionModel,
55236      autoSizeColumns: true,
55237      monitorWindowResize: false,
55238      trackMouseOver: true
55239  });
55240  // set any options
55241  grid.render();
55242  * </code></pre>
55243  * <b>Common Problems:</b><br/>
55244  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55245  * element will correct this<br/>
55246  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55247  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55248  * are unpredictable.<br/>
55249  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55250  * grid to calculate dimensions/offsets.<br/>
55251   * @constructor
55252  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55253  * The container MUST have some type of size defined for the grid to fill. The container will be
55254  * automatically set to position relative if it isn't already.
55255  * @param {Object} config A config object that sets properties on this grid.
55256  */
55257 Roo.grid.Grid = function(container, config){
55258         // initialize the container
55259         this.container = Roo.get(container);
55260         this.container.update("");
55261         this.container.setStyle("overflow", "hidden");
55262     this.container.addClass('x-grid-container');
55263
55264     this.id = this.container.id;
55265
55266     Roo.apply(this, config);
55267     // check and correct shorthanded configs
55268     if(this.ds){
55269         this.dataSource = this.ds;
55270         delete this.ds;
55271     }
55272     if(this.cm){
55273         this.colModel = this.cm;
55274         delete this.cm;
55275     }
55276     if(this.sm){
55277         this.selModel = this.sm;
55278         delete this.sm;
55279     }
55280
55281     if (this.selModel) {
55282         this.selModel = Roo.factory(this.selModel, Roo.grid);
55283         this.sm = this.selModel;
55284         this.sm.xmodule = this.xmodule || false;
55285     }
55286     if (typeof(this.colModel.config) == 'undefined') {
55287         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55288         this.cm = this.colModel;
55289         this.cm.xmodule = this.xmodule || false;
55290     }
55291     if (this.dataSource) {
55292         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55293         this.ds = this.dataSource;
55294         this.ds.xmodule = this.xmodule || false;
55295          
55296     }
55297     
55298     
55299     
55300     if(this.width){
55301         this.container.setWidth(this.width);
55302     }
55303
55304     if(this.height){
55305         this.container.setHeight(this.height);
55306     }
55307     /** @private */
55308         this.addEvents({
55309         // raw events
55310         /**
55311          * @event click
55312          * The raw click event for the entire grid.
55313          * @param {Roo.EventObject} e
55314          */
55315         "click" : true,
55316         /**
55317          * @event dblclick
55318          * The raw dblclick event for the entire grid.
55319          * @param {Roo.EventObject} e
55320          */
55321         "dblclick" : true,
55322         /**
55323          * @event contextmenu
55324          * The raw contextmenu event for the entire grid.
55325          * @param {Roo.EventObject} e
55326          */
55327         "contextmenu" : true,
55328         /**
55329          * @event mousedown
55330          * The raw mousedown event for the entire grid.
55331          * @param {Roo.EventObject} e
55332          */
55333         "mousedown" : true,
55334         /**
55335          * @event mouseup
55336          * The raw mouseup event for the entire grid.
55337          * @param {Roo.EventObject} e
55338          */
55339         "mouseup" : true,
55340         /**
55341          * @event mouseover
55342          * The raw mouseover event for the entire grid.
55343          * @param {Roo.EventObject} e
55344          */
55345         "mouseover" : true,
55346         /**
55347          * @event mouseout
55348          * The raw mouseout event for the entire grid.
55349          * @param {Roo.EventObject} e
55350          */
55351         "mouseout" : true,
55352         /**
55353          * @event keypress
55354          * The raw keypress event for the entire grid.
55355          * @param {Roo.EventObject} e
55356          */
55357         "keypress" : true,
55358         /**
55359          * @event keydown
55360          * The raw keydown event for the entire grid.
55361          * @param {Roo.EventObject} e
55362          */
55363         "keydown" : true,
55364
55365         // custom events
55366
55367         /**
55368          * @event cellclick
55369          * Fires when a cell is clicked
55370          * @param {Grid} this
55371          * @param {Number} rowIndex
55372          * @param {Number} columnIndex
55373          * @param {Roo.EventObject} e
55374          */
55375         "cellclick" : true,
55376         /**
55377          * @event celldblclick
55378          * Fires when a cell is double clicked
55379          * @param {Grid} this
55380          * @param {Number} rowIndex
55381          * @param {Number} columnIndex
55382          * @param {Roo.EventObject} e
55383          */
55384         "celldblclick" : true,
55385         /**
55386          * @event rowclick
55387          * Fires when a row is clicked
55388          * @param {Grid} this
55389          * @param {Number} rowIndex
55390          * @param {Roo.EventObject} e
55391          */
55392         "rowclick" : true,
55393         /**
55394          * @event rowdblclick
55395          * Fires when a row is double clicked
55396          * @param {Grid} this
55397          * @param {Number} rowIndex
55398          * @param {Roo.EventObject} e
55399          */
55400         "rowdblclick" : true,
55401         /**
55402          * @event headerclick
55403          * Fires when a header is clicked
55404          * @param {Grid} this
55405          * @param {Number} columnIndex
55406          * @param {Roo.EventObject} e
55407          */
55408         "headerclick" : true,
55409         /**
55410          * @event headerdblclick
55411          * Fires when a header cell is double clicked
55412          * @param {Grid} this
55413          * @param {Number} columnIndex
55414          * @param {Roo.EventObject} e
55415          */
55416         "headerdblclick" : true,
55417         /**
55418          * @event rowcontextmenu
55419          * Fires when a row is right clicked
55420          * @param {Grid} this
55421          * @param {Number} rowIndex
55422          * @param {Roo.EventObject} e
55423          */
55424         "rowcontextmenu" : true,
55425         /**
55426          * @event cellcontextmenu
55427          * Fires when a cell is right clicked
55428          * @param {Grid} this
55429          * @param {Number} rowIndex
55430          * @param {Number} cellIndex
55431          * @param {Roo.EventObject} e
55432          */
55433          "cellcontextmenu" : true,
55434         /**
55435          * @event headercontextmenu
55436          * Fires when a header is right clicked
55437          * @param {Grid} this
55438          * @param {Number} columnIndex
55439          * @param {Roo.EventObject} e
55440          */
55441         "headercontextmenu" : true,
55442         /**
55443          * @event bodyscroll
55444          * Fires when the body element is scrolled
55445          * @param {Number} scrollLeft
55446          * @param {Number} scrollTop
55447          */
55448         "bodyscroll" : true,
55449         /**
55450          * @event columnresize
55451          * Fires when the user resizes a column
55452          * @param {Number} columnIndex
55453          * @param {Number} newSize
55454          */
55455         "columnresize" : true,
55456         /**
55457          * @event columnmove
55458          * Fires when the user moves a column
55459          * @param {Number} oldIndex
55460          * @param {Number} newIndex
55461          */
55462         "columnmove" : true,
55463         /**
55464          * @event startdrag
55465          * Fires when row(s) start being dragged
55466          * @param {Grid} this
55467          * @param {Roo.GridDD} dd The drag drop object
55468          * @param {event} e The raw browser event
55469          */
55470         "startdrag" : true,
55471         /**
55472          * @event enddrag
55473          * Fires when a drag operation is complete
55474          * @param {Grid} this
55475          * @param {Roo.GridDD} dd The drag drop object
55476          * @param {event} e The raw browser event
55477          */
55478         "enddrag" : true,
55479         /**
55480          * @event dragdrop
55481          * Fires when dragged row(s) are dropped on a valid DD target
55482          * @param {Grid} this
55483          * @param {Roo.GridDD} dd The drag drop object
55484          * @param {String} targetId The target drag drop object
55485          * @param {event} e The raw browser event
55486          */
55487         "dragdrop" : true,
55488         /**
55489          * @event dragover
55490          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55491          * @param {Grid} this
55492          * @param {Roo.GridDD} dd The drag drop object
55493          * @param {String} targetId The target drag drop object
55494          * @param {event} e The raw browser event
55495          */
55496         "dragover" : true,
55497         /**
55498          * @event dragenter
55499          *  Fires when the dragged row(s) first cross another DD target while being dragged
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         "dragenter" : true,
55506         /**
55507          * @event dragout
55508          * Fires when the dragged row(s) leave another DD target while being dragged
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         "dragout" : true,
55515         /**
55516          * @event rowclass
55517          * Fires when a row is rendered, so you can change add a style to it.
55518          * @param {GridView} gridview   The grid view
55519          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55520          */
55521         'rowclass' : true,
55522
55523         /**
55524          * @event render
55525          * Fires when the grid is rendered
55526          * @param {Grid} grid
55527          */
55528         'render' : true
55529     });
55530
55531     Roo.grid.Grid.superclass.constructor.call(this);
55532 };
55533 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55534     
55535     /**
55536      * @cfg {String} ddGroup - drag drop group.
55537      */
55538       /**
55539      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55540      */
55541
55542     /**
55543      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55544      */
55545     minColumnWidth : 25,
55546
55547     /**
55548      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55549      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55550      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55551      */
55552     autoSizeColumns : false,
55553
55554     /**
55555      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55556      */
55557     autoSizeHeaders : true,
55558
55559     /**
55560      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55561      */
55562     monitorWindowResize : true,
55563
55564     /**
55565      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55566      * rows measured to get a columns size. Default is 0 (all rows).
55567      */
55568     maxRowsToMeasure : 0,
55569
55570     /**
55571      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55572      */
55573     trackMouseOver : true,
55574
55575     /**
55576     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55577     */
55578       /**
55579     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55580     */
55581     
55582     /**
55583     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55584     */
55585     enableDragDrop : false,
55586     
55587     /**
55588     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55589     */
55590     enableColumnMove : true,
55591     
55592     /**
55593     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55594     */
55595     enableColumnHide : true,
55596     
55597     /**
55598     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55599     */
55600     enableRowHeightSync : false,
55601     
55602     /**
55603     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55604     */
55605     stripeRows : true,
55606     
55607     /**
55608     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55609     */
55610     autoHeight : false,
55611
55612     /**
55613      * @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.
55614      */
55615     autoExpandColumn : false,
55616
55617     /**
55618     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55619     * Default is 50.
55620     */
55621     autoExpandMin : 50,
55622
55623     /**
55624     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55625     */
55626     autoExpandMax : 1000,
55627
55628     /**
55629     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55630     */
55631     view : null,
55632
55633     /**
55634     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55635     */
55636     loadMask : false,
55637     /**
55638     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55639     */
55640     dropTarget: false,
55641     
55642    
55643     
55644     // private
55645     rendered : false,
55646
55647     /**
55648     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55649     * of a fixed width. Default is false.
55650     */
55651     /**
55652     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55653     */
55654     
55655     
55656     /**
55657     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55658     * %0 is replaced with the number of selected rows.
55659     */
55660     ddText : "{0} selected row{1}",
55661     
55662     
55663     /**
55664      * Called once after all setup has been completed and the grid is ready to be rendered.
55665      * @return {Roo.grid.Grid} this
55666      */
55667     render : function()
55668     {
55669         var c = this.container;
55670         // try to detect autoHeight/width mode
55671         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55672             this.autoHeight = true;
55673         }
55674         var view = this.getView();
55675         view.init(this);
55676
55677         c.on("click", this.onClick, this);
55678         c.on("dblclick", this.onDblClick, this);
55679         c.on("contextmenu", this.onContextMenu, this);
55680         c.on("keydown", this.onKeyDown, this);
55681         if (Roo.isTouch) {
55682             c.on("touchstart", this.onTouchStart, this);
55683         }
55684
55685         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55686
55687         this.getSelectionModel().init(this);
55688
55689         view.render();
55690
55691         if(this.loadMask){
55692             this.loadMask = new Roo.LoadMask(this.container,
55693                     Roo.apply({store:this.dataSource}, this.loadMask));
55694         }
55695         
55696         
55697         if (this.toolbar && this.toolbar.xtype) {
55698             this.toolbar.container = this.getView().getHeaderPanel(true);
55699             this.toolbar = new Roo.Toolbar(this.toolbar);
55700         }
55701         if (this.footer && this.footer.xtype) {
55702             this.footer.dataSource = this.getDataSource();
55703             this.footer.container = this.getView().getFooterPanel(true);
55704             this.footer = Roo.factory(this.footer, Roo);
55705         }
55706         if (this.dropTarget && this.dropTarget.xtype) {
55707             delete this.dropTarget.xtype;
55708             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55709         }
55710         
55711         
55712         this.rendered = true;
55713         this.fireEvent('render', this);
55714         return this;
55715     },
55716
55717     /**
55718      * Reconfigures the grid to use a different Store and Column Model.
55719      * The View will be bound to the new objects and refreshed.
55720      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55721      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55722      */
55723     reconfigure : function(dataSource, colModel){
55724         if(this.loadMask){
55725             this.loadMask.destroy();
55726             this.loadMask = new Roo.LoadMask(this.container,
55727                     Roo.apply({store:dataSource}, this.loadMask));
55728         }
55729         this.view.bind(dataSource, colModel);
55730         this.dataSource = dataSource;
55731         this.colModel = colModel;
55732         this.view.refresh(true);
55733     },
55734     /**
55735      * addColumns
55736      * Add's a column, default at the end..
55737      
55738      * @param {int} position to add (default end)
55739      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55740      */
55741     addColumns : function(pos, ar)
55742     {
55743         
55744         for (var i =0;i< ar.length;i++) {
55745             var cfg = ar[i];
55746             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55747             this.cm.lookup[cfg.id] = cfg;
55748         }
55749         
55750         
55751         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55752             pos = this.cm.config.length; //this.cm.config.push(cfg);
55753         } 
55754         pos = Math.max(0,pos);
55755         ar.unshift(0);
55756         ar.unshift(pos);
55757         this.cm.config.splice.apply(this.cm.config, ar);
55758         
55759         
55760         
55761         this.view.generateRules(this.cm);
55762         this.view.refresh(true);
55763         
55764     },
55765     
55766     
55767     
55768     
55769     // private
55770     onKeyDown : function(e){
55771         this.fireEvent("keydown", e);
55772     },
55773
55774     /**
55775      * Destroy this grid.
55776      * @param {Boolean} removeEl True to remove the element
55777      */
55778     destroy : function(removeEl, keepListeners){
55779         if(this.loadMask){
55780             this.loadMask.destroy();
55781         }
55782         var c = this.container;
55783         c.removeAllListeners();
55784         this.view.destroy();
55785         this.colModel.purgeListeners();
55786         if(!keepListeners){
55787             this.purgeListeners();
55788         }
55789         c.update("");
55790         if(removeEl === true){
55791             c.remove();
55792         }
55793     },
55794
55795     // private
55796     processEvent : function(name, e){
55797         // does this fire select???
55798         //Roo.log('grid:processEvent '  + name);
55799         
55800         if (name != 'touchstart' ) {
55801             this.fireEvent(name, e);    
55802         }
55803         
55804         var t = e.getTarget();
55805         var v = this.view;
55806         var header = v.findHeaderIndex(t);
55807         if(header !== false){
55808             var ename = name == 'touchstart' ? 'click' : name;
55809              
55810             this.fireEvent("header" + ename, this, header, e);
55811         }else{
55812             var row = v.findRowIndex(t);
55813             var cell = v.findCellIndex(t);
55814             if (name == 'touchstart') {
55815                 // first touch is always a click.
55816                 // hopefull this happens after selection is updated.?
55817                 name = false;
55818                 
55819                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55820                     var cs = this.selModel.getSelectedCell();
55821                     if (row == cs[0] && cell == cs[1]){
55822                         name = 'dblclick';
55823                     }
55824                 }
55825                 if (typeof(this.selModel.getSelections) != 'undefined') {
55826                     var cs = this.selModel.getSelections();
55827                     var ds = this.dataSource;
55828                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55829                         name = 'dblclick';
55830                     }
55831                 }
55832                 if (!name) {
55833                     return;
55834                 }
55835             }
55836             
55837             
55838             if(row !== false){
55839                 this.fireEvent("row" + name, this, row, e);
55840                 if(cell !== false){
55841                     this.fireEvent("cell" + name, this, row, cell, e);
55842                 }
55843             }
55844         }
55845     },
55846
55847     // private
55848     onClick : function(e){
55849         this.processEvent("click", e);
55850     },
55851    // private
55852     onTouchStart : function(e){
55853         this.processEvent("touchstart", e);
55854     },
55855
55856     // private
55857     onContextMenu : function(e, t){
55858         this.processEvent("contextmenu", e);
55859     },
55860
55861     // private
55862     onDblClick : function(e){
55863         this.processEvent("dblclick", e);
55864     },
55865
55866     // private
55867     walkCells : function(row, col, step, fn, scope){
55868         var cm = this.colModel, clen = cm.getColumnCount();
55869         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55870         if(step < 0){
55871             if(col < 0){
55872                 row--;
55873                 first = false;
55874             }
55875             while(row >= 0){
55876                 if(!first){
55877                     col = clen-1;
55878                 }
55879                 first = false;
55880                 while(col >= 0){
55881                     if(fn.call(scope || this, row, col, cm) === true){
55882                         return [row, col];
55883                     }
55884                     col--;
55885                 }
55886                 row--;
55887             }
55888         } else {
55889             if(col >= clen){
55890                 row++;
55891                 first = false;
55892             }
55893             while(row < rlen){
55894                 if(!first){
55895                     col = 0;
55896                 }
55897                 first = false;
55898                 while(col < clen){
55899                     if(fn.call(scope || this, row, col, cm) === true){
55900                         return [row, col];
55901                     }
55902                     col++;
55903                 }
55904                 row++;
55905             }
55906         }
55907         return null;
55908     },
55909
55910     // private
55911     getSelections : function(){
55912         return this.selModel.getSelections();
55913     },
55914
55915     /**
55916      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55917      * but if manual update is required this method will initiate it.
55918      */
55919     autoSize : function(){
55920         if(this.rendered){
55921             this.view.layout();
55922             if(this.view.adjustForScroll){
55923                 this.view.adjustForScroll();
55924             }
55925         }
55926     },
55927
55928     /**
55929      * Returns the grid's underlying element.
55930      * @return {Element} The element
55931      */
55932     getGridEl : function(){
55933         return this.container;
55934     },
55935
55936     // private for compatibility, overridden by editor grid
55937     stopEditing : function(){},
55938
55939     /**
55940      * Returns the grid's SelectionModel.
55941      * @return {SelectionModel}
55942      */
55943     getSelectionModel : function(){
55944         if(!this.selModel){
55945             this.selModel = new Roo.grid.RowSelectionModel();
55946         }
55947         return this.selModel;
55948     },
55949
55950     /**
55951      * Returns the grid's DataSource.
55952      * @return {DataSource}
55953      */
55954     getDataSource : function(){
55955         return this.dataSource;
55956     },
55957
55958     /**
55959      * Returns the grid's ColumnModel.
55960      * @return {ColumnModel}
55961      */
55962     getColumnModel : function(){
55963         return this.colModel;
55964     },
55965
55966     /**
55967      * Returns the grid's GridView object.
55968      * @return {GridView}
55969      */
55970     getView : function(){
55971         if(!this.view){
55972             this.view = new Roo.grid.GridView(this.viewConfig);
55973             this.relayEvents(this.view, [
55974                 "beforerowremoved", "beforerowsinserted",
55975                 "beforerefresh", "rowremoved",
55976                 "rowsinserted", "rowupdated" ,"refresh"
55977             ]);
55978         }
55979         return this.view;
55980     },
55981     /**
55982      * Called to get grid's drag proxy text, by default returns this.ddText.
55983      * Override this to put something different in the dragged text.
55984      * @return {String}
55985      */
55986     getDragDropText : function(){
55987         var count = this.selModel.getCount();
55988         return String.format(this.ddText, count, count == 1 ? '' : 's');
55989     }
55990 });
55991 /*
55992  * Based on:
55993  * Ext JS Library 1.1.1
55994  * Copyright(c) 2006-2007, Ext JS, LLC.
55995  *
55996  * Originally Released Under LGPL - original licence link has changed is not relivant.
55997  *
55998  * Fork - LGPL
55999  * <script type="text/javascript">
56000  */
56001  
56002 Roo.grid.AbstractGridView = function(){
56003         this.grid = null;
56004         
56005         this.events = {
56006             "beforerowremoved" : true,
56007             "beforerowsinserted" : true,
56008             "beforerefresh" : true,
56009             "rowremoved" : true,
56010             "rowsinserted" : true,
56011             "rowupdated" : true,
56012             "refresh" : true
56013         };
56014     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56015 };
56016
56017 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56018     rowClass : "x-grid-row",
56019     cellClass : "x-grid-cell",
56020     tdClass : "x-grid-td",
56021     hdClass : "x-grid-hd",
56022     splitClass : "x-grid-hd-split",
56023     
56024     init: function(grid){
56025         this.grid = grid;
56026                 var cid = this.grid.getGridEl().id;
56027         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56028         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56029         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56030         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56031         },
56032         
56033     getColumnRenderers : function(){
56034         var renderers = [];
56035         var cm = this.grid.colModel;
56036         var colCount = cm.getColumnCount();
56037         for(var i = 0; i < colCount; i++){
56038             renderers[i] = cm.getRenderer(i);
56039         }
56040         return renderers;
56041     },
56042     
56043     getColumnIds : function(){
56044         var ids = [];
56045         var cm = this.grid.colModel;
56046         var colCount = cm.getColumnCount();
56047         for(var i = 0; i < colCount; i++){
56048             ids[i] = cm.getColumnId(i);
56049         }
56050         return ids;
56051     },
56052     
56053     getDataIndexes : function(){
56054         if(!this.indexMap){
56055             this.indexMap = this.buildIndexMap();
56056         }
56057         return this.indexMap.colToData;
56058     },
56059     
56060     getColumnIndexByDataIndex : function(dataIndex){
56061         if(!this.indexMap){
56062             this.indexMap = this.buildIndexMap();
56063         }
56064         return this.indexMap.dataToCol[dataIndex];
56065     },
56066     
56067     /**
56068      * Set a css style for a column dynamically. 
56069      * @param {Number} colIndex The index of the column
56070      * @param {String} name The css property name
56071      * @param {String} value The css value
56072      */
56073     setCSSStyle : function(colIndex, name, value){
56074         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56075         Roo.util.CSS.updateRule(selector, name, value);
56076     },
56077     
56078     generateRules : function(cm){
56079         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56080         Roo.util.CSS.removeStyleSheet(rulesId);
56081         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56082             var cid = cm.getColumnId(i);
56083             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56084                          this.tdSelector, cid, " {\n}\n",
56085                          this.hdSelector, cid, " {\n}\n",
56086                          this.splitSelector, cid, " {\n}\n");
56087         }
56088         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56089     }
56090 });/*
56091  * Based on:
56092  * Ext JS Library 1.1.1
56093  * Copyright(c) 2006-2007, Ext JS, LLC.
56094  *
56095  * Originally Released Under LGPL - original licence link has changed is not relivant.
56096  *
56097  * Fork - LGPL
56098  * <script type="text/javascript">
56099  */
56100
56101 // private
56102 // This is a support class used internally by the Grid components
56103 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56104     this.grid = grid;
56105     this.view = grid.getView();
56106     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56107     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56108     if(hd2){
56109         this.setHandleElId(Roo.id(hd));
56110         this.setOuterHandleElId(Roo.id(hd2));
56111     }
56112     this.scroll = false;
56113 };
56114 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56115     maxDragWidth: 120,
56116     getDragData : function(e){
56117         var t = Roo.lib.Event.getTarget(e);
56118         var h = this.view.findHeaderCell(t);
56119         if(h){
56120             return {ddel: h.firstChild, header:h};
56121         }
56122         return false;
56123     },
56124
56125     onInitDrag : function(e){
56126         this.view.headersDisabled = true;
56127         var clone = this.dragData.ddel.cloneNode(true);
56128         clone.id = Roo.id();
56129         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56130         this.proxy.update(clone);
56131         return true;
56132     },
56133
56134     afterValidDrop : function(){
56135         var v = this.view;
56136         setTimeout(function(){
56137             v.headersDisabled = false;
56138         }, 50);
56139     },
56140
56141     afterInvalidDrop : function(){
56142         var v = this.view;
56143         setTimeout(function(){
56144             v.headersDisabled = false;
56145         }, 50);
56146     }
56147 });
56148 /*
56149  * Based on:
56150  * Ext JS Library 1.1.1
56151  * Copyright(c) 2006-2007, Ext JS, LLC.
56152  *
56153  * Originally Released Under LGPL - original licence link has changed is not relivant.
56154  *
56155  * Fork - LGPL
56156  * <script type="text/javascript">
56157  */
56158 // private
56159 // This is a support class used internally by the Grid components
56160 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56161     this.grid = grid;
56162     this.view = grid.getView();
56163     // split the proxies so they don't interfere with mouse events
56164     this.proxyTop = Roo.DomHelper.append(document.body, {
56165         cls:"col-move-top", html:"&#160;"
56166     }, true);
56167     this.proxyBottom = Roo.DomHelper.append(document.body, {
56168         cls:"col-move-bottom", html:"&#160;"
56169     }, true);
56170     this.proxyTop.hide = this.proxyBottom.hide = function(){
56171         this.setLeftTop(-100,-100);
56172         this.setStyle("visibility", "hidden");
56173     };
56174     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56175     // temporarily disabled
56176     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56177     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56178 };
56179 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56180     proxyOffsets : [-4, -9],
56181     fly: Roo.Element.fly,
56182
56183     getTargetFromEvent : function(e){
56184         var t = Roo.lib.Event.getTarget(e);
56185         var cindex = this.view.findCellIndex(t);
56186         if(cindex !== false){
56187             return this.view.getHeaderCell(cindex);
56188         }
56189         return null;
56190     },
56191
56192     nextVisible : function(h){
56193         var v = this.view, cm = this.grid.colModel;
56194         h = h.nextSibling;
56195         while(h){
56196             if(!cm.isHidden(v.getCellIndex(h))){
56197                 return h;
56198             }
56199             h = h.nextSibling;
56200         }
56201         return null;
56202     },
56203
56204     prevVisible : function(h){
56205         var v = this.view, cm = this.grid.colModel;
56206         h = h.prevSibling;
56207         while(h){
56208             if(!cm.isHidden(v.getCellIndex(h))){
56209                 return h;
56210             }
56211             h = h.prevSibling;
56212         }
56213         return null;
56214     },
56215
56216     positionIndicator : function(h, n, e){
56217         var x = Roo.lib.Event.getPageX(e);
56218         var r = Roo.lib.Dom.getRegion(n.firstChild);
56219         var px, pt, py = r.top + this.proxyOffsets[1];
56220         if((r.right - x) <= (r.right-r.left)/2){
56221             px = r.right+this.view.borderWidth;
56222             pt = "after";
56223         }else{
56224             px = r.left;
56225             pt = "before";
56226         }
56227         var oldIndex = this.view.getCellIndex(h);
56228         var newIndex = this.view.getCellIndex(n);
56229
56230         if(this.grid.colModel.isFixed(newIndex)){
56231             return false;
56232         }
56233
56234         var locked = this.grid.colModel.isLocked(newIndex);
56235
56236         if(pt == "after"){
56237             newIndex++;
56238         }
56239         if(oldIndex < newIndex){
56240             newIndex--;
56241         }
56242         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56243             return false;
56244         }
56245         px +=  this.proxyOffsets[0];
56246         this.proxyTop.setLeftTop(px, py);
56247         this.proxyTop.show();
56248         if(!this.bottomOffset){
56249             this.bottomOffset = this.view.mainHd.getHeight();
56250         }
56251         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56252         this.proxyBottom.show();
56253         return pt;
56254     },
56255
56256     onNodeEnter : function(n, dd, e, data){
56257         if(data.header != n){
56258             this.positionIndicator(data.header, n, e);
56259         }
56260     },
56261
56262     onNodeOver : function(n, dd, e, data){
56263         var result = false;
56264         if(data.header != n){
56265             result = this.positionIndicator(data.header, n, e);
56266         }
56267         if(!result){
56268             this.proxyTop.hide();
56269             this.proxyBottom.hide();
56270         }
56271         return result ? this.dropAllowed : this.dropNotAllowed;
56272     },
56273
56274     onNodeOut : function(n, dd, e, data){
56275         this.proxyTop.hide();
56276         this.proxyBottom.hide();
56277     },
56278
56279     onNodeDrop : function(n, dd, e, data){
56280         var h = data.header;
56281         if(h != n){
56282             var cm = this.grid.colModel;
56283             var x = Roo.lib.Event.getPageX(e);
56284             var r = Roo.lib.Dom.getRegion(n.firstChild);
56285             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56286             var oldIndex = this.view.getCellIndex(h);
56287             var newIndex = this.view.getCellIndex(n);
56288             var locked = cm.isLocked(newIndex);
56289             if(pt == "after"){
56290                 newIndex++;
56291             }
56292             if(oldIndex < newIndex){
56293                 newIndex--;
56294             }
56295             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56296                 return false;
56297             }
56298             cm.setLocked(oldIndex, locked, true);
56299             cm.moveColumn(oldIndex, newIndex);
56300             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56301             return true;
56302         }
56303         return false;
56304     }
56305 });
56306 /*
56307  * Based on:
56308  * Ext JS Library 1.1.1
56309  * Copyright(c) 2006-2007, Ext JS, LLC.
56310  *
56311  * Originally Released Under LGPL - original licence link has changed is not relivant.
56312  *
56313  * Fork - LGPL
56314  * <script type="text/javascript">
56315  */
56316   
56317 /**
56318  * @class Roo.grid.GridView
56319  * @extends Roo.util.Observable
56320  *
56321  * @constructor
56322  * @param {Object} config
56323  */
56324 Roo.grid.GridView = function(config){
56325     Roo.grid.GridView.superclass.constructor.call(this);
56326     this.el = null;
56327
56328     Roo.apply(this, config);
56329 };
56330
56331 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56332
56333     unselectable :  'unselectable="on"',
56334     unselectableCls :  'x-unselectable',
56335     
56336     
56337     rowClass : "x-grid-row",
56338
56339     cellClass : "x-grid-col",
56340
56341     tdClass : "x-grid-td",
56342
56343     hdClass : "x-grid-hd",
56344
56345     splitClass : "x-grid-split",
56346
56347     sortClasses : ["sort-asc", "sort-desc"],
56348
56349     enableMoveAnim : false,
56350
56351     hlColor: "C3DAF9",
56352
56353     dh : Roo.DomHelper,
56354
56355     fly : Roo.Element.fly,
56356
56357     css : Roo.util.CSS,
56358
56359     borderWidth: 1,
56360
56361     splitOffset: 3,
56362
56363     scrollIncrement : 22,
56364
56365     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56366
56367     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56368
56369     bind : function(ds, cm){
56370         if(this.ds){
56371             this.ds.un("load", this.onLoad, this);
56372             this.ds.un("datachanged", this.onDataChange, this);
56373             this.ds.un("add", this.onAdd, this);
56374             this.ds.un("remove", this.onRemove, this);
56375             this.ds.un("update", this.onUpdate, this);
56376             this.ds.un("clear", this.onClear, this);
56377         }
56378         if(ds){
56379             ds.on("load", this.onLoad, this);
56380             ds.on("datachanged", this.onDataChange, this);
56381             ds.on("add", this.onAdd, this);
56382             ds.on("remove", this.onRemove, this);
56383             ds.on("update", this.onUpdate, this);
56384             ds.on("clear", this.onClear, this);
56385         }
56386         this.ds = ds;
56387
56388         if(this.cm){
56389             this.cm.un("widthchange", this.onColWidthChange, this);
56390             this.cm.un("headerchange", this.onHeaderChange, this);
56391             this.cm.un("hiddenchange", this.onHiddenChange, this);
56392             this.cm.un("columnmoved", this.onColumnMove, this);
56393             this.cm.un("columnlockchange", this.onColumnLock, this);
56394         }
56395         if(cm){
56396             this.generateRules(cm);
56397             cm.on("widthchange", this.onColWidthChange, this);
56398             cm.on("headerchange", this.onHeaderChange, this);
56399             cm.on("hiddenchange", this.onHiddenChange, this);
56400             cm.on("columnmoved", this.onColumnMove, this);
56401             cm.on("columnlockchange", this.onColumnLock, this);
56402         }
56403         this.cm = cm;
56404     },
56405
56406     init: function(grid){
56407         Roo.grid.GridView.superclass.init.call(this, grid);
56408
56409         this.bind(grid.dataSource, grid.colModel);
56410
56411         grid.on("headerclick", this.handleHeaderClick, this);
56412
56413         if(grid.trackMouseOver){
56414             grid.on("mouseover", this.onRowOver, this);
56415             grid.on("mouseout", this.onRowOut, this);
56416         }
56417         grid.cancelTextSelection = function(){};
56418         this.gridId = grid.id;
56419
56420         var tpls = this.templates || {};
56421
56422         if(!tpls.master){
56423             tpls.master = new Roo.Template(
56424                '<div class="x-grid" hidefocus="true">',
56425                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56426                   '<div class="x-grid-topbar"></div>',
56427                   '<div class="x-grid-scroller"><div></div></div>',
56428                   '<div class="x-grid-locked">',
56429                       '<div class="x-grid-header">{lockedHeader}</div>',
56430                       '<div class="x-grid-body">{lockedBody}</div>',
56431                   "</div>",
56432                   '<div class="x-grid-viewport">',
56433                       '<div class="x-grid-header">{header}</div>',
56434                       '<div class="x-grid-body">{body}</div>',
56435                   "</div>",
56436                   '<div class="x-grid-bottombar"></div>',
56437                  
56438                   '<div class="x-grid-resize-proxy">&#160;</div>',
56439                "</div>"
56440             );
56441             tpls.master.disableformats = true;
56442         }
56443
56444         if(!tpls.header){
56445             tpls.header = new Roo.Template(
56446                '<table border="0" cellspacing="0" cellpadding="0">',
56447                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56448                "</table>{splits}"
56449             );
56450             tpls.header.disableformats = true;
56451         }
56452         tpls.header.compile();
56453
56454         if(!tpls.hcell){
56455             tpls.hcell = new Roo.Template(
56456                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56457                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56458                 "</div></td>"
56459              );
56460              tpls.hcell.disableFormats = true;
56461         }
56462         tpls.hcell.compile();
56463
56464         if(!tpls.hsplit){
56465             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56466                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56467             tpls.hsplit.disableFormats = true;
56468         }
56469         tpls.hsplit.compile();
56470
56471         if(!tpls.body){
56472             tpls.body = new Roo.Template(
56473                '<table border="0" cellspacing="0" cellpadding="0">',
56474                "<tbody>{rows}</tbody>",
56475                "</table>"
56476             );
56477             tpls.body.disableFormats = true;
56478         }
56479         tpls.body.compile();
56480
56481         if(!tpls.row){
56482             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56483             tpls.row.disableFormats = true;
56484         }
56485         tpls.row.compile();
56486
56487         if(!tpls.cell){
56488             tpls.cell = new Roo.Template(
56489                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56490                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56491                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56492                 "</td>"
56493             );
56494             tpls.cell.disableFormats = true;
56495         }
56496         tpls.cell.compile();
56497
56498         this.templates = tpls;
56499     },
56500
56501     // remap these for backwards compat
56502     onColWidthChange : function(){
56503         this.updateColumns.apply(this, arguments);
56504     },
56505     onHeaderChange : function(){
56506         this.updateHeaders.apply(this, arguments);
56507     }, 
56508     onHiddenChange : function(){
56509         this.handleHiddenChange.apply(this, arguments);
56510     },
56511     onColumnMove : function(){
56512         this.handleColumnMove.apply(this, arguments);
56513     },
56514     onColumnLock : function(){
56515         this.handleLockChange.apply(this, arguments);
56516     },
56517
56518     onDataChange : function(){
56519         this.refresh();
56520         this.updateHeaderSortState();
56521     },
56522
56523     onClear : function(){
56524         this.refresh();
56525     },
56526
56527     onUpdate : function(ds, record){
56528         this.refreshRow(record);
56529     },
56530
56531     refreshRow : function(record){
56532         var ds = this.ds, index;
56533         if(typeof record == 'number'){
56534             index = record;
56535             record = ds.getAt(index);
56536         }else{
56537             index = ds.indexOf(record);
56538         }
56539         this.insertRows(ds, index, index, true);
56540         this.onRemove(ds, record, index+1, true);
56541         this.syncRowHeights(index, index);
56542         this.layout();
56543         this.fireEvent("rowupdated", this, index, record);
56544     },
56545
56546     onAdd : function(ds, records, index){
56547         this.insertRows(ds, index, index + (records.length-1));
56548     },
56549
56550     onRemove : function(ds, record, index, isUpdate){
56551         if(isUpdate !== true){
56552             this.fireEvent("beforerowremoved", this, index, record);
56553         }
56554         var bt = this.getBodyTable(), lt = this.getLockedTable();
56555         if(bt.rows[index]){
56556             bt.firstChild.removeChild(bt.rows[index]);
56557         }
56558         if(lt.rows[index]){
56559             lt.firstChild.removeChild(lt.rows[index]);
56560         }
56561         if(isUpdate !== true){
56562             this.stripeRows(index);
56563             this.syncRowHeights(index, index);
56564             this.layout();
56565             this.fireEvent("rowremoved", this, index, record);
56566         }
56567     },
56568
56569     onLoad : function(){
56570         this.scrollToTop();
56571     },
56572
56573     /**
56574      * Scrolls the grid to the top
56575      */
56576     scrollToTop : function(){
56577         if(this.scroller){
56578             this.scroller.dom.scrollTop = 0;
56579             this.syncScroll();
56580         }
56581     },
56582
56583     /**
56584      * Gets a panel in the header of the grid that can be used for toolbars etc.
56585      * After modifying the contents of this panel a call to grid.autoSize() may be
56586      * required to register any changes in size.
56587      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56588      * @return Roo.Element
56589      */
56590     getHeaderPanel : function(doShow){
56591         if(doShow){
56592             this.headerPanel.show();
56593         }
56594         return this.headerPanel;
56595     },
56596
56597     /**
56598      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56599      * After modifying the contents of this panel a call to grid.autoSize() may be
56600      * required to register any changes in size.
56601      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56602      * @return Roo.Element
56603      */
56604     getFooterPanel : function(doShow){
56605         if(doShow){
56606             this.footerPanel.show();
56607         }
56608         return this.footerPanel;
56609     },
56610
56611     initElements : function(){
56612         var E = Roo.Element;
56613         var el = this.grid.getGridEl().dom.firstChild;
56614         var cs = el.childNodes;
56615
56616         this.el = new E(el);
56617         
56618          this.focusEl = new E(el.firstChild);
56619         this.focusEl.swallowEvent("click", true);
56620         
56621         this.headerPanel = new E(cs[1]);
56622         this.headerPanel.enableDisplayMode("block");
56623
56624         this.scroller = new E(cs[2]);
56625         this.scrollSizer = new E(this.scroller.dom.firstChild);
56626
56627         this.lockedWrap = new E(cs[3]);
56628         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56629         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56630
56631         this.mainWrap = new E(cs[4]);
56632         this.mainHd = new E(this.mainWrap.dom.firstChild);
56633         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56634
56635         this.footerPanel = new E(cs[5]);
56636         this.footerPanel.enableDisplayMode("block");
56637
56638         this.resizeProxy = new E(cs[6]);
56639
56640         this.headerSelector = String.format(
56641            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56642            this.lockedHd.id, this.mainHd.id
56643         );
56644
56645         this.splitterSelector = String.format(
56646            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56647            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56648         );
56649     },
56650     idToCssName : function(s)
56651     {
56652         return s.replace(/[^a-z0-9]+/ig, '-');
56653     },
56654
56655     getHeaderCell : function(index){
56656         return Roo.DomQuery.select(this.headerSelector)[index];
56657     },
56658
56659     getHeaderCellMeasure : function(index){
56660         return this.getHeaderCell(index).firstChild;
56661     },
56662
56663     getHeaderCellText : function(index){
56664         return this.getHeaderCell(index).firstChild.firstChild;
56665     },
56666
56667     getLockedTable : function(){
56668         return this.lockedBody.dom.firstChild;
56669     },
56670
56671     getBodyTable : function(){
56672         return this.mainBody.dom.firstChild;
56673     },
56674
56675     getLockedRow : function(index){
56676         return this.getLockedTable().rows[index];
56677     },
56678
56679     getRow : function(index){
56680         return this.getBodyTable().rows[index];
56681     },
56682
56683     getRowComposite : function(index){
56684         if(!this.rowEl){
56685             this.rowEl = new Roo.CompositeElementLite();
56686         }
56687         var els = [], lrow, mrow;
56688         if(lrow = this.getLockedRow(index)){
56689             els.push(lrow);
56690         }
56691         if(mrow = this.getRow(index)){
56692             els.push(mrow);
56693         }
56694         this.rowEl.elements = els;
56695         return this.rowEl;
56696     },
56697     /**
56698      * Gets the 'td' of the cell
56699      * 
56700      * @param {Integer} rowIndex row to select
56701      * @param {Integer} colIndex column to select
56702      * 
56703      * @return {Object} 
56704      */
56705     getCell : function(rowIndex, colIndex){
56706         var locked = this.cm.getLockedCount();
56707         var source;
56708         if(colIndex < locked){
56709             source = this.lockedBody.dom.firstChild;
56710         }else{
56711             source = this.mainBody.dom.firstChild;
56712             colIndex -= locked;
56713         }
56714         return source.rows[rowIndex].childNodes[colIndex];
56715     },
56716
56717     getCellText : function(rowIndex, colIndex){
56718         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56719     },
56720
56721     getCellBox : function(cell){
56722         var b = this.fly(cell).getBox();
56723         if(Roo.isOpera){ // opera fails to report the Y
56724             b.y = cell.offsetTop + this.mainBody.getY();
56725         }
56726         return b;
56727     },
56728
56729     getCellIndex : function(cell){
56730         var id = String(cell.className).match(this.cellRE);
56731         if(id){
56732             return parseInt(id[1], 10);
56733         }
56734         return 0;
56735     },
56736
56737     findHeaderIndex : function(n){
56738         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56739         return r ? this.getCellIndex(r) : false;
56740     },
56741
56742     findHeaderCell : function(n){
56743         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56744         return r ? r : false;
56745     },
56746
56747     findRowIndex : function(n){
56748         if(!n){
56749             return false;
56750         }
56751         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56752         return r ? r.rowIndex : false;
56753     },
56754
56755     findCellIndex : function(node){
56756         var stop = this.el.dom;
56757         while(node && node != stop){
56758             if(this.findRE.test(node.className)){
56759                 return this.getCellIndex(node);
56760             }
56761             node = node.parentNode;
56762         }
56763         return false;
56764     },
56765
56766     getColumnId : function(index){
56767         return this.cm.getColumnId(index);
56768     },
56769
56770     getSplitters : function()
56771     {
56772         if(this.splitterSelector){
56773            return Roo.DomQuery.select(this.splitterSelector);
56774         }else{
56775             return null;
56776       }
56777     },
56778
56779     getSplitter : function(index){
56780         return this.getSplitters()[index];
56781     },
56782
56783     onRowOver : function(e, t){
56784         var row;
56785         if((row = this.findRowIndex(t)) !== false){
56786             this.getRowComposite(row).addClass("x-grid-row-over");
56787         }
56788     },
56789
56790     onRowOut : function(e, t){
56791         var row;
56792         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56793             this.getRowComposite(row).removeClass("x-grid-row-over");
56794         }
56795     },
56796
56797     renderHeaders : function(){
56798         var cm = this.cm;
56799         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56800         var cb = [], lb = [], sb = [], lsb = [], p = {};
56801         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56802             p.cellId = "x-grid-hd-0-" + i;
56803             p.splitId = "x-grid-csplit-0-" + i;
56804             p.id = cm.getColumnId(i);
56805             p.value = cm.getColumnHeader(i) || "";
56806             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56807             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56808             if(!cm.isLocked(i)){
56809                 cb[cb.length] = ct.apply(p);
56810                 sb[sb.length] = st.apply(p);
56811             }else{
56812                 lb[lb.length] = ct.apply(p);
56813                 lsb[lsb.length] = st.apply(p);
56814             }
56815         }
56816         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56817                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56818     },
56819
56820     updateHeaders : function(){
56821         var html = this.renderHeaders();
56822         this.lockedHd.update(html[0]);
56823         this.mainHd.update(html[1]);
56824     },
56825
56826     /**
56827      * Focuses the specified row.
56828      * @param {Number} row The row index
56829      */
56830     focusRow : function(row)
56831     {
56832         //Roo.log('GridView.focusRow');
56833         var x = this.scroller.dom.scrollLeft;
56834         this.focusCell(row, 0, false);
56835         this.scroller.dom.scrollLeft = x;
56836     },
56837
56838     /**
56839      * Focuses the specified cell.
56840      * @param {Number} row The row index
56841      * @param {Number} col The column index
56842      * @param {Boolean} hscroll false to disable horizontal scrolling
56843      */
56844     focusCell : function(row, col, hscroll)
56845     {
56846         //Roo.log('GridView.focusCell');
56847         var el = this.ensureVisible(row, col, hscroll);
56848         this.focusEl.alignTo(el, "tl-tl");
56849         if(Roo.isGecko){
56850             this.focusEl.focus();
56851         }else{
56852             this.focusEl.focus.defer(1, this.focusEl);
56853         }
56854     },
56855
56856     /**
56857      * Scrolls the specified cell into view
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     ensureVisible : function(row, col, hscroll)
56863     {
56864         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56865         //return null; //disable for testing.
56866         if(typeof row != "number"){
56867             row = row.rowIndex;
56868         }
56869         if(row < 0 && row >= this.ds.getCount()){
56870             return  null;
56871         }
56872         col = (col !== undefined ? col : 0);
56873         var cm = this.grid.colModel;
56874         while(cm.isHidden(col)){
56875             col++;
56876         }
56877
56878         var el = this.getCell(row, col);
56879         if(!el){
56880             return null;
56881         }
56882         var c = this.scroller.dom;
56883
56884         var ctop = parseInt(el.offsetTop, 10);
56885         var cleft = parseInt(el.offsetLeft, 10);
56886         var cbot = ctop + el.offsetHeight;
56887         var cright = cleft + el.offsetWidth;
56888         
56889         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56890         var stop = parseInt(c.scrollTop, 10);
56891         var sleft = parseInt(c.scrollLeft, 10);
56892         var sbot = stop + ch;
56893         var sright = sleft + c.clientWidth;
56894         /*
56895         Roo.log('GridView.ensureVisible:' +
56896                 ' ctop:' + ctop +
56897                 ' c.clientHeight:' + c.clientHeight +
56898                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56899                 ' stop:' + stop +
56900                 ' cbot:' + cbot +
56901                 ' sbot:' + sbot +
56902                 ' ch:' + ch  
56903                 );
56904         */
56905         if(ctop < stop){
56906             c.scrollTop = ctop;
56907             //Roo.log("set scrolltop to ctop DISABLE?");
56908         }else if(cbot > sbot){
56909             //Roo.log("set scrolltop to cbot-ch");
56910             c.scrollTop = cbot-ch;
56911         }
56912         
56913         if(hscroll !== false){
56914             if(cleft < sleft){
56915                 c.scrollLeft = cleft;
56916             }else if(cright > sright){
56917                 c.scrollLeft = cright-c.clientWidth;
56918             }
56919         }
56920          
56921         return el;
56922     },
56923
56924     updateColumns : function(){
56925         this.grid.stopEditing();
56926         var cm = this.grid.colModel, colIds = this.getColumnIds();
56927         //var totalWidth = cm.getTotalWidth();
56928         var pos = 0;
56929         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56930             //if(cm.isHidden(i)) continue;
56931             var w = cm.getColumnWidth(i);
56932             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56933             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56934         }
56935         this.updateSplitters();
56936     },
56937
56938     generateRules : function(cm){
56939         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56940         Roo.util.CSS.removeStyleSheet(rulesId);
56941         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56942             var cid = cm.getColumnId(i);
56943             var align = '';
56944             if(cm.config[i].align){
56945                 align = 'text-align:'+cm.config[i].align+';';
56946             }
56947             var hidden = '';
56948             if(cm.isHidden(i)){
56949                 hidden = 'display:none;';
56950             }
56951             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56952             ruleBuf.push(
56953                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56954                     this.hdSelector, cid, " {\n", align, width, "}\n",
56955                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56956                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56957         }
56958         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56959     },
56960
56961     updateSplitters : function(){
56962         var cm = this.cm, s = this.getSplitters();
56963         if(s){ // splitters not created yet
56964             var pos = 0, locked = true;
56965             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56966                 if(cm.isHidden(i)) {
56967                     continue;
56968                 }
56969                 var w = cm.getColumnWidth(i); // make sure it's a number
56970                 if(!cm.isLocked(i) && locked){
56971                     pos = 0;
56972                     locked = false;
56973                 }
56974                 pos += w;
56975                 s[i].style.left = (pos-this.splitOffset) + "px";
56976             }
56977         }
56978     },
56979
56980     handleHiddenChange : function(colModel, colIndex, hidden){
56981         if(hidden){
56982             this.hideColumn(colIndex);
56983         }else{
56984             this.unhideColumn(colIndex);
56985         }
56986     },
56987
56988     hideColumn : function(colIndex){
56989         var cid = this.getColumnId(colIndex);
56990         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56991         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56992         if(Roo.isSafari){
56993             this.updateHeaders();
56994         }
56995         this.updateSplitters();
56996         this.layout();
56997     },
56998
56999     unhideColumn : function(colIndex){
57000         var cid = this.getColumnId(colIndex);
57001         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57002         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57003
57004         if(Roo.isSafari){
57005             this.updateHeaders();
57006         }
57007         this.updateSplitters();
57008         this.layout();
57009     },
57010
57011     insertRows : function(dm, firstRow, lastRow, isUpdate){
57012         if(firstRow == 0 && lastRow == dm.getCount()-1){
57013             this.refresh();
57014         }else{
57015             if(!isUpdate){
57016                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57017             }
57018             var s = this.getScrollState();
57019             var markup = this.renderRows(firstRow, lastRow);
57020             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57021             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57022             this.restoreScroll(s);
57023             if(!isUpdate){
57024                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57025                 this.syncRowHeights(firstRow, lastRow);
57026                 this.stripeRows(firstRow);
57027                 this.layout();
57028             }
57029         }
57030     },
57031
57032     bufferRows : function(markup, target, index){
57033         var before = null, trows = target.rows, tbody = target.tBodies[0];
57034         if(index < trows.length){
57035             before = trows[index];
57036         }
57037         var b = document.createElement("div");
57038         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57039         var rows = b.firstChild.rows;
57040         for(var i = 0, len = rows.length; i < len; i++){
57041             if(before){
57042                 tbody.insertBefore(rows[0], before);
57043             }else{
57044                 tbody.appendChild(rows[0]);
57045             }
57046         }
57047         b.innerHTML = "";
57048         b = null;
57049     },
57050
57051     deleteRows : function(dm, firstRow, lastRow){
57052         if(dm.getRowCount()<1){
57053             this.fireEvent("beforerefresh", this);
57054             this.mainBody.update("");
57055             this.lockedBody.update("");
57056             this.fireEvent("refresh", this);
57057         }else{
57058             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57059             var bt = this.getBodyTable();
57060             var tbody = bt.firstChild;
57061             var rows = bt.rows;
57062             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57063                 tbody.removeChild(rows[firstRow]);
57064             }
57065             this.stripeRows(firstRow);
57066             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57067         }
57068     },
57069
57070     updateRows : function(dataSource, firstRow, lastRow){
57071         var s = this.getScrollState();
57072         this.refresh();
57073         this.restoreScroll(s);
57074     },
57075
57076     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57077         if(!noRefresh){
57078            this.refresh();
57079         }
57080         this.updateHeaderSortState();
57081     },
57082
57083     getScrollState : function(){
57084         
57085         var sb = this.scroller.dom;
57086         return {left: sb.scrollLeft, top: sb.scrollTop};
57087     },
57088
57089     stripeRows : function(startRow){
57090         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57091             return;
57092         }
57093         startRow = startRow || 0;
57094         var rows = this.getBodyTable().rows;
57095         var lrows = this.getLockedTable().rows;
57096         var cls = ' x-grid-row-alt ';
57097         for(var i = startRow, len = rows.length; i < len; i++){
57098             var row = rows[i], lrow = lrows[i];
57099             var isAlt = ((i+1) % 2 == 0);
57100             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57101             if(isAlt == hasAlt){
57102                 continue;
57103             }
57104             if(isAlt){
57105                 row.className += " x-grid-row-alt";
57106             }else{
57107                 row.className = row.className.replace("x-grid-row-alt", "");
57108             }
57109             if(lrow){
57110                 lrow.className = row.className;
57111             }
57112         }
57113     },
57114
57115     restoreScroll : function(state){
57116         //Roo.log('GridView.restoreScroll');
57117         var sb = this.scroller.dom;
57118         sb.scrollLeft = state.left;
57119         sb.scrollTop = state.top;
57120         this.syncScroll();
57121     },
57122
57123     syncScroll : function(){
57124         //Roo.log('GridView.syncScroll');
57125         var sb = this.scroller.dom;
57126         var sh = this.mainHd.dom;
57127         var bs = this.mainBody.dom;
57128         var lv = this.lockedBody.dom;
57129         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57130         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57131     },
57132
57133     handleScroll : function(e){
57134         this.syncScroll();
57135         var sb = this.scroller.dom;
57136         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57137         e.stopEvent();
57138     },
57139
57140     handleWheel : function(e){
57141         var d = e.getWheelDelta();
57142         this.scroller.dom.scrollTop -= d*22;
57143         // set this here to prevent jumpy scrolling on large tables
57144         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57145         e.stopEvent();
57146     },
57147
57148     renderRows : function(startRow, endRow){
57149         // pull in all the crap needed to render rows
57150         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57151         var colCount = cm.getColumnCount();
57152
57153         if(ds.getCount() < 1){
57154             return ["", ""];
57155         }
57156
57157         // build a map for all the columns
57158         var cs = [];
57159         for(var i = 0; i < colCount; i++){
57160             var name = cm.getDataIndex(i);
57161             cs[i] = {
57162                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57163                 renderer : cm.getRenderer(i),
57164                 id : cm.getColumnId(i),
57165                 locked : cm.isLocked(i),
57166                 has_editor : cm.isCellEditable(i)
57167             };
57168         }
57169
57170         startRow = startRow || 0;
57171         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57172
57173         // records to render
57174         var rs = ds.getRange(startRow, endRow);
57175
57176         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57177     },
57178
57179     // As much as I hate to duplicate code, this was branched because FireFox really hates
57180     // [].join("") on strings. The performance difference was substantial enough to
57181     // branch this function
57182     doRender : Roo.isGecko ?
57183             function(cs, rs, ds, startRow, colCount, stripe){
57184                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57185                 // buffers
57186                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57187                 
57188                 var hasListener = this.grid.hasListener('rowclass');
57189                 var rowcfg = {};
57190                 for(var j = 0, len = rs.length; j < len; j++){
57191                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57192                     for(var i = 0; i < colCount; i++){
57193                         c = cs[i];
57194                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57195                         p.id = c.id;
57196                         p.css = p.attr = "";
57197                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57198                         if(p.value == undefined || p.value === "") {
57199                             p.value = "&#160;";
57200                         }
57201                         if(c.has_editor){
57202                             p.css += ' x-grid-editable-cell';
57203                         }
57204                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57205                             p.css +=  ' x-grid-dirty-cell';
57206                         }
57207                         var markup = ct.apply(p);
57208                         if(!c.locked){
57209                             cb+= markup;
57210                         }else{
57211                             lcb+= markup;
57212                         }
57213                     }
57214                     var alt = [];
57215                     if(stripe && ((rowIndex+1) % 2 == 0)){
57216                         alt.push("x-grid-row-alt")
57217                     }
57218                     if(r.dirty){
57219                         alt.push(  " x-grid-dirty-row");
57220                     }
57221                     rp.cells = lcb;
57222                     if(this.getRowClass){
57223                         alt.push(this.getRowClass(r, rowIndex));
57224                     }
57225                     if (hasListener) {
57226                         rowcfg = {
57227                              
57228                             record: r,
57229                             rowIndex : rowIndex,
57230                             rowClass : ''
57231                         };
57232                         this.grid.fireEvent('rowclass', this, rowcfg);
57233                         alt.push(rowcfg.rowClass);
57234                     }
57235                     rp.alt = alt.join(" ");
57236                     lbuf+= rt.apply(rp);
57237                     rp.cells = cb;
57238                     buf+=  rt.apply(rp);
57239                 }
57240                 return [lbuf, buf];
57241             } :
57242             function(cs, rs, ds, startRow, colCount, stripe){
57243                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57244                 // buffers
57245                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57246                 var hasListener = this.grid.hasListener('rowclass');
57247  
57248                 var rowcfg = {};
57249                 for(var j = 0, len = rs.length; j < len; j++){
57250                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57251                     for(var i = 0; i < colCount; i++){
57252                         c = cs[i];
57253                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57254                         p.id = c.id;
57255                         p.css = p.attr = "";
57256                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57257                         if(p.value == undefined || p.value === "") {
57258                             p.value = "&#160;";
57259                         }
57260                         //Roo.log(c);
57261                          if(c.has_editor){
57262                             p.css += ' x-grid-editable-cell';
57263                         }
57264                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57265                             p.css += ' x-grid-dirty-cell' 
57266                         }
57267                         
57268                         var markup = ct.apply(p);
57269                         if(!c.locked){
57270                             cb[cb.length] = markup;
57271                         }else{
57272                             lcb[lcb.length] = markup;
57273                         }
57274                     }
57275                     var alt = [];
57276                     if(stripe && ((rowIndex+1) % 2 == 0)){
57277                         alt.push( "x-grid-row-alt");
57278                     }
57279                     if(r.dirty){
57280                         alt.push(" x-grid-dirty-row");
57281                     }
57282                     rp.cells = lcb;
57283                     if(this.getRowClass){
57284                         alt.push( this.getRowClass(r, rowIndex));
57285                     }
57286                     if (hasListener) {
57287                         rowcfg = {
57288                              
57289                             record: r,
57290                             rowIndex : rowIndex,
57291                             rowClass : ''
57292                         };
57293                         this.grid.fireEvent('rowclass', this, rowcfg);
57294                         alt.push(rowcfg.rowClass);
57295                     }
57296                     
57297                     rp.alt = alt.join(" ");
57298                     rp.cells = lcb.join("");
57299                     lbuf[lbuf.length] = rt.apply(rp);
57300                     rp.cells = cb.join("");
57301                     buf[buf.length] =  rt.apply(rp);
57302                 }
57303                 return [lbuf.join(""), buf.join("")];
57304             },
57305
57306     renderBody : function(){
57307         var markup = this.renderRows();
57308         var bt = this.templates.body;
57309         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57310     },
57311
57312     /**
57313      * Refreshes the grid
57314      * @param {Boolean} headersToo
57315      */
57316     refresh : function(headersToo){
57317         this.fireEvent("beforerefresh", this);
57318         this.grid.stopEditing();
57319         var result = this.renderBody();
57320         this.lockedBody.update(result[0]);
57321         this.mainBody.update(result[1]);
57322         if(headersToo === true){
57323             this.updateHeaders();
57324             this.updateColumns();
57325             this.updateSplitters();
57326             this.updateHeaderSortState();
57327         }
57328         this.syncRowHeights();
57329         this.layout();
57330         this.fireEvent("refresh", this);
57331     },
57332
57333     handleColumnMove : function(cm, oldIndex, newIndex){
57334         this.indexMap = null;
57335         var s = this.getScrollState();
57336         this.refresh(true);
57337         this.restoreScroll(s);
57338         this.afterMove(newIndex);
57339     },
57340
57341     afterMove : function(colIndex){
57342         if(this.enableMoveAnim && Roo.enableFx){
57343             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57344         }
57345         // if multisort - fix sortOrder, and reload..
57346         if (this.grid.dataSource.multiSort) {
57347             // the we can call sort again..
57348             var dm = this.grid.dataSource;
57349             var cm = this.grid.colModel;
57350             var so = [];
57351             for(var i = 0; i < cm.config.length; i++ ) {
57352                 
57353                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57354                     continue; // dont' bother, it's not in sort list or being set.
57355                 }
57356                 
57357                 so.push(cm.config[i].dataIndex);
57358             };
57359             dm.sortOrder = so;
57360             dm.load(dm.lastOptions);
57361             
57362             
57363         }
57364         
57365     },
57366
57367     updateCell : function(dm, rowIndex, dataIndex){
57368         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57369         if(typeof colIndex == "undefined"){ // not present in grid
57370             return;
57371         }
57372         var cm = this.grid.colModel;
57373         var cell = this.getCell(rowIndex, colIndex);
57374         var cellText = this.getCellText(rowIndex, colIndex);
57375
57376         var p = {
57377             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57378             id : cm.getColumnId(colIndex),
57379             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57380         };
57381         var renderer = cm.getRenderer(colIndex);
57382         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57383         if(typeof val == "undefined" || val === "") {
57384             val = "&#160;";
57385         }
57386         cellText.innerHTML = val;
57387         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57388         this.syncRowHeights(rowIndex, rowIndex);
57389     },
57390
57391     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57392         var maxWidth = 0;
57393         if(this.grid.autoSizeHeaders){
57394             var h = this.getHeaderCellMeasure(colIndex);
57395             maxWidth = Math.max(maxWidth, h.scrollWidth);
57396         }
57397         var tb, index;
57398         if(this.cm.isLocked(colIndex)){
57399             tb = this.getLockedTable();
57400             index = colIndex;
57401         }else{
57402             tb = this.getBodyTable();
57403             index = colIndex - this.cm.getLockedCount();
57404         }
57405         if(tb && tb.rows){
57406             var rows = tb.rows;
57407             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57408             for(var i = 0; i < stopIndex; i++){
57409                 var cell = rows[i].childNodes[index].firstChild;
57410                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57411             }
57412         }
57413         return maxWidth + /*margin for error in IE*/ 5;
57414     },
57415     /**
57416      * Autofit a column to its content.
57417      * @param {Number} colIndex
57418      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57419      */
57420      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57421          if(this.cm.isHidden(colIndex)){
57422              return; // can't calc a hidden column
57423          }
57424         if(forceMinSize){
57425             var cid = this.cm.getColumnId(colIndex);
57426             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57427            if(this.grid.autoSizeHeaders){
57428                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57429            }
57430         }
57431         var newWidth = this.calcColumnWidth(colIndex);
57432         this.cm.setColumnWidth(colIndex,
57433             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57434         if(!suppressEvent){
57435             this.grid.fireEvent("columnresize", colIndex, newWidth);
57436         }
57437     },
57438
57439     /**
57440      * Autofits all columns to their content and then expands to fit any extra space in the grid
57441      */
57442      autoSizeColumns : function(){
57443         var cm = this.grid.colModel;
57444         var colCount = cm.getColumnCount();
57445         for(var i = 0; i < colCount; i++){
57446             this.autoSizeColumn(i, true, true);
57447         }
57448         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57449             this.fitColumns();
57450         }else{
57451             this.updateColumns();
57452             this.layout();
57453         }
57454     },
57455
57456     /**
57457      * Autofits all columns to the grid's width proportionate with their current size
57458      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57459      */
57460     fitColumns : function(reserveScrollSpace){
57461         var cm = this.grid.colModel;
57462         var colCount = cm.getColumnCount();
57463         var cols = [];
57464         var width = 0;
57465         var i, w;
57466         for (i = 0; i < colCount; i++){
57467             if(!cm.isHidden(i) && !cm.isFixed(i)){
57468                 w = cm.getColumnWidth(i);
57469                 cols.push(i);
57470                 cols.push(w);
57471                 width += w;
57472             }
57473         }
57474         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57475         if(reserveScrollSpace){
57476             avail -= 17;
57477         }
57478         var frac = (avail - cm.getTotalWidth())/width;
57479         while (cols.length){
57480             w = cols.pop();
57481             i = cols.pop();
57482             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57483         }
57484         this.updateColumns();
57485         this.layout();
57486     },
57487
57488     onRowSelect : function(rowIndex){
57489         var row = this.getRowComposite(rowIndex);
57490         row.addClass("x-grid-row-selected");
57491     },
57492
57493     onRowDeselect : function(rowIndex){
57494         var row = this.getRowComposite(rowIndex);
57495         row.removeClass("x-grid-row-selected");
57496     },
57497
57498     onCellSelect : function(row, col){
57499         var cell = this.getCell(row, col);
57500         if(cell){
57501             Roo.fly(cell).addClass("x-grid-cell-selected");
57502         }
57503     },
57504
57505     onCellDeselect : function(row, col){
57506         var cell = this.getCell(row, col);
57507         if(cell){
57508             Roo.fly(cell).removeClass("x-grid-cell-selected");
57509         }
57510     },
57511
57512     updateHeaderSortState : function(){
57513         
57514         // sort state can be single { field: xxx, direction : yyy}
57515         // or   { xxx=>ASC , yyy : DESC ..... }
57516         
57517         var mstate = {};
57518         if (!this.ds.multiSort) { 
57519             var state = this.ds.getSortState();
57520             if(!state){
57521                 return;
57522             }
57523             mstate[state.field] = state.direction;
57524             // FIXME... - this is not used here.. but might be elsewhere..
57525             this.sortState = state;
57526             
57527         } else {
57528             mstate = this.ds.sortToggle;
57529         }
57530         //remove existing sort classes..
57531         
57532         var sc = this.sortClasses;
57533         var hds = this.el.select(this.headerSelector).removeClass(sc);
57534         
57535         for(var f in mstate) {
57536         
57537             var sortColumn = this.cm.findColumnIndex(f);
57538             
57539             if(sortColumn != -1){
57540                 var sortDir = mstate[f];        
57541                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57542             }
57543         }
57544         
57545          
57546         
57547     },
57548
57549
57550     handleHeaderClick : function(g, index,e){
57551         
57552         Roo.log("header click");
57553         
57554         if (Roo.isTouch) {
57555             // touch events on header are handled by context
57556             this.handleHdCtx(g,index,e);
57557             return;
57558         }
57559         
57560         
57561         if(this.headersDisabled){
57562             return;
57563         }
57564         var dm = g.dataSource, cm = g.colModel;
57565         if(!cm.isSortable(index)){
57566             return;
57567         }
57568         g.stopEditing();
57569         
57570         if (dm.multiSort) {
57571             // update the sortOrder
57572             var so = [];
57573             for(var i = 0; i < cm.config.length; i++ ) {
57574                 
57575                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57576                     continue; // dont' bother, it's not in sort list or being set.
57577                 }
57578                 
57579                 so.push(cm.config[i].dataIndex);
57580             };
57581             dm.sortOrder = so;
57582         }
57583         
57584         
57585         dm.sort(cm.getDataIndex(index));
57586     },
57587
57588
57589     destroy : function(){
57590         if(this.colMenu){
57591             this.colMenu.removeAll();
57592             Roo.menu.MenuMgr.unregister(this.colMenu);
57593             this.colMenu.getEl().remove();
57594             delete this.colMenu;
57595         }
57596         if(this.hmenu){
57597             this.hmenu.removeAll();
57598             Roo.menu.MenuMgr.unregister(this.hmenu);
57599             this.hmenu.getEl().remove();
57600             delete this.hmenu;
57601         }
57602         if(this.grid.enableColumnMove){
57603             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57604             if(dds){
57605                 for(var dd in dds){
57606                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57607                         var elid = dds[dd].dragElId;
57608                         dds[dd].unreg();
57609                         Roo.get(elid).remove();
57610                     } else if(dds[dd].config.isTarget){
57611                         dds[dd].proxyTop.remove();
57612                         dds[dd].proxyBottom.remove();
57613                         dds[dd].unreg();
57614                     }
57615                     if(Roo.dd.DDM.locationCache[dd]){
57616                         delete Roo.dd.DDM.locationCache[dd];
57617                     }
57618                 }
57619                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57620             }
57621         }
57622         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57623         this.bind(null, null);
57624         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57625     },
57626
57627     handleLockChange : function(){
57628         this.refresh(true);
57629     },
57630
57631     onDenyColumnLock : function(){
57632
57633     },
57634
57635     onDenyColumnHide : function(){
57636
57637     },
57638
57639     handleHdMenuClick : function(item){
57640         var index = this.hdCtxIndex;
57641         var cm = this.cm, ds = this.ds;
57642         switch(item.id){
57643             case "asc":
57644                 ds.sort(cm.getDataIndex(index), "ASC");
57645                 break;
57646             case "desc":
57647                 ds.sort(cm.getDataIndex(index), "DESC");
57648                 break;
57649             case "lock":
57650                 var lc = cm.getLockedCount();
57651                 if(cm.getColumnCount(true) <= lc+1){
57652                     this.onDenyColumnLock();
57653                     return;
57654                 }
57655                 if(lc != index){
57656                     cm.setLocked(index, true, true);
57657                     cm.moveColumn(index, lc);
57658                     this.grid.fireEvent("columnmove", index, lc);
57659                 }else{
57660                     cm.setLocked(index, true);
57661                 }
57662             break;
57663             case "unlock":
57664                 var lc = cm.getLockedCount();
57665                 if((lc-1) != index){
57666                     cm.setLocked(index, false, true);
57667                     cm.moveColumn(index, lc-1);
57668                     this.grid.fireEvent("columnmove", index, lc-1);
57669                 }else{
57670                     cm.setLocked(index, false);
57671                 }
57672             break;
57673             case 'wider': // used to expand cols on touch..
57674             case 'narrow':
57675                 var cw = cm.getColumnWidth(index);
57676                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57677                 cw = Math.max(0, cw);
57678                 cw = Math.min(cw,4000);
57679                 cm.setColumnWidth(index, cw);
57680                 break;
57681                 
57682             default:
57683                 index = cm.getIndexById(item.id.substr(4));
57684                 if(index != -1){
57685                     if(item.checked && cm.getColumnCount(true) <= 1){
57686                         this.onDenyColumnHide();
57687                         return false;
57688                     }
57689                     cm.setHidden(index, item.checked);
57690                 }
57691         }
57692         return true;
57693     },
57694
57695     beforeColMenuShow : function(){
57696         var cm = this.cm,  colCount = cm.getColumnCount();
57697         this.colMenu.removeAll();
57698         for(var i = 0; i < colCount; i++){
57699             this.colMenu.add(new Roo.menu.CheckItem({
57700                 id: "col-"+cm.getColumnId(i),
57701                 text: cm.getColumnHeader(i),
57702                 checked: !cm.isHidden(i),
57703                 hideOnClick:false
57704             }));
57705         }
57706     },
57707
57708     handleHdCtx : function(g, index, e){
57709         e.stopEvent();
57710         var hd = this.getHeaderCell(index);
57711         this.hdCtxIndex = index;
57712         var ms = this.hmenu.items, cm = this.cm;
57713         ms.get("asc").setDisabled(!cm.isSortable(index));
57714         ms.get("desc").setDisabled(!cm.isSortable(index));
57715         if(this.grid.enableColLock !== false){
57716             ms.get("lock").setDisabled(cm.isLocked(index));
57717             ms.get("unlock").setDisabled(!cm.isLocked(index));
57718         }
57719         this.hmenu.show(hd, "tl-bl");
57720     },
57721
57722     handleHdOver : function(e){
57723         var hd = this.findHeaderCell(e.getTarget());
57724         if(hd && !this.headersDisabled){
57725             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57726                this.fly(hd).addClass("x-grid-hd-over");
57727             }
57728         }
57729     },
57730
57731     handleHdOut : function(e){
57732         var hd = this.findHeaderCell(e.getTarget());
57733         if(hd){
57734             this.fly(hd).removeClass("x-grid-hd-over");
57735         }
57736     },
57737
57738     handleSplitDblClick : function(e, t){
57739         var i = this.getCellIndex(t);
57740         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57741             this.autoSizeColumn(i, true);
57742             this.layout();
57743         }
57744     },
57745
57746     render : function(){
57747
57748         var cm = this.cm;
57749         var colCount = cm.getColumnCount();
57750
57751         if(this.grid.monitorWindowResize === true){
57752             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57753         }
57754         var header = this.renderHeaders();
57755         var body = this.templates.body.apply({rows:""});
57756         var html = this.templates.master.apply({
57757             lockedBody: body,
57758             body: body,
57759             lockedHeader: header[0],
57760             header: header[1]
57761         });
57762
57763         //this.updateColumns();
57764
57765         this.grid.getGridEl().dom.innerHTML = html;
57766
57767         this.initElements();
57768         
57769         // a kludge to fix the random scolling effect in webkit
57770         this.el.on("scroll", function() {
57771             this.el.dom.scrollTop=0; // hopefully not recursive..
57772         },this);
57773
57774         this.scroller.on("scroll", this.handleScroll, this);
57775         this.lockedBody.on("mousewheel", this.handleWheel, this);
57776         this.mainBody.on("mousewheel", this.handleWheel, this);
57777
57778         this.mainHd.on("mouseover", this.handleHdOver, this);
57779         this.mainHd.on("mouseout", this.handleHdOut, this);
57780         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57781                 {delegate: "."+this.splitClass});
57782
57783         this.lockedHd.on("mouseover", this.handleHdOver, this);
57784         this.lockedHd.on("mouseout", this.handleHdOut, this);
57785         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57786                 {delegate: "."+this.splitClass});
57787
57788         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57789             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57790         }
57791
57792         this.updateSplitters();
57793
57794         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57795             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57796             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57797         }
57798
57799         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57800             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57801             this.hmenu.add(
57802                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57803                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57804             );
57805             if(this.grid.enableColLock !== false){
57806                 this.hmenu.add('-',
57807                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57808                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57809                 );
57810             }
57811             if (Roo.isTouch) {
57812                  this.hmenu.add('-',
57813                     {id:"wider", text: this.columnsWiderText},
57814                     {id:"narrow", text: this.columnsNarrowText }
57815                 );
57816                 
57817                  
57818             }
57819             
57820             if(this.grid.enableColumnHide !== false){
57821
57822                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57823                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57824                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57825
57826                 this.hmenu.add('-',
57827                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57828                 );
57829             }
57830             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57831
57832             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57833         }
57834
57835         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57836             this.dd = new Roo.grid.GridDragZone(this.grid, {
57837                 ddGroup : this.grid.ddGroup || 'GridDD'
57838             });
57839             
57840         }
57841
57842         /*
57843         for(var i = 0; i < colCount; i++){
57844             if(cm.isHidden(i)){
57845                 this.hideColumn(i);
57846             }
57847             if(cm.config[i].align){
57848                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57849                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57850             }
57851         }*/
57852         
57853         this.updateHeaderSortState();
57854
57855         this.beforeInitialResize();
57856         this.layout(true);
57857
57858         // two part rendering gives faster view to the user
57859         this.renderPhase2.defer(1, this);
57860     },
57861
57862     renderPhase2 : function(){
57863         // render the rows now
57864         this.refresh();
57865         if(this.grid.autoSizeColumns){
57866             this.autoSizeColumns();
57867         }
57868     },
57869
57870     beforeInitialResize : function(){
57871
57872     },
57873
57874     onColumnSplitterMoved : function(i, w){
57875         this.userResized = true;
57876         var cm = this.grid.colModel;
57877         cm.setColumnWidth(i, w, true);
57878         var cid = cm.getColumnId(i);
57879         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57880         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57881         this.updateSplitters();
57882         this.layout();
57883         this.grid.fireEvent("columnresize", i, w);
57884     },
57885
57886     syncRowHeights : function(startIndex, endIndex){
57887         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57888             startIndex = startIndex || 0;
57889             var mrows = this.getBodyTable().rows;
57890             var lrows = this.getLockedTable().rows;
57891             var len = mrows.length-1;
57892             endIndex = Math.min(endIndex || len, len);
57893             for(var i = startIndex; i <= endIndex; i++){
57894                 var m = mrows[i], l = lrows[i];
57895                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57896                 m.style.height = l.style.height = h + "px";
57897             }
57898         }
57899     },
57900
57901     layout : function(initialRender, is2ndPass)
57902     {
57903         var g = this.grid;
57904         var auto = g.autoHeight;
57905         var scrollOffset = 16;
57906         var c = g.getGridEl(), cm = this.cm,
57907                 expandCol = g.autoExpandColumn,
57908                 gv = this;
57909         //c.beginMeasure();
57910
57911         if(!c.dom.offsetWidth){ // display:none?
57912             if(initialRender){
57913                 this.lockedWrap.show();
57914                 this.mainWrap.show();
57915             }
57916             return;
57917         }
57918
57919         var hasLock = this.cm.isLocked(0);
57920
57921         var tbh = this.headerPanel.getHeight();
57922         var bbh = this.footerPanel.getHeight();
57923
57924         if(auto){
57925             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57926             var newHeight = ch + c.getBorderWidth("tb");
57927             if(g.maxHeight){
57928                 newHeight = Math.min(g.maxHeight, newHeight);
57929             }
57930             c.setHeight(newHeight);
57931         }
57932
57933         if(g.autoWidth){
57934             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57935         }
57936
57937         var s = this.scroller;
57938
57939         var csize = c.getSize(true);
57940
57941         this.el.setSize(csize.width, csize.height);
57942
57943         this.headerPanel.setWidth(csize.width);
57944         this.footerPanel.setWidth(csize.width);
57945
57946         var hdHeight = this.mainHd.getHeight();
57947         var vw = csize.width;
57948         var vh = csize.height - (tbh + bbh);
57949
57950         s.setSize(vw, vh);
57951
57952         var bt = this.getBodyTable();
57953         
57954         if(cm.getLockedCount() == cm.config.length){
57955             bt = this.getLockedTable();
57956         }
57957         
57958         var ltWidth = hasLock ?
57959                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57960
57961         var scrollHeight = bt.offsetHeight;
57962         var scrollWidth = ltWidth + bt.offsetWidth;
57963         var vscroll = false, hscroll = false;
57964
57965         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57966
57967         var lw = this.lockedWrap, mw = this.mainWrap;
57968         var lb = this.lockedBody, mb = this.mainBody;
57969
57970         setTimeout(function(){
57971             var t = s.dom.offsetTop;
57972             var w = s.dom.clientWidth,
57973                 h = s.dom.clientHeight;
57974
57975             lw.setTop(t);
57976             lw.setSize(ltWidth, h);
57977
57978             mw.setLeftTop(ltWidth, t);
57979             mw.setSize(w-ltWidth, h);
57980
57981             lb.setHeight(h-hdHeight);
57982             mb.setHeight(h-hdHeight);
57983
57984             if(is2ndPass !== true && !gv.userResized && expandCol){
57985                 // high speed resize without full column calculation
57986                 
57987                 var ci = cm.getIndexById(expandCol);
57988                 if (ci < 0) {
57989                     ci = cm.findColumnIndex(expandCol);
57990                 }
57991                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57992                 var expandId = cm.getColumnId(ci);
57993                 var  tw = cm.getTotalWidth(false);
57994                 var currentWidth = cm.getColumnWidth(ci);
57995                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57996                 if(currentWidth != cw){
57997                     cm.setColumnWidth(ci, cw, true);
57998                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57999                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58000                     gv.updateSplitters();
58001                     gv.layout(false, true);
58002                 }
58003             }
58004
58005             if(initialRender){
58006                 lw.show();
58007                 mw.show();
58008             }
58009             //c.endMeasure();
58010         }, 10);
58011     },
58012
58013     onWindowResize : function(){
58014         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58015             return;
58016         }
58017         this.layout();
58018     },
58019
58020     appendFooter : function(parentEl){
58021         return null;
58022     },
58023
58024     sortAscText : "Sort Ascending",
58025     sortDescText : "Sort Descending",
58026     lockText : "Lock Column",
58027     unlockText : "Unlock Column",
58028     columnsText : "Columns",
58029  
58030     columnsWiderText : "Wider",
58031     columnsNarrowText : "Thinner"
58032 });
58033
58034
58035 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58036     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58037     this.proxy.el.addClass('x-grid3-col-dd');
58038 };
58039
58040 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58041     handleMouseDown : function(e){
58042
58043     },
58044
58045     callHandleMouseDown : function(e){
58046         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58047     }
58048 });
58049 /*
58050  * Based on:
58051  * Ext JS Library 1.1.1
58052  * Copyright(c) 2006-2007, Ext JS, LLC.
58053  *
58054  * Originally Released Under LGPL - original licence link has changed is not relivant.
58055  *
58056  * Fork - LGPL
58057  * <script type="text/javascript">
58058  */
58059  
58060 // private
58061 // This is a support class used internally by the Grid components
58062 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58063     this.grid = grid;
58064     this.view = grid.getView();
58065     this.proxy = this.view.resizeProxy;
58066     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
58067         "gridSplitters" + this.grid.getGridEl().id, {
58068         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
58069     });
58070     this.setHandleElId(Roo.id(hd));
58071     this.setOuterHandleElId(Roo.id(hd2));
58072     this.scroll = false;
58073 };
58074 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58075     fly: Roo.Element.fly,
58076
58077     b4StartDrag : function(x, y){
58078         this.view.headersDisabled = true;
58079         this.proxy.setHeight(this.view.mainWrap.getHeight());
58080         var w = this.cm.getColumnWidth(this.cellIndex);
58081         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58082         this.resetConstraints();
58083         this.setXConstraint(minw, 1000);
58084         this.setYConstraint(0, 0);
58085         this.minX = x - minw;
58086         this.maxX = x + 1000;
58087         this.startPos = x;
58088         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58089     },
58090
58091
58092     handleMouseDown : function(e){
58093         ev = Roo.EventObject.setEvent(e);
58094         var t = this.fly(ev.getTarget());
58095         if(t.hasClass("x-grid-split")){
58096             this.cellIndex = this.view.getCellIndex(t.dom);
58097             this.split = t.dom;
58098             this.cm = this.grid.colModel;
58099             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58100                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58101             }
58102         }
58103     },
58104
58105     endDrag : function(e){
58106         this.view.headersDisabled = false;
58107         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58108         var diff = endX - this.startPos;
58109         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
58110     },
58111
58112     autoOffset : function(){
58113         this.setDelta(0,0);
58114     }
58115 });/*
58116  * Based on:
58117  * Ext JS Library 1.1.1
58118  * Copyright(c) 2006-2007, Ext JS, LLC.
58119  *
58120  * Originally Released Under LGPL - original licence link has changed is not relivant.
58121  *
58122  * Fork - LGPL
58123  * <script type="text/javascript">
58124  */
58125  
58126 // private
58127 // This is a support class used internally by the Grid components
58128 Roo.grid.GridDragZone = function(grid, config){
58129     this.view = grid.getView();
58130     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58131     if(this.view.lockedBody){
58132         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58133         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58134     }
58135     this.scroll = false;
58136     this.grid = grid;
58137     this.ddel = document.createElement('div');
58138     this.ddel.className = 'x-grid-dd-wrap';
58139 };
58140
58141 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58142     ddGroup : "GridDD",
58143
58144     getDragData : function(e){
58145         var t = Roo.lib.Event.getTarget(e);
58146         var rowIndex = this.view.findRowIndex(t);
58147         var sm = this.grid.selModel;
58148             
58149         //Roo.log(rowIndex);
58150         
58151         if (sm.getSelectedCell) {
58152             // cell selection..
58153             if (!sm.getSelectedCell()) {
58154                 return false;
58155             }
58156             if (rowIndex != sm.getSelectedCell()[0]) {
58157                 return false;
58158             }
58159         
58160         }
58161         if (sm.getSelections && sm.getSelections().length < 1) {
58162             return false;
58163         }
58164         
58165         
58166         // before it used to all dragging of unseleted... - now we dont do that.
58167         if(rowIndex !== false){
58168             
58169             // if editorgrid.. 
58170             
58171             
58172             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58173                
58174             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58175               //  
58176             //}
58177             if (e.hasModifier()){
58178                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58179             }
58180             
58181             Roo.log("getDragData");
58182             
58183             return {
58184                 grid: this.grid,
58185                 ddel: this.ddel,
58186                 rowIndex: rowIndex,
58187                 selections: sm.getSelections ? sm.getSelections() : (
58188                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58189             };
58190         }
58191         return false;
58192     },
58193     
58194     
58195     onInitDrag : function(e){
58196         var data = this.dragData;
58197         this.ddel.innerHTML = this.grid.getDragDropText();
58198         this.proxy.update(this.ddel);
58199         // fire start drag?
58200     },
58201
58202     afterRepair : function(){
58203         this.dragging = false;
58204     },
58205
58206     getRepairXY : function(e, data){
58207         return false;
58208     },
58209
58210     onEndDrag : function(data, e){
58211         // fire end drag?
58212     },
58213
58214     onValidDrop : function(dd, e, id){
58215         // fire drag drop?
58216         this.hideProxy();
58217     },
58218
58219     beforeInvalidDrop : function(e, id){
58220
58221     }
58222 });/*
58223  * Based on:
58224  * Ext JS Library 1.1.1
58225  * Copyright(c) 2006-2007, Ext JS, LLC.
58226  *
58227  * Originally Released Under LGPL - original licence link has changed is not relivant.
58228  *
58229  * Fork - LGPL
58230  * <script type="text/javascript">
58231  */
58232  
58233
58234 /**
58235  * @class Roo.grid.ColumnModel
58236  * @extends Roo.util.Observable
58237  * This is the default implementation of a ColumnModel used by the Grid. It defines
58238  * the columns in the grid.
58239  * <br>Usage:<br>
58240  <pre><code>
58241  var colModel = new Roo.grid.ColumnModel([
58242         {header: "Ticker", width: 60, sortable: true, locked: true},
58243         {header: "Company Name", width: 150, sortable: true},
58244         {header: "Market Cap.", width: 100, sortable: true},
58245         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58246         {header: "Employees", width: 100, sortable: true, resizable: false}
58247  ]);
58248  </code></pre>
58249  * <p>
58250  
58251  * The config options listed for this class are options which may appear in each
58252  * individual column definition.
58253  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58254  * @constructor
58255  * @param {Object} config An Array of column config objects. See this class's
58256  * config objects for details.
58257 */
58258 Roo.grid.ColumnModel = function(config){
58259         /**
58260      * The config passed into the constructor
58261      */
58262     this.config = config;
58263     this.lookup = {};
58264
58265     // if no id, create one
58266     // if the column does not have a dataIndex mapping,
58267     // map it to the order it is in the config
58268     for(var i = 0, len = config.length; i < len; i++){
58269         var c = config[i];
58270         if(typeof c.dataIndex == "undefined"){
58271             c.dataIndex = i;
58272         }
58273         if(typeof c.renderer == "string"){
58274             c.renderer = Roo.util.Format[c.renderer];
58275         }
58276         if(typeof c.id == "undefined"){
58277             c.id = Roo.id();
58278         }
58279         if(c.editor && c.editor.xtype){
58280             c.editor  = Roo.factory(c.editor, Roo.grid);
58281         }
58282         if(c.editor && c.editor.isFormField){
58283             c.editor = new Roo.grid.GridEditor(c.editor);
58284         }
58285         this.lookup[c.id] = c;
58286     }
58287
58288     /**
58289      * The width of columns which have no width specified (defaults to 100)
58290      * @type Number
58291      */
58292     this.defaultWidth = 100;
58293
58294     /**
58295      * Default sortable of columns which have no sortable specified (defaults to false)
58296      * @type Boolean
58297      */
58298     this.defaultSortable = false;
58299
58300     this.addEvents({
58301         /**
58302              * @event widthchange
58303              * Fires when the width of a column changes.
58304              * @param {ColumnModel} this
58305              * @param {Number} columnIndex The column index
58306              * @param {Number} newWidth The new width
58307              */
58308             "widthchange": true,
58309         /**
58310              * @event headerchange
58311              * Fires when the text of a header changes.
58312              * @param {ColumnModel} this
58313              * @param {Number} columnIndex The column index
58314              * @param {Number} newText The new header text
58315              */
58316             "headerchange": true,
58317         /**
58318              * @event hiddenchange
58319              * Fires when a column is hidden or "unhidden".
58320              * @param {ColumnModel} this
58321              * @param {Number} columnIndex The column index
58322              * @param {Boolean} hidden true if hidden, false otherwise
58323              */
58324             "hiddenchange": true,
58325             /**
58326          * @event columnmoved
58327          * Fires when a column is moved.
58328          * @param {ColumnModel} this
58329          * @param {Number} oldIndex
58330          * @param {Number} newIndex
58331          */
58332         "columnmoved" : true,
58333         /**
58334          * @event columlockchange
58335          * Fires when a column's locked state is changed
58336          * @param {ColumnModel} this
58337          * @param {Number} colIndex
58338          * @param {Boolean} locked true if locked
58339          */
58340         "columnlockchange" : true
58341     });
58342     Roo.grid.ColumnModel.superclass.constructor.call(this);
58343 };
58344 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58345     /**
58346      * @cfg {String} header The header text to display in the Grid view.
58347      */
58348     /**
58349      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58350      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58351      * specified, the column's index is used as an index into the Record's data Array.
58352      */
58353     /**
58354      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58355      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58356      */
58357     /**
58358      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58359      * Defaults to the value of the {@link #defaultSortable} property.
58360      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58361      */
58362     /**
58363      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58364      */
58365     /**
58366      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58367      */
58368     /**
58369      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58370      */
58371     /**
58372      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58373      */
58374     /**
58375      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58376      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58377      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58378      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58379      */
58380        /**
58381      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58382      */
58383     /**
58384      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58385      */
58386     /**
58387      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58388      */
58389     /**
58390      * @cfg {String} cursor (Optional)
58391      */
58392     /**
58393      * @cfg {String} tooltip (Optional)
58394      */
58395     /**
58396      * @cfg {Number} xs (Optional)
58397      */
58398     /**
58399      * @cfg {Number} sm (Optional)
58400      */
58401     /**
58402      * @cfg {Number} md (Optional)
58403      */
58404     /**
58405      * @cfg {Number} lg (Optional)
58406      */
58407     /**
58408      * Returns the id of the column at the specified index.
58409      * @param {Number} index The column index
58410      * @return {String} the id
58411      */
58412     getColumnId : function(index){
58413         return this.config[index].id;
58414     },
58415
58416     /**
58417      * Returns the column for a specified id.
58418      * @param {String} id The column id
58419      * @return {Object} the column
58420      */
58421     getColumnById : function(id){
58422         return this.lookup[id];
58423     },
58424
58425     
58426     /**
58427      * Returns the column for a specified dataIndex.
58428      * @param {String} dataIndex The column dataIndex
58429      * @return {Object|Boolean} the column or false if not found
58430      */
58431     getColumnByDataIndex: function(dataIndex){
58432         var index = this.findColumnIndex(dataIndex);
58433         return index > -1 ? this.config[index] : false;
58434     },
58435     
58436     /**
58437      * Returns the index for a specified column id.
58438      * @param {String} id The column id
58439      * @return {Number} the index, or -1 if not found
58440      */
58441     getIndexById : function(id){
58442         for(var i = 0, len = this.config.length; i < len; i++){
58443             if(this.config[i].id == id){
58444                 return i;
58445             }
58446         }
58447         return -1;
58448     },
58449     
58450     /**
58451      * Returns the index for a specified column dataIndex.
58452      * @param {String} dataIndex The column dataIndex
58453      * @return {Number} the index, or -1 if not found
58454      */
58455     
58456     findColumnIndex : function(dataIndex){
58457         for(var i = 0, len = this.config.length; i < len; i++){
58458             if(this.config[i].dataIndex == dataIndex){
58459                 return i;
58460             }
58461         }
58462         return -1;
58463     },
58464     
58465     
58466     moveColumn : function(oldIndex, newIndex){
58467         var c = this.config[oldIndex];
58468         this.config.splice(oldIndex, 1);
58469         this.config.splice(newIndex, 0, c);
58470         this.dataMap = null;
58471         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58472     },
58473
58474     isLocked : function(colIndex){
58475         return this.config[colIndex].locked === true;
58476     },
58477
58478     setLocked : function(colIndex, value, suppressEvent){
58479         if(this.isLocked(colIndex) == value){
58480             return;
58481         }
58482         this.config[colIndex].locked = value;
58483         if(!suppressEvent){
58484             this.fireEvent("columnlockchange", this, colIndex, value);
58485         }
58486     },
58487
58488     getTotalLockedWidth : function(){
58489         var totalWidth = 0;
58490         for(var i = 0; i < this.config.length; i++){
58491             if(this.isLocked(i) && !this.isHidden(i)){
58492                 this.totalWidth += this.getColumnWidth(i);
58493             }
58494         }
58495         return totalWidth;
58496     },
58497
58498     getLockedCount : function(){
58499         for(var i = 0, len = this.config.length; i < len; i++){
58500             if(!this.isLocked(i)){
58501                 return i;
58502             }
58503         }
58504         
58505         return this.config.length;
58506     },
58507
58508     /**
58509      * Returns the number of columns.
58510      * @return {Number}
58511      */
58512     getColumnCount : function(visibleOnly){
58513         if(visibleOnly === true){
58514             var c = 0;
58515             for(var i = 0, len = this.config.length; i < len; i++){
58516                 if(!this.isHidden(i)){
58517                     c++;
58518                 }
58519             }
58520             return c;
58521         }
58522         return this.config.length;
58523     },
58524
58525     /**
58526      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58527      * @param {Function} fn
58528      * @param {Object} scope (optional)
58529      * @return {Array} result
58530      */
58531     getColumnsBy : function(fn, scope){
58532         var r = [];
58533         for(var i = 0, len = this.config.length; i < len; i++){
58534             var c = this.config[i];
58535             if(fn.call(scope||this, c, i) === true){
58536                 r[r.length] = c;
58537             }
58538         }
58539         return r;
58540     },
58541
58542     /**
58543      * Returns true if the specified column is sortable.
58544      * @param {Number} col The column index
58545      * @return {Boolean}
58546      */
58547     isSortable : function(col){
58548         if(typeof this.config[col].sortable == "undefined"){
58549             return this.defaultSortable;
58550         }
58551         return this.config[col].sortable;
58552     },
58553
58554     /**
58555      * Returns the rendering (formatting) function defined for the column.
58556      * @param {Number} col The column index.
58557      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58558      */
58559     getRenderer : function(col){
58560         if(!this.config[col].renderer){
58561             return Roo.grid.ColumnModel.defaultRenderer;
58562         }
58563         return this.config[col].renderer;
58564     },
58565
58566     /**
58567      * Sets the rendering (formatting) function for a column.
58568      * @param {Number} col The column index
58569      * @param {Function} fn The function to use to process the cell's raw data
58570      * to return HTML markup for the grid view. The render function is called with
58571      * the following parameters:<ul>
58572      * <li>Data value.</li>
58573      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58574      * <li>css A CSS style string to apply to the table cell.</li>
58575      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58576      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58577      * <li>Row index</li>
58578      * <li>Column index</li>
58579      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58580      */
58581     setRenderer : function(col, fn){
58582         this.config[col].renderer = fn;
58583     },
58584
58585     /**
58586      * Returns the width for the specified column.
58587      * @param {Number} col The column index
58588      * @return {Number}
58589      */
58590     getColumnWidth : function(col){
58591         return this.config[col].width * 1 || this.defaultWidth;
58592     },
58593
58594     /**
58595      * Sets the width for a column.
58596      * @param {Number} col The column index
58597      * @param {Number} width The new width
58598      */
58599     setColumnWidth : function(col, width, suppressEvent){
58600         this.config[col].width = width;
58601         this.totalWidth = null;
58602         if(!suppressEvent){
58603              this.fireEvent("widthchange", this, col, width);
58604         }
58605     },
58606
58607     /**
58608      * Returns the total width of all columns.
58609      * @param {Boolean} includeHidden True to include hidden column widths
58610      * @return {Number}
58611      */
58612     getTotalWidth : function(includeHidden){
58613         if(!this.totalWidth){
58614             this.totalWidth = 0;
58615             for(var i = 0, len = this.config.length; i < len; i++){
58616                 if(includeHidden || !this.isHidden(i)){
58617                     this.totalWidth += this.getColumnWidth(i);
58618                 }
58619             }
58620         }
58621         return this.totalWidth;
58622     },
58623
58624     /**
58625      * Returns the header for the specified column.
58626      * @param {Number} col The column index
58627      * @return {String}
58628      */
58629     getColumnHeader : function(col){
58630         return this.config[col].header;
58631     },
58632
58633     /**
58634      * Sets the header for a column.
58635      * @param {Number} col The column index
58636      * @param {String} header The new header
58637      */
58638     setColumnHeader : function(col, header){
58639         this.config[col].header = header;
58640         this.fireEvent("headerchange", this, col, header);
58641     },
58642
58643     /**
58644      * Returns the tooltip for the specified column.
58645      * @param {Number} col The column index
58646      * @return {String}
58647      */
58648     getColumnTooltip : function(col){
58649             return this.config[col].tooltip;
58650     },
58651     /**
58652      * Sets the tooltip for a column.
58653      * @param {Number} col The column index
58654      * @param {String} tooltip The new tooltip
58655      */
58656     setColumnTooltip : function(col, tooltip){
58657             this.config[col].tooltip = tooltip;
58658     },
58659
58660     /**
58661      * Returns the dataIndex for the specified column.
58662      * @param {Number} col The column index
58663      * @return {Number}
58664      */
58665     getDataIndex : function(col){
58666         return this.config[col].dataIndex;
58667     },
58668
58669     /**
58670      * Sets the dataIndex for a column.
58671      * @param {Number} col The column index
58672      * @param {Number} dataIndex The new dataIndex
58673      */
58674     setDataIndex : function(col, dataIndex){
58675         this.config[col].dataIndex = dataIndex;
58676     },
58677
58678     
58679     
58680     /**
58681      * Returns true if the cell is editable.
58682      * @param {Number} colIndex The column index
58683      * @param {Number} rowIndex The row index - this is nto actually used..?
58684      * @return {Boolean}
58685      */
58686     isCellEditable : function(colIndex, rowIndex){
58687         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58688     },
58689
58690     /**
58691      * Returns the editor defined for the cell/column.
58692      * return false or null to disable editing.
58693      * @param {Number} colIndex The column index
58694      * @param {Number} rowIndex The row index
58695      * @return {Object}
58696      */
58697     getCellEditor : function(colIndex, rowIndex){
58698         return this.config[colIndex].editor;
58699     },
58700
58701     /**
58702      * Sets if a column is editable.
58703      * @param {Number} col The column index
58704      * @param {Boolean} editable True if the column is editable
58705      */
58706     setEditable : function(col, editable){
58707         this.config[col].editable = editable;
58708     },
58709
58710
58711     /**
58712      * Returns true if the column is hidden.
58713      * @param {Number} colIndex The column index
58714      * @return {Boolean}
58715      */
58716     isHidden : function(colIndex){
58717         return this.config[colIndex].hidden;
58718     },
58719
58720
58721     /**
58722      * Returns true if the column width cannot be changed
58723      */
58724     isFixed : function(colIndex){
58725         return this.config[colIndex].fixed;
58726     },
58727
58728     /**
58729      * Returns true if the column can be resized
58730      * @return {Boolean}
58731      */
58732     isResizable : function(colIndex){
58733         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58734     },
58735     /**
58736      * Sets if a column is hidden.
58737      * @param {Number} colIndex The column index
58738      * @param {Boolean} hidden True if the column is hidden
58739      */
58740     setHidden : function(colIndex, hidden){
58741         this.config[colIndex].hidden = hidden;
58742         this.totalWidth = null;
58743         this.fireEvent("hiddenchange", this, colIndex, hidden);
58744     },
58745
58746     /**
58747      * Sets the editor for a column.
58748      * @param {Number} col The column index
58749      * @param {Object} editor The editor object
58750      */
58751     setEditor : function(col, editor){
58752         this.config[col].editor = editor;
58753     }
58754 });
58755
58756 Roo.grid.ColumnModel.defaultRenderer = function(value)
58757 {
58758     if(typeof value == "object") {
58759         return value;
58760     }
58761         if(typeof value == "string" && value.length < 1){
58762             return "&#160;";
58763         }
58764     
58765         return String.format("{0}", value);
58766 };
58767
58768 // Alias for backwards compatibility
58769 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58770 /*
58771  * Based on:
58772  * Ext JS Library 1.1.1
58773  * Copyright(c) 2006-2007, Ext JS, LLC.
58774  *
58775  * Originally Released Under LGPL - original licence link has changed is not relivant.
58776  *
58777  * Fork - LGPL
58778  * <script type="text/javascript">
58779  */
58780
58781 /**
58782  * @class Roo.grid.AbstractSelectionModel
58783  * @extends Roo.util.Observable
58784  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58785  * implemented by descendant classes.  This class should not be directly instantiated.
58786  * @constructor
58787  */
58788 Roo.grid.AbstractSelectionModel = function(){
58789     this.locked = false;
58790     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58791 };
58792
58793 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58794     /** @ignore Called by the grid automatically. Do not call directly. */
58795     init : function(grid){
58796         this.grid = grid;
58797         this.initEvents();
58798     },
58799
58800     /**
58801      * Locks the selections.
58802      */
58803     lock : function(){
58804         this.locked = true;
58805     },
58806
58807     /**
58808      * Unlocks the selections.
58809      */
58810     unlock : function(){
58811         this.locked = false;
58812     },
58813
58814     /**
58815      * Returns true if the selections are locked.
58816      * @return {Boolean}
58817      */
58818     isLocked : function(){
58819         return this.locked;
58820     }
58821 });/*
58822  * Based on:
58823  * Ext JS Library 1.1.1
58824  * Copyright(c) 2006-2007, Ext JS, LLC.
58825  *
58826  * Originally Released Under LGPL - original licence link has changed is not relivant.
58827  *
58828  * Fork - LGPL
58829  * <script type="text/javascript">
58830  */
58831 /**
58832  * @extends Roo.grid.AbstractSelectionModel
58833  * @class Roo.grid.RowSelectionModel
58834  * The default SelectionModel used by {@link Roo.grid.Grid}.
58835  * It supports multiple selections and keyboard selection/navigation. 
58836  * @constructor
58837  * @param {Object} config
58838  */
58839 Roo.grid.RowSelectionModel = function(config){
58840     Roo.apply(this, config);
58841     this.selections = new Roo.util.MixedCollection(false, function(o){
58842         return o.id;
58843     });
58844
58845     this.last = false;
58846     this.lastActive = false;
58847
58848     this.addEvents({
58849         /**
58850              * @event selectionchange
58851              * Fires when the selection changes
58852              * @param {SelectionModel} this
58853              */
58854             "selectionchange" : true,
58855         /**
58856              * @event afterselectionchange
58857              * Fires after the selection changes (eg. by key press or clicking)
58858              * @param {SelectionModel} this
58859              */
58860             "afterselectionchange" : true,
58861         /**
58862              * @event beforerowselect
58863              * Fires when a row is selected being selected, return false to cancel.
58864              * @param {SelectionModel} this
58865              * @param {Number} rowIndex The selected index
58866              * @param {Boolean} keepExisting False if other selections will be cleared
58867              */
58868             "beforerowselect" : true,
58869         /**
58870              * @event rowselect
58871              * Fires when a row is selected.
58872              * @param {SelectionModel} this
58873              * @param {Number} rowIndex The selected index
58874              * @param {Roo.data.Record} r The record
58875              */
58876             "rowselect" : true,
58877         /**
58878              * @event rowdeselect
58879              * Fires when a row is deselected.
58880              * @param {SelectionModel} this
58881              * @param {Number} rowIndex The selected index
58882              */
58883         "rowdeselect" : true
58884     });
58885     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58886     this.locked = false;
58887 };
58888
58889 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58890     /**
58891      * @cfg {Boolean} singleSelect
58892      * True to allow selection of only one row at a time (defaults to false)
58893      */
58894     singleSelect : false,
58895
58896     // private
58897     initEvents : function(){
58898
58899         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58900             this.grid.on("mousedown", this.handleMouseDown, this);
58901         }else{ // allow click to work like normal
58902             this.grid.on("rowclick", this.handleDragableRowClick, this);
58903         }
58904
58905         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58906             "up" : function(e){
58907                 if(!e.shiftKey){
58908                     this.selectPrevious(e.shiftKey);
58909                 }else if(this.last !== false && this.lastActive !== false){
58910                     var last = this.last;
58911                     this.selectRange(this.last,  this.lastActive-1);
58912                     this.grid.getView().focusRow(this.lastActive);
58913                     if(last !== false){
58914                         this.last = last;
58915                     }
58916                 }else{
58917                     this.selectFirstRow();
58918                 }
58919                 this.fireEvent("afterselectionchange", this);
58920             },
58921             "down" : function(e){
58922                 if(!e.shiftKey){
58923                     this.selectNext(e.shiftKey);
58924                 }else if(this.last !== false && this.lastActive !== false){
58925                     var last = this.last;
58926                     this.selectRange(this.last,  this.lastActive+1);
58927                     this.grid.getView().focusRow(this.lastActive);
58928                     if(last !== false){
58929                         this.last = last;
58930                     }
58931                 }else{
58932                     this.selectFirstRow();
58933                 }
58934                 this.fireEvent("afterselectionchange", this);
58935             },
58936             scope: this
58937         });
58938
58939         var view = this.grid.view;
58940         view.on("refresh", this.onRefresh, this);
58941         view.on("rowupdated", this.onRowUpdated, this);
58942         view.on("rowremoved", this.onRemove, this);
58943     },
58944
58945     // private
58946     onRefresh : function(){
58947         var ds = this.grid.dataSource, i, v = this.grid.view;
58948         var s = this.selections;
58949         s.each(function(r){
58950             if((i = ds.indexOfId(r.id)) != -1){
58951                 v.onRowSelect(i);
58952                 s.add(ds.getAt(i)); // updating the selection relate data
58953             }else{
58954                 s.remove(r);
58955             }
58956         });
58957     },
58958
58959     // private
58960     onRemove : function(v, index, r){
58961         this.selections.remove(r);
58962     },
58963
58964     // private
58965     onRowUpdated : function(v, index, r){
58966         if(this.isSelected(r)){
58967             v.onRowSelect(index);
58968         }
58969     },
58970
58971     /**
58972      * Select records.
58973      * @param {Array} records The records to select
58974      * @param {Boolean} keepExisting (optional) True to keep existing selections
58975      */
58976     selectRecords : function(records, keepExisting){
58977         if(!keepExisting){
58978             this.clearSelections();
58979         }
58980         var ds = this.grid.dataSource;
58981         for(var i = 0, len = records.length; i < len; i++){
58982             this.selectRow(ds.indexOf(records[i]), true);
58983         }
58984     },
58985
58986     /**
58987      * Gets the number of selected rows.
58988      * @return {Number}
58989      */
58990     getCount : function(){
58991         return this.selections.length;
58992     },
58993
58994     /**
58995      * Selects the first row in the grid.
58996      */
58997     selectFirstRow : function(){
58998         this.selectRow(0);
58999     },
59000
59001     /**
59002      * Select the last row.
59003      * @param {Boolean} keepExisting (optional) True to keep existing selections
59004      */
59005     selectLastRow : function(keepExisting){
59006         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
59007     },
59008
59009     /**
59010      * Selects the row immediately following the last selected row.
59011      * @param {Boolean} keepExisting (optional) True to keep existing selections
59012      */
59013     selectNext : function(keepExisting){
59014         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
59015             this.selectRow(this.last+1, keepExisting);
59016             this.grid.getView().focusRow(this.last);
59017         }
59018     },
59019
59020     /**
59021      * Selects the row that precedes the last selected row.
59022      * @param {Boolean} keepExisting (optional) True to keep existing selections
59023      */
59024     selectPrevious : function(keepExisting){
59025         if(this.last){
59026             this.selectRow(this.last-1, keepExisting);
59027             this.grid.getView().focusRow(this.last);
59028         }
59029     },
59030
59031     /**
59032      * Returns the selected records
59033      * @return {Array} Array of selected records
59034      */
59035     getSelections : function(){
59036         return [].concat(this.selections.items);
59037     },
59038
59039     /**
59040      * Returns the first selected record.
59041      * @return {Record}
59042      */
59043     getSelected : function(){
59044         return this.selections.itemAt(0);
59045     },
59046
59047
59048     /**
59049      * Clears all selections.
59050      */
59051     clearSelections : function(fast){
59052         if(this.locked) {
59053             return;
59054         }
59055         if(fast !== true){
59056             var ds = this.grid.dataSource;
59057             var s = this.selections;
59058             s.each(function(r){
59059                 this.deselectRow(ds.indexOfId(r.id));
59060             }, this);
59061             s.clear();
59062         }else{
59063             this.selections.clear();
59064         }
59065         this.last = false;
59066     },
59067
59068
59069     /**
59070      * Selects all rows.
59071      */
59072     selectAll : function(){
59073         if(this.locked) {
59074             return;
59075         }
59076         this.selections.clear();
59077         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
59078             this.selectRow(i, true);
59079         }
59080     },
59081
59082     /**
59083      * Returns True if there is a selection.
59084      * @return {Boolean}
59085      */
59086     hasSelection : function(){
59087         return this.selections.length > 0;
59088     },
59089
59090     /**
59091      * Returns True if the specified row is selected.
59092      * @param {Number/Record} record The record or index of the record to check
59093      * @return {Boolean}
59094      */
59095     isSelected : function(index){
59096         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
59097         return (r && this.selections.key(r.id) ? true : false);
59098     },
59099
59100     /**
59101      * Returns True if the specified record id is selected.
59102      * @param {String} id The id of record to check
59103      * @return {Boolean}
59104      */
59105     isIdSelected : function(id){
59106         return (this.selections.key(id) ? true : false);
59107     },
59108
59109     // private
59110     handleMouseDown : function(e, t){
59111         var view = this.grid.getView(), rowIndex;
59112         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59113             return;
59114         };
59115         if(e.shiftKey && this.last !== false){
59116             var last = this.last;
59117             this.selectRange(last, rowIndex, e.ctrlKey);
59118             this.last = last; // reset the last
59119             view.focusRow(rowIndex);
59120         }else{
59121             var isSelected = this.isSelected(rowIndex);
59122             if(e.button !== 0 && isSelected){
59123                 view.focusRow(rowIndex);
59124             }else if(e.ctrlKey && isSelected){
59125                 this.deselectRow(rowIndex);
59126             }else if(!isSelected){
59127                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59128                 view.focusRow(rowIndex);
59129             }
59130         }
59131         this.fireEvent("afterselectionchange", this);
59132     },
59133     // private
59134     handleDragableRowClick :  function(grid, rowIndex, e) 
59135     {
59136         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59137             this.selectRow(rowIndex, false);
59138             grid.view.focusRow(rowIndex);
59139              this.fireEvent("afterselectionchange", this);
59140         }
59141     },
59142     
59143     /**
59144      * Selects multiple rows.
59145      * @param {Array} rows Array of the indexes of the row to select
59146      * @param {Boolean} keepExisting (optional) True to keep existing selections
59147      */
59148     selectRows : function(rows, keepExisting){
59149         if(!keepExisting){
59150             this.clearSelections();
59151         }
59152         for(var i = 0, len = rows.length; i < len; i++){
59153             this.selectRow(rows[i], true);
59154         }
59155     },
59156
59157     /**
59158      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59159      * @param {Number} startRow The index of the first row in the range
59160      * @param {Number} endRow The index of the last row in the range
59161      * @param {Boolean} keepExisting (optional) True to retain existing selections
59162      */
59163     selectRange : function(startRow, endRow, keepExisting){
59164         if(this.locked) {
59165             return;
59166         }
59167         if(!keepExisting){
59168             this.clearSelections();
59169         }
59170         if(startRow <= endRow){
59171             for(var i = startRow; i <= endRow; i++){
59172                 this.selectRow(i, true);
59173             }
59174         }else{
59175             for(var i = startRow; i >= endRow; i--){
59176                 this.selectRow(i, true);
59177             }
59178         }
59179     },
59180
59181     /**
59182      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59183      * @param {Number} startRow The index of the first row in the range
59184      * @param {Number} endRow The index of the last row in the range
59185      */
59186     deselectRange : function(startRow, endRow, preventViewNotify){
59187         if(this.locked) {
59188             return;
59189         }
59190         for(var i = startRow; i <= endRow; i++){
59191             this.deselectRow(i, preventViewNotify);
59192         }
59193     },
59194
59195     /**
59196      * Selects a row.
59197      * @param {Number} row The index of the row to select
59198      * @param {Boolean} keepExisting (optional) True to keep existing selections
59199      */
59200     selectRow : function(index, keepExisting, preventViewNotify){
59201         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59202             return;
59203         }
59204         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59205             if(!keepExisting || this.singleSelect){
59206                 this.clearSelections();
59207             }
59208             var r = this.grid.dataSource.getAt(index);
59209             this.selections.add(r);
59210             this.last = this.lastActive = index;
59211             if(!preventViewNotify){
59212                 this.grid.getView().onRowSelect(index);
59213             }
59214             this.fireEvent("rowselect", this, index, r);
59215             this.fireEvent("selectionchange", this);
59216         }
59217     },
59218
59219     /**
59220      * Deselects a row.
59221      * @param {Number} row The index of the row to deselect
59222      */
59223     deselectRow : function(index, preventViewNotify){
59224         if(this.locked) {
59225             return;
59226         }
59227         if(this.last == index){
59228             this.last = false;
59229         }
59230         if(this.lastActive == index){
59231             this.lastActive = false;
59232         }
59233         var r = this.grid.dataSource.getAt(index);
59234         this.selections.remove(r);
59235         if(!preventViewNotify){
59236             this.grid.getView().onRowDeselect(index);
59237         }
59238         this.fireEvent("rowdeselect", this, index);
59239         this.fireEvent("selectionchange", this);
59240     },
59241
59242     // private
59243     restoreLast : function(){
59244         if(this._last){
59245             this.last = this._last;
59246         }
59247     },
59248
59249     // private
59250     acceptsNav : function(row, col, cm){
59251         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59252     },
59253
59254     // private
59255     onEditorKey : function(field, e){
59256         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59257         if(k == e.TAB){
59258             e.stopEvent();
59259             ed.completeEdit();
59260             if(e.shiftKey){
59261                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59262             }else{
59263                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59264             }
59265         }else if(k == e.ENTER && !e.ctrlKey){
59266             e.stopEvent();
59267             ed.completeEdit();
59268             if(e.shiftKey){
59269                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59270             }else{
59271                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59272             }
59273         }else if(k == e.ESC){
59274             ed.cancelEdit();
59275         }
59276         if(newCell){
59277             g.startEditing(newCell[0], newCell[1]);
59278         }
59279     }
59280 });/*
59281  * Based on:
59282  * Ext JS Library 1.1.1
59283  * Copyright(c) 2006-2007, Ext JS, LLC.
59284  *
59285  * Originally Released Under LGPL - original licence link has changed is not relivant.
59286  *
59287  * Fork - LGPL
59288  * <script type="text/javascript">
59289  */
59290 /**
59291  * @class Roo.grid.CellSelectionModel
59292  * @extends Roo.grid.AbstractSelectionModel
59293  * This class provides the basic implementation for cell selection in a grid.
59294  * @constructor
59295  * @param {Object} config The object containing the configuration of this model.
59296  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59297  */
59298 Roo.grid.CellSelectionModel = function(config){
59299     Roo.apply(this, config);
59300
59301     this.selection = null;
59302
59303     this.addEvents({
59304         /**
59305              * @event beforerowselect
59306              * Fires before a cell is selected.
59307              * @param {SelectionModel} this
59308              * @param {Number} rowIndex The selected row index
59309              * @param {Number} colIndex The selected cell index
59310              */
59311             "beforecellselect" : true,
59312         /**
59313              * @event cellselect
59314              * Fires when a cell is selected.
59315              * @param {SelectionModel} this
59316              * @param {Number} rowIndex The selected row index
59317              * @param {Number} colIndex The selected cell index
59318              */
59319             "cellselect" : true,
59320         /**
59321              * @event selectionchange
59322              * Fires when the active selection changes.
59323              * @param {SelectionModel} this
59324              * @param {Object} selection null for no selection or an object (o) with two properties
59325                 <ul>
59326                 <li>o.record: the record object for the row the selection is in</li>
59327                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59328                 </ul>
59329              */
59330             "selectionchange" : true,
59331         /**
59332              * @event tabend
59333              * Fires when the tab (or enter) was pressed on the last editable cell
59334              * You can use this to trigger add new row.
59335              * @param {SelectionModel} this
59336              */
59337             "tabend" : true,
59338          /**
59339              * @event beforeeditnext
59340              * Fires before the next editable sell is made active
59341              * You can use this to skip to another cell or fire the tabend
59342              *    if you set cell to false
59343              * @param {Object} eventdata object : { cell : [ row, col ] } 
59344              */
59345             "beforeeditnext" : true
59346     });
59347     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59348 };
59349
59350 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59351     
59352     enter_is_tab: false,
59353
59354     /** @ignore */
59355     initEvents : function(){
59356         this.grid.on("mousedown", this.handleMouseDown, this);
59357         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59358         var view = this.grid.view;
59359         view.on("refresh", this.onViewChange, this);
59360         view.on("rowupdated", this.onRowUpdated, this);
59361         view.on("beforerowremoved", this.clearSelections, this);
59362         view.on("beforerowsinserted", this.clearSelections, this);
59363         if(this.grid.isEditor){
59364             this.grid.on("beforeedit", this.beforeEdit,  this);
59365         }
59366     },
59367
59368         //private
59369     beforeEdit : function(e){
59370         this.select(e.row, e.column, false, true, e.record);
59371     },
59372
59373         //private
59374     onRowUpdated : function(v, index, r){
59375         if(this.selection && this.selection.record == r){
59376             v.onCellSelect(index, this.selection.cell[1]);
59377         }
59378     },
59379
59380         //private
59381     onViewChange : function(){
59382         this.clearSelections(true);
59383     },
59384
59385         /**
59386          * Returns the currently selected cell,.
59387          * @return {Array} The selected cell (row, column) or null if none selected.
59388          */
59389     getSelectedCell : function(){
59390         return this.selection ? this.selection.cell : null;
59391     },
59392
59393     /**
59394      * Clears all selections.
59395      * @param {Boolean} true to prevent the gridview from being notified about the change.
59396      */
59397     clearSelections : function(preventNotify){
59398         var s = this.selection;
59399         if(s){
59400             if(preventNotify !== true){
59401                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59402             }
59403             this.selection = null;
59404             this.fireEvent("selectionchange", this, null);
59405         }
59406     },
59407
59408     /**
59409      * Returns true if there is a selection.
59410      * @return {Boolean}
59411      */
59412     hasSelection : function(){
59413         return this.selection ? true : false;
59414     },
59415
59416     /** @ignore */
59417     handleMouseDown : function(e, t){
59418         var v = this.grid.getView();
59419         if(this.isLocked()){
59420             return;
59421         };
59422         var row = v.findRowIndex(t);
59423         var cell = v.findCellIndex(t);
59424         if(row !== false && cell !== false){
59425             this.select(row, cell);
59426         }
59427     },
59428
59429     /**
59430      * Selects a cell.
59431      * @param {Number} rowIndex
59432      * @param {Number} collIndex
59433      */
59434     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59435         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59436             this.clearSelections();
59437             r = r || this.grid.dataSource.getAt(rowIndex);
59438             this.selection = {
59439                 record : r,
59440                 cell : [rowIndex, colIndex]
59441             };
59442             if(!preventViewNotify){
59443                 var v = this.grid.getView();
59444                 v.onCellSelect(rowIndex, colIndex);
59445                 if(preventFocus !== true){
59446                     v.focusCell(rowIndex, colIndex);
59447                 }
59448             }
59449             this.fireEvent("cellselect", this, rowIndex, colIndex);
59450             this.fireEvent("selectionchange", this, this.selection);
59451         }
59452     },
59453
59454         //private
59455     isSelectable : function(rowIndex, colIndex, cm){
59456         return !cm.isHidden(colIndex);
59457     },
59458
59459     /** @ignore */
59460     handleKeyDown : function(e){
59461         //Roo.log('Cell Sel Model handleKeyDown');
59462         if(!e.isNavKeyPress()){
59463             return;
59464         }
59465         var g = this.grid, s = this.selection;
59466         if(!s){
59467             e.stopEvent();
59468             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59469             if(cell){
59470                 this.select(cell[0], cell[1]);
59471             }
59472             return;
59473         }
59474         var sm = this;
59475         var walk = function(row, col, step){
59476             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59477         };
59478         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59479         var newCell;
59480
59481       
59482
59483         switch(k){
59484             case e.TAB:
59485                 // handled by onEditorKey
59486                 if (g.isEditor && g.editing) {
59487                     return;
59488                 }
59489                 if(e.shiftKey) {
59490                     newCell = walk(r, c-1, -1);
59491                 } else {
59492                     newCell = walk(r, c+1, 1);
59493                 }
59494                 break;
59495             
59496             case e.DOWN:
59497                newCell = walk(r+1, c, 1);
59498                 break;
59499             
59500             case e.UP:
59501                 newCell = walk(r-1, c, -1);
59502                 break;
59503             
59504             case e.RIGHT:
59505                 newCell = walk(r, c+1, 1);
59506                 break;
59507             
59508             case e.LEFT:
59509                 newCell = walk(r, c-1, -1);
59510                 break;
59511             
59512             case e.ENTER:
59513                 
59514                 if(g.isEditor && !g.editing){
59515                    g.startEditing(r, c);
59516                    e.stopEvent();
59517                    return;
59518                 }
59519                 
59520                 
59521              break;
59522         };
59523         if(newCell){
59524             this.select(newCell[0], newCell[1]);
59525             e.stopEvent();
59526             
59527         }
59528     },
59529
59530     acceptsNav : function(row, col, cm){
59531         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59532     },
59533     /**
59534      * Selects a cell.
59535      * @param {Number} field (not used) - as it's normally used as a listener
59536      * @param {Number} e - event - fake it by using
59537      *
59538      * var e = Roo.EventObjectImpl.prototype;
59539      * e.keyCode = e.TAB
59540      *
59541      * 
59542      */
59543     onEditorKey : function(field, e){
59544         
59545         var k = e.getKey(),
59546             newCell,
59547             g = this.grid,
59548             ed = g.activeEditor,
59549             forward = false;
59550         ///Roo.log('onEditorKey' + k);
59551         
59552         
59553         if (this.enter_is_tab && k == e.ENTER) {
59554             k = e.TAB;
59555         }
59556         
59557         if(k == e.TAB){
59558             if(e.shiftKey){
59559                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59560             }else{
59561                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59562                 forward = true;
59563             }
59564             
59565             e.stopEvent();
59566             
59567         } else if(k == e.ENTER &&  !e.ctrlKey){
59568             ed.completeEdit();
59569             e.stopEvent();
59570             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59571         
59572                 } else if(k == e.ESC){
59573             ed.cancelEdit();
59574         }
59575                 
59576         if (newCell) {
59577             var ecall = { cell : newCell, forward : forward };
59578             this.fireEvent('beforeeditnext', ecall );
59579             newCell = ecall.cell;
59580                         forward = ecall.forward;
59581         }
59582                 
59583         if(newCell){
59584             //Roo.log('next cell after edit');
59585             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59586         } else if (forward) {
59587             // tabbed past last
59588             this.fireEvent.defer(100, this, ['tabend',this]);
59589         }
59590     }
59591 });/*
59592  * Based on:
59593  * Ext JS Library 1.1.1
59594  * Copyright(c) 2006-2007, Ext JS, LLC.
59595  *
59596  * Originally Released Under LGPL - original licence link has changed is not relivant.
59597  *
59598  * Fork - LGPL
59599  * <script type="text/javascript">
59600  */
59601  
59602 /**
59603  * @class Roo.grid.EditorGrid
59604  * @extends Roo.grid.Grid
59605  * Class for creating and editable grid.
59606  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59607  * The container MUST have some type of size defined for the grid to fill. The container will be 
59608  * automatically set to position relative if it isn't already.
59609  * @param {Object} dataSource The data model to bind to
59610  * @param {Object} colModel The column model with info about this grid's columns
59611  */
59612 Roo.grid.EditorGrid = function(container, config){
59613     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59614     this.getGridEl().addClass("xedit-grid");
59615
59616     if(!this.selModel){
59617         this.selModel = new Roo.grid.CellSelectionModel();
59618     }
59619
59620     this.activeEditor = null;
59621
59622         this.addEvents({
59623             /**
59624              * @event beforeedit
59625              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59626              * <ul style="padding:5px;padding-left:16px;">
59627              * <li>grid - This grid</li>
59628              * <li>record - The record being edited</li>
59629              * <li>field - The field name being edited</li>
59630              * <li>value - The value for the field being edited.</li>
59631              * <li>row - The grid row index</li>
59632              * <li>column - The grid column index</li>
59633              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59634              * </ul>
59635              * @param {Object} e An edit event (see above for description)
59636              */
59637             "beforeedit" : true,
59638             /**
59639              * @event afteredit
59640              * Fires after a cell is edited. <br />
59641              * <ul style="padding:5px;padding-left:16px;">
59642              * <li>grid - This grid</li>
59643              * <li>record - The record being edited</li>
59644              * <li>field - The field name being edited</li>
59645              * <li>value - The value being set</li>
59646              * <li>originalValue - The original value for the field, before the edit.</li>
59647              * <li>row - The grid row index</li>
59648              * <li>column - The grid column index</li>
59649              * </ul>
59650              * @param {Object} e An edit event (see above for description)
59651              */
59652             "afteredit" : true,
59653             /**
59654              * @event validateedit
59655              * Fires after a cell is edited, but before the value is set in the record. 
59656          * You can use this to modify the value being set in the field, Return false
59657              * to cancel the change. The edit event object has the following properties <br />
59658              * <ul style="padding:5px;padding-left:16px;">
59659          * <li>editor - This editor</li>
59660              * <li>grid - This grid</li>
59661              * <li>record - The record being edited</li>
59662              * <li>field - The field name being edited</li>
59663              * <li>value - The value being set</li>
59664              * <li>originalValue - The original value for the field, before the edit.</li>
59665              * <li>row - The grid row index</li>
59666              * <li>column - The grid column index</li>
59667              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59668              * </ul>
59669              * @param {Object} e An edit event (see above for description)
59670              */
59671             "validateedit" : true
59672         });
59673     this.on("bodyscroll", this.stopEditing,  this);
59674     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59675 };
59676
59677 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59678     /**
59679      * @cfg {Number} clicksToEdit
59680      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59681      */
59682     clicksToEdit: 2,
59683
59684     // private
59685     isEditor : true,
59686     // private
59687     trackMouseOver: false, // causes very odd FF errors
59688
59689     onCellDblClick : function(g, row, col){
59690         this.startEditing(row, col);
59691     },
59692
59693     onEditComplete : function(ed, value, startValue){
59694         this.editing = false;
59695         this.activeEditor = null;
59696         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59697         var r = ed.record;
59698         var field = this.colModel.getDataIndex(ed.col);
59699         var e = {
59700             grid: this,
59701             record: r,
59702             field: field,
59703             originalValue: startValue,
59704             value: value,
59705             row: ed.row,
59706             column: ed.col,
59707             cancel:false,
59708             editor: ed
59709         };
59710         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59711         cell.show();
59712           
59713         if(String(value) !== String(startValue)){
59714             
59715             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59716                 r.set(field, e.value);
59717                 // if we are dealing with a combo box..
59718                 // then we also set the 'name' colum to be the displayField
59719                 if (ed.field.displayField && ed.field.name) {
59720                     r.set(ed.field.name, ed.field.el.dom.value);
59721                 }
59722                 
59723                 delete e.cancel; //?? why!!!
59724                 this.fireEvent("afteredit", e);
59725             }
59726         } else {
59727             this.fireEvent("afteredit", e); // always fire it!
59728         }
59729         this.view.focusCell(ed.row, ed.col);
59730     },
59731
59732     /**
59733      * Starts editing the specified for the specified row/column
59734      * @param {Number} rowIndex
59735      * @param {Number} colIndex
59736      */
59737     startEditing : function(row, col){
59738         this.stopEditing();
59739         if(this.colModel.isCellEditable(col, row)){
59740             this.view.ensureVisible(row, col, true);
59741           
59742             var r = this.dataSource.getAt(row);
59743             var field = this.colModel.getDataIndex(col);
59744             var cell = Roo.get(this.view.getCell(row,col));
59745             var e = {
59746                 grid: this,
59747                 record: r,
59748                 field: field,
59749                 value: r.data[field],
59750                 row: row,
59751                 column: col,
59752                 cancel:false 
59753             };
59754             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59755                 this.editing = true;
59756                 var ed = this.colModel.getCellEditor(col, row);
59757                 
59758                 if (!ed) {
59759                     return;
59760                 }
59761                 if(!ed.rendered){
59762                     ed.render(ed.parentEl || document.body);
59763                 }
59764                 ed.field.reset();
59765                
59766                 cell.hide();
59767                 
59768                 (function(){ // complex but required for focus issues in safari, ie and opera
59769                     ed.row = row;
59770                     ed.col = col;
59771                     ed.record = r;
59772                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59773                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59774                     this.activeEditor = ed;
59775                     var v = r.data[field];
59776                     ed.startEdit(this.view.getCell(row, col), v);
59777                     // combo's with 'displayField and name set
59778                     if (ed.field.displayField && ed.field.name) {
59779                         ed.field.el.dom.value = r.data[ed.field.name];
59780                     }
59781                     
59782                     
59783                 }).defer(50, this);
59784             }
59785         }
59786     },
59787         
59788     /**
59789      * Stops any active editing
59790      */
59791     stopEditing : function(){
59792         if(this.activeEditor){
59793             this.activeEditor.completeEdit();
59794         }
59795         this.activeEditor = null;
59796     },
59797         
59798          /**
59799      * Called to get grid's drag proxy text, by default returns this.ddText.
59800      * @return {String}
59801      */
59802     getDragDropText : function(){
59803         var count = this.selModel.getSelectedCell() ? 1 : 0;
59804         return String.format(this.ddText, count, count == 1 ? '' : 's');
59805     }
59806         
59807 });/*
59808  * Based on:
59809  * Ext JS Library 1.1.1
59810  * Copyright(c) 2006-2007, Ext JS, LLC.
59811  *
59812  * Originally Released Under LGPL - original licence link has changed is not relivant.
59813  *
59814  * Fork - LGPL
59815  * <script type="text/javascript">
59816  */
59817
59818 // private - not really -- you end up using it !
59819 // This is a support class used internally by the Grid components
59820
59821 /**
59822  * @class Roo.grid.GridEditor
59823  * @extends Roo.Editor
59824  * Class for creating and editable grid elements.
59825  * @param {Object} config any settings (must include field)
59826  */
59827 Roo.grid.GridEditor = function(field, config){
59828     if (!config && field.field) {
59829         config = field;
59830         field = Roo.factory(config.field, Roo.form);
59831     }
59832     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59833     field.monitorTab = false;
59834 };
59835
59836 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59837     
59838     /**
59839      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59840      */
59841     
59842     alignment: "tl-tl",
59843     autoSize: "width",
59844     hideEl : false,
59845     cls: "x-small-editor x-grid-editor",
59846     shim:false,
59847     shadow:"frame"
59848 });/*
59849  * Based on:
59850  * Ext JS Library 1.1.1
59851  * Copyright(c) 2006-2007, Ext JS, LLC.
59852  *
59853  * Originally Released Under LGPL - original licence link has changed is not relivant.
59854  *
59855  * Fork - LGPL
59856  * <script type="text/javascript">
59857  */
59858   
59859
59860   
59861 Roo.grid.PropertyRecord = Roo.data.Record.create([
59862     {name:'name',type:'string'},  'value'
59863 ]);
59864
59865
59866 Roo.grid.PropertyStore = function(grid, source){
59867     this.grid = grid;
59868     this.store = new Roo.data.Store({
59869         recordType : Roo.grid.PropertyRecord
59870     });
59871     this.store.on('update', this.onUpdate,  this);
59872     if(source){
59873         this.setSource(source);
59874     }
59875     Roo.grid.PropertyStore.superclass.constructor.call(this);
59876 };
59877
59878
59879
59880 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59881     setSource : function(o){
59882         this.source = o;
59883         this.store.removeAll();
59884         var data = [];
59885         for(var k in o){
59886             if(this.isEditableValue(o[k])){
59887                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59888             }
59889         }
59890         this.store.loadRecords({records: data}, {}, true);
59891     },
59892
59893     onUpdate : function(ds, record, type){
59894         if(type == Roo.data.Record.EDIT){
59895             var v = record.data['value'];
59896             var oldValue = record.modified['value'];
59897             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59898                 this.source[record.id] = v;
59899                 record.commit();
59900                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59901             }else{
59902                 record.reject();
59903             }
59904         }
59905     },
59906
59907     getProperty : function(row){
59908        return this.store.getAt(row);
59909     },
59910
59911     isEditableValue: function(val){
59912         if(val && val instanceof Date){
59913             return true;
59914         }else if(typeof val == 'object' || typeof val == 'function'){
59915             return false;
59916         }
59917         return true;
59918     },
59919
59920     setValue : function(prop, value){
59921         this.source[prop] = value;
59922         this.store.getById(prop).set('value', value);
59923     },
59924
59925     getSource : function(){
59926         return this.source;
59927     }
59928 });
59929
59930 Roo.grid.PropertyColumnModel = function(grid, store){
59931     this.grid = grid;
59932     var g = Roo.grid;
59933     g.PropertyColumnModel.superclass.constructor.call(this, [
59934         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59935         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59936     ]);
59937     this.store = store;
59938     this.bselect = Roo.DomHelper.append(document.body, {
59939         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59940             {tag: 'option', value: 'true', html: 'true'},
59941             {tag: 'option', value: 'false', html: 'false'}
59942         ]
59943     });
59944     Roo.id(this.bselect);
59945     var f = Roo.form;
59946     this.editors = {
59947         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59948         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59949         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59950         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59951         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59952     };
59953     this.renderCellDelegate = this.renderCell.createDelegate(this);
59954     this.renderPropDelegate = this.renderProp.createDelegate(this);
59955 };
59956
59957 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59958     
59959     
59960     nameText : 'Name',
59961     valueText : 'Value',
59962     
59963     dateFormat : 'm/j/Y',
59964     
59965     
59966     renderDate : function(dateVal){
59967         return dateVal.dateFormat(this.dateFormat);
59968     },
59969
59970     renderBool : function(bVal){
59971         return bVal ? 'true' : 'false';
59972     },
59973
59974     isCellEditable : function(colIndex, rowIndex){
59975         return colIndex == 1;
59976     },
59977
59978     getRenderer : function(col){
59979         return col == 1 ?
59980             this.renderCellDelegate : this.renderPropDelegate;
59981     },
59982
59983     renderProp : function(v){
59984         return this.getPropertyName(v);
59985     },
59986
59987     renderCell : function(val){
59988         var rv = val;
59989         if(val instanceof Date){
59990             rv = this.renderDate(val);
59991         }else if(typeof val == 'boolean'){
59992             rv = this.renderBool(val);
59993         }
59994         return Roo.util.Format.htmlEncode(rv);
59995     },
59996
59997     getPropertyName : function(name){
59998         var pn = this.grid.propertyNames;
59999         return pn && pn[name] ? pn[name] : name;
60000     },
60001
60002     getCellEditor : function(colIndex, rowIndex){
60003         var p = this.store.getProperty(rowIndex);
60004         var n = p.data['name'], val = p.data['value'];
60005         
60006         if(typeof(this.grid.customEditors[n]) == 'string'){
60007             return this.editors[this.grid.customEditors[n]];
60008         }
60009         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60010             return this.grid.customEditors[n];
60011         }
60012         if(val instanceof Date){
60013             return this.editors['date'];
60014         }else if(typeof val == 'number'){
60015             return this.editors['number'];
60016         }else if(typeof val == 'boolean'){
60017             return this.editors['boolean'];
60018         }else{
60019             return this.editors['string'];
60020         }
60021     }
60022 });
60023
60024 /**
60025  * @class Roo.grid.PropertyGrid
60026  * @extends Roo.grid.EditorGrid
60027  * This class represents the  interface of a component based property grid control.
60028  * <br><br>Usage:<pre><code>
60029  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60030       
60031  });
60032  // set any options
60033  grid.render();
60034  * </code></pre>
60035   
60036  * @constructor
60037  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60038  * The container MUST have some type of size defined for the grid to fill. The container will be
60039  * automatically set to position relative if it isn't already.
60040  * @param {Object} config A config object that sets properties on this grid.
60041  */
60042 Roo.grid.PropertyGrid = function(container, config){
60043     config = config || {};
60044     var store = new Roo.grid.PropertyStore(this);
60045     this.store = store;
60046     var cm = new Roo.grid.PropertyColumnModel(this, store);
60047     store.store.sort('name', 'ASC');
60048     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60049         ds: store.store,
60050         cm: cm,
60051         enableColLock:false,
60052         enableColumnMove:false,
60053         stripeRows:false,
60054         trackMouseOver: false,
60055         clicksToEdit:1
60056     }, config));
60057     this.getGridEl().addClass('x-props-grid');
60058     this.lastEditRow = null;
60059     this.on('columnresize', this.onColumnResize, this);
60060     this.addEvents({
60061          /**
60062              * @event beforepropertychange
60063              * Fires before a property changes (return false to stop?)
60064              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60065              * @param {String} id Record Id
60066              * @param {String} newval New Value
60067          * @param {String} oldval Old Value
60068              */
60069         "beforepropertychange": true,
60070         /**
60071              * @event propertychange
60072              * Fires after a property changes
60073              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60074              * @param {String} id Record Id
60075              * @param {String} newval New Value
60076          * @param {String} oldval Old Value
60077              */
60078         "propertychange": true
60079     });
60080     this.customEditors = this.customEditors || {};
60081 };
60082 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60083     
60084      /**
60085      * @cfg {Object} customEditors map of colnames=> custom editors.
60086      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60087      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60088      * false disables editing of the field.
60089          */
60090     
60091       /**
60092      * @cfg {Object} propertyNames map of property Names to their displayed value
60093          */
60094     
60095     render : function(){
60096         Roo.grid.PropertyGrid.superclass.render.call(this);
60097         this.autoSize.defer(100, this);
60098     },
60099
60100     autoSize : function(){
60101         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60102         if(this.view){
60103             this.view.fitColumns();
60104         }
60105     },
60106
60107     onColumnResize : function(){
60108         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60109         this.autoSize();
60110     },
60111     /**
60112      * Sets the data for the Grid
60113      * accepts a Key => Value object of all the elements avaiable.
60114      * @param {Object} data  to appear in grid.
60115      */
60116     setSource : function(source){
60117         this.store.setSource(source);
60118         //this.autoSize();
60119     },
60120     /**
60121      * Gets all the data from the grid.
60122      * @return {Object} data  data stored in grid
60123      */
60124     getSource : function(){
60125         return this.store.getSource();
60126     }
60127 });/*
60128   
60129  * Licence LGPL
60130  
60131  */
60132  
60133 /**
60134  * @class Roo.grid.Calendar
60135  * @extends Roo.util.Grid
60136  * This class extends the Grid to provide a calendar widget
60137  * <br><br>Usage:<pre><code>
60138  var grid = new Roo.grid.Calendar("my-container-id", {
60139      ds: myDataStore,
60140      cm: myColModel,
60141      selModel: mySelectionModel,
60142      autoSizeColumns: true,
60143      monitorWindowResize: false,
60144      trackMouseOver: true
60145      eventstore : real data store..
60146  });
60147  // set any options
60148  grid.render();
60149   
60150   * @constructor
60151  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60152  * The container MUST have some type of size defined for the grid to fill. The container will be
60153  * automatically set to position relative if it isn't already.
60154  * @param {Object} config A config object that sets properties on this grid.
60155  */
60156 Roo.grid.Calendar = function(container, config){
60157         // initialize the container
60158         this.container = Roo.get(container);
60159         this.container.update("");
60160         this.container.setStyle("overflow", "hidden");
60161     this.container.addClass('x-grid-container');
60162
60163     this.id = this.container.id;
60164
60165     Roo.apply(this, config);
60166     // check and correct shorthanded configs
60167     
60168     var rows = [];
60169     var d =1;
60170     for (var r = 0;r < 6;r++) {
60171         
60172         rows[r]=[];
60173         for (var c =0;c < 7;c++) {
60174             rows[r][c]= '';
60175         }
60176     }
60177     if (this.eventStore) {
60178         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60179         this.eventStore.on('load',this.onLoad, this);
60180         this.eventStore.on('beforeload',this.clearEvents, this);
60181          
60182     }
60183     
60184     this.dataSource = new Roo.data.Store({
60185             proxy: new Roo.data.MemoryProxy(rows),
60186             reader: new Roo.data.ArrayReader({}, [
60187                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60188     });
60189
60190     this.dataSource.load();
60191     this.ds = this.dataSource;
60192     this.ds.xmodule = this.xmodule || false;
60193     
60194     
60195     var cellRender = function(v,x,r)
60196     {
60197         return String.format(
60198             '<div class="fc-day  fc-widget-content"><div>' +
60199                 '<div class="fc-event-container"></div>' +
60200                 '<div class="fc-day-number">{0}</div>'+
60201                 
60202                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60203             '</div></div>', v);
60204     
60205     }
60206     
60207     
60208     this.colModel = new Roo.grid.ColumnModel( [
60209         {
60210             xtype: 'ColumnModel',
60211             xns: Roo.grid,
60212             dataIndex : 'weekday0',
60213             header : 'Sunday',
60214             renderer : cellRender
60215         },
60216         {
60217             xtype: 'ColumnModel',
60218             xns: Roo.grid,
60219             dataIndex : 'weekday1',
60220             header : 'Monday',
60221             renderer : cellRender
60222         },
60223         {
60224             xtype: 'ColumnModel',
60225             xns: Roo.grid,
60226             dataIndex : 'weekday2',
60227             header : 'Tuesday',
60228             renderer : cellRender
60229         },
60230         {
60231             xtype: 'ColumnModel',
60232             xns: Roo.grid,
60233             dataIndex : 'weekday3',
60234             header : 'Wednesday',
60235             renderer : cellRender
60236         },
60237         {
60238             xtype: 'ColumnModel',
60239             xns: Roo.grid,
60240             dataIndex : 'weekday4',
60241             header : 'Thursday',
60242             renderer : cellRender
60243         },
60244         {
60245             xtype: 'ColumnModel',
60246             xns: Roo.grid,
60247             dataIndex : 'weekday5',
60248             header : 'Friday',
60249             renderer : cellRender
60250         },
60251         {
60252             xtype: 'ColumnModel',
60253             xns: Roo.grid,
60254             dataIndex : 'weekday6',
60255             header : 'Saturday',
60256             renderer : cellRender
60257         }
60258     ]);
60259     this.cm = this.colModel;
60260     this.cm.xmodule = this.xmodule || false;
60261  
60262         
60263           
60264     //this.selModel = new Roo.grid.CellSelectionModel();
60265     //this.sm = this.selModel;
60266     //this.selModel.init(this);
60267     
60268     
60269     if(this.width){
60270         this.container.setWidth(this.width);
60271     }
60272
60273     if(this.height){
60274         this.container.setHeight(this.height);
60275     }
60276     /** @private */
60277         this.addEvents({
60278         // raw events
60279         /**
60280          * @event click
60281          * The raw click event for the entire grid.
60282          * @param {Roo.EventObject} e
60283          */
60284         "click" : true,
60285         /**
60286          * @event dblclick
60287          * The raw dblclick event for the entire grid.
60288          * @param {Roo.EventObject} e
60289          */
60290         "dblclick" : true,
60291         /**
60292          * @event contextmenu
60293          * The raw contextmenu event for the entire grid.
60294          * @param {Roo.EventObject} e
60295          */
60296         "contextmenu" : true,
60297         /**
60298          * @event mousedown
60299          * The raw mousedown event for the entire grid.
60300          * @param {Roo.EventObject} e
60301          */
60302         "mousedown" : true,
60303         /**
60304          * @event mouseup
60305          * The raw mouseup event for the entire grid.
60306          * @param {Roo.EventObject} e
60307          */
60308         "mouseup" : true,
60309         /**
60310          * @event mouseover
60311          * The raw mouseover event for the entire grid.
60312          * @param {Roo.EventObject} e
60313          */
60314         "mouseover" : true,
60315         /**
60316          * @event mouseout
60317          * The raw mouseout event for the entire grid.
60318          * @param {Roo.EventObject} e
60319          */
60320         "mouseout" : true,
60321         /**
60322          * @event keypress
60323          * The raw keypress event for the entire grid.
60324          * @param {Roo.EventObject} e
60325          */
60326         "keypress" : true,
60327         /**
60328          * @event keydown
60329          * The raw keydown event for the entire grid.
60330          * @param {Roo.EventObject} e
60331          */
60332         "keydown" : true,
60333
60334         // custom events
60335
60336         /**
60337          * @event cellclick
60338          * Fires when a cell is clicked
60339          * @param {Grid} this
60340          * @param {Number} rowIndex
60341          * @param {Number} columnIndex
60342          * @param {Roo.EventObject} e
60343          */
60344         "cellclick" : true,
60345         /**
60346          * @event celldblclick
60347          * Fires when a cell is double clicked
60348          * @param {Grid} this
60349          * @param {Number} rowIndex
60350          * @param {Number} columnIndex
60351          * @param {Roo.EventObject} e
60352          */
60353         "celldblclick" : true,
60354         /**
60355          * @event rowclick
60356          * Fires when a row is clicked
60357          * @param {Grid} this
60358          * @param {Number} rowIndex
60359          * @param {Roo.EventObject} e
60360          */
60361         "rowclick" : true,
60362         /**
60363          * @event rowdblclick
60364          * Fires when a row is double clicked
60365          * @param {Grid} this
60366          * @param {Number} rowIndex
60367          * @param {Roo.EventObject} e
60368          */
60369         "rowdblclick" : true,
60370         /**
60371          * @event headerclick
60372          * Fires when a header is clicked
60373          * @param {Grid} this
60374          * @param {Number} columnIndex
60375          * @param {Roo.EventObject} e
60376          */
60377         "headerclick" : true,
60378         /**
60379          * @event headerdblclick
60380          * Fires when a header cell is double clicked
60381          * @param {Grid} this
60382          * @param {Number} columnIndex
60383          * @param {Roo.EventObject} e
60384          */
60385         "headerdblclick" : true,
60386         /**
60387          * @event rowcontextmenu
60388          * Fires when a row is right clicked
60389          * @param {Grid} this
60390          * @param {Number} rowIndex
60391          * @param {Roo.EventObject} e
60392          */
60393         "rowcontextmenu" : true,
60394         /**
60395          * @event cellcontextmenu
60396          * Fires when a cell is right clicked
60397          * @param {Grid} this
60398          * @param {Number} rowIndex
60399          * @param {Number} cellIndex
60400          * @param {Roo.EventObject} e
60401          */
60402          "cellcontextmenu" : true,
60403         /**
60404          * @event headercontextmenu
60405          * Fires when a header is right clicked
60406          * @param {Grid} this
60407          * @param {Number} columnIndex
60408          * @param {Roo.EventObject} e
60409          */
60410         "headercontextmenu" : true,
60411         /**
60412          * @event bodyscroll
60413          * Fires when the body element is scrolled
60414          * @param {Number} scrollLeft
60415          * @param {Number} scrollTop
60416          */
60417         "bodyscroll" : true,
60418         /**
60419          * @event columnresize
60420          * Fires when the user resizes a column
60421          * @param {Number} columnIndex
60422          * @param {Number} newSize
60423          */
60424         "columnresize" : true,
60425         /**
60426          * @event columnmove
60427          * Fires when the user moves a column
60428          * @param {Number} oldIndex
60429          * @param {Number} newIndex
60430          */
60431         "columnmove" : true,
60432         /**
60433          * @event startdrag
60434          * Fires when row(s) start being dragged
60435          * @param {Grid} this
60436          * @param {Roo.GridDD} dd The drag drop object
60437          * @param {event} e The raw browser event
60438          */
60439         "startdrag" : true,
60440         /**
60441          * @event enddrag
60442          * Fires when a drag operation is complete
60443          * @param {Grid} this
60444          * @param {Roo.GridDD} dd The drag drop object
60445          * @param {event} e The raw browser event
60446          */
60447         "enddrag" : true,
60448         /**
60449          * @event dragdrop
60450          * Fires when dragged row(s) are dropped on a valid DD target
60451          * @param {Grid} this
60452          * @param {Roo.GridDD} dd The drag drop object
60453          * @param {String} targetId The target drag drop object
60454          * @param {event} e The raw browser event
60455          */
60456         "dragdrop" : true,
60457         /**
60458          * @event dragover
60459          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60460          * @param {Grid} this
60461          * @param {Roo.GridDD} dd The drag drop object
60462          * @param {String} targetId The target drag drop object
60463          * @param {event} e The raw browser event
60464          */
60465         "dragover" : true,
60466         /**
60467          * @event dragenter
60468          *  Fires when the dragged row(s) first cross another DD target while being dragged
60469          * @param {Grid} this
60470          * @param {Roo.GridDD} dd The drag drop object
60471          * @param {String} targetId The target drag drop object
60472          * @param {event} e The raw browser event
60473          */
60474         "dragenter" : true,
60475         /**
60476          * @event dragout
60477          * Fires when the dragged row(s) leave another DD target while being dragged
60478          * @param {Grid} this
60479          * @param {Roo.GridDD} dd The drag drop object
60480          * @param {String} targetId The target drag drop object
60481          * @param {event} e The raw browser event
60482          */
60483         "dragout" : true,
60484         /**
60485          * @event rowclass
60486          * Fires when a row is rendered, so you can change add a style to it.
60487          * @param {GridView} gridview   The grid view
60488          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60489          */
60490         'rowclass' : true,
60491
60492         /**
60493          * @event render
60494          * Fires when the grid is rendered
60495          * @param {Grid} grid
60496          */
60497         'render' : true,
60498             /**
60499              * @event select
60500              * Fires when a date is selected
60501              * @param {DatePicker} this
60502              * @param {Date} date The selected date
60503              */
60504         'select': true,
60505         /**
60506              * @event monthchange
60507              * Fires when the displayed month changes 
60508              * @param {DatePicker} this
60509              * @param {Date} date The selected month
60510              */
60511         'monthchange': true,
60512         /**
60513              * @event evententer
60514              * Fires when mouse over an event
60515              * @param {Calendar} this
60516              * @param {event} Event
60517              */
60518         'evententer': true,
60519         /**
60520              * @event eventleave
60521              * Fires when the mouse leaves an
60522              * @param {Calendar} this
60523              * @param {event}
60524              */
60525         'eventleave': true,
60526         /**
60527              * @event eventclick
60528              * Fires when the mouse click an
60529              * @param {Calendar} this
60530              * @param {event}
60531              */
60532         'eventclick': true,
60533         /**
60534              * @event eventrender
60535              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60536              * @param {Calendar} this
60537              * @param {data} data to be modified
60538              */
60539         'eventrender': true
60540         
60541     });
60542
60543     Roo.grid.Grid.superclass.constructor.call(this);
60544     this.on('render', function() {
60545         this.view.el.addClass('x-grid-cal'); 
60546         
60547         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60548
60549     },this);
60550     
60551     if (!Roo.grid.Calendar.style) {
60552         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60553             
60554             
60555             '.x-grid-cal .x-grid-col' :  {
60556                 height: 'auto !important',
60557                 'vertical-align': 'top'
60558             },
60559             '.x-grid-cal  .fc-event-hori' : {
60560                 height: '14px'
60561             }
60562              
60563             
60564         }, Roo.id());
60565     }
60566
60567     
60568     
60569 };
60570 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60571     /**
60572      * @cfg {Store} eventStore The store that loads events.
60573      */
60574     eventStore : 25,
60575
60576      
60577     activeDate : false,
60578     startDay : 0,
60579     autoWidth : true,
60580     monitorWindowResize : false,
60581
60582     
60583     resizeColumns : function() {
60584         var col = (this.view.el.getWidth() / 7) - 3;
60585         // loop through cols, and setWidth
60586         for(var i =0 ; i < 7 ; i++){
60587             this.cm.setColumnWidth(i, col);
60588         }
60589     },
60590      setDate :function(date) {
60591         
60592         Roo.log('setDate?');
60593         
60594         this.resizeColumns();
60595         var vd = this.activeDate;
60596         this.activeDate = date;
60597 //        if(vd && this.el){
60598 //            var t = date.getTime();
60599 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60600 //                Roo.log('using add remove');
60601 //                
60602 //                this.fireEvent('monthchange', this, date);
60603 //                
60604 //                this.cells.removeClass("fc-state-highlight");
60605 //                this.cells.each(function(c){
60606 //                   if(c.dateValue == t){
60607 //                       c.addClass("fc-state-highlight");
60608 //                       setTimeout(function(){
60609 //                            try{c.dom.firstChild.focus();}catch(e){}
60610 //                       }, 50);
60611 //                       return false;
60612 //                   }
60613 //                   return true;
60614 //                });
60615 //                return;
60616 //            }
60617 //        }
60618         
60619         var days = date.getDaysInMonth();
60620         
60621         var firstOfMonth = date.getFirstDateOfMonth();
60622         var startingPos = firstOfMonth.getDay()-this.startDay;
60623         
60624         if(startingPos < this.startDay){
60625             startingPos += 7;
60626         }
60627         
60628         var pm = date.add(Date.MONTH, -1);
60629         var prevStart = pm.getDaysInMonth()-startingPos;
60630 //        
60631         
60632         
60633         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60634         
60635         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60636         //this.cells.addClassOnOver('fc-state-hover');
60637         
60638         var cells = this.cells.elements;
60639         var textEls = this.textNodes;
60640         
60641         //Roo.each(cells, function(cell){
60642         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60643         //});
60644         
60645         days += startingPos;
60646
60647         // convert everything to numbers so it's fast
60648         var day = 86400000;
60649         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60650         //Roo.log(d);
60651         //Roo.log(pm);
60652         //Roo.log(prevStart);
60653         
60654         var today = new Date().clearTime().getTime();
60655         var sel = date.clearTime().getTime();
60656         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60657         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60658         var ddMatch = this.disabledDatesRE;
60659         var ddText = this.disabledDatesText;
60660         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60661         var ddaysText = this.disabledDaysText;
60662         var format = this.format;
60663         
60664         var setCellClass = function(cal, cell){
60665             
60666             //Roo.log('set Cell Class');
60667             cell.title = "";
60668             var t = d.getTime();
60669             
60670             //Roo.log(d);
60671             
60672             
60673             cell.dateValue = t;
60674             if(t == today){
60675                 cell.className += " fc-today";
60676                 cell.className += " fc-state-highlight";
60677                 cell.title = cal.todayText;
60678             }
60679             if(t == sel){
60680                 // disable highlight in other month..
60681                 cell.className += " fc-state-highlight";
60682                 
60683             }
60684             // disabling
60685             if(t < min) {
60686                 //cell.className = " fc-state-disabled";
60687                 cell.title = cal.minText;
60688                 return;
60689             }
60690             if(t > max) {
60691                 //cell.className = " fc-state-disabled";
60692                 cell.title = cal.maxText;
60693                 return;
60694             }
60695             if(ddays){
60696                 if(ddays.indexOf(d.getDay()) != -1){
60697                     // cell.title = ddaysText;
60698                    // cell.className = " fc-state-disabled";
60699                 }
60700             }
60701             if(ddMatch && format){
60702                 var fvalue = d.dateFormat(format);
60703                 if(ddMatch.test(fvalue)){
60704                     cell.title = ddText.replace("%0", fvalue);
60705                    cell.className = " fc-state-disabled";
60706                 }
60707             }
60708             
60709             if (!cell.initialClassName) {
60710                 cell.initialClassName = cell.dom.className;
60711             }
60712             
60713             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60714         };
60715
60716         var i = 0;
60717         
60718         for(; i < startingPos; i++) {
60719             cells[i].dayName =  (++prevStart);
60720             Roo.log(textEls[i]);
60721             d.setDate(d.getDate()+1);
60722             
60723             //cells[i].className = "fc-past fc-other-month";
60724             setCellClass(this, cells[i]);
60725         }
60726         
60727         var intDay = 0;
60728         
60729         for(; i < days; i++){
60730             intDay = i - startingPos + 1;
60731             cells[i].dayName =  (intDay);
60732             d.setDate(d.getDate()+1);
60733             
60734             cells[i].className = ''; // "x-date-active";
60735             setCellClass(this, cells[i]);
60736         }
60737         var extraDays = 0;
60738         
60739         for(; i < 42; i++) {
60740             //textEls[i].innerHTML = (++extraDays);
60741             
60742             d.setDate(d.getDate()+1);
60743             cells[i].dayName = (++extraDays);
60744             cells[i].className = "fc-future fc-other-month";
60745             setCellClass(this, cells[i]);
60746         }
60747         
60748         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60749         
60750         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60751         
60752         // this will cause all the cells to mis
60753         var rows= [];
60754         var i =0;
60755         for (var r = 0;r < 6;r++) {
60756             for (var c =0;c < 7;c++) {
60757                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60758             }    
60759         }
60760         
60761         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60762         for(i=0;i<cells.length;i++) {
60763             
60764             this.cells.elements[i].dayName = cells[i].dayName ;
60765             this.cells.elements[i].className = cells[i].className;
60766             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60767             this.cells.elements[i].title = cells[i].title ;
60768             this.cells.elements[i].dateValue = cells[i].dateValue ;
60769         }
60770         
60771         
60772         
60773         
60774         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60775         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60776         
60777         ////if(totalRows != 6){
60778             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60779            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60780        // }
60781         
60782         this.fireEvent('monthchange', this, date);
60783         
60784         
60785     },
60786  /**
60787      * Returns the grid's SelectionModel.
60788      * @return {SelectionModel}
60789      */
60790     getSelectionModel : function(){
60791         if(!this.selModel){
60792             this.selModel = new Roo.grid.CellSelectionModel();
60793         }
60794         return this.selModel;
60795     },
60796
60797     load: function() {
60798         this.eventStore.load()
60799         
60800         
60801         
60802     },
60803     
60804     findCell : function(dt) {
60805         dt = dt.clearTime().getTime();
60806         var ret = false;
60807         this.cells.each(function(c){
60808             //Roo.log("check " +c.dateValue + '?=' + dt);
60809             if(c.dateValue == dt){
60810                 ret = c;
60811                 return false;
60812             }
60813             return true;
60814         });
60815         
60816         return ret;
60817     },
60818     
60819     findCells : function(rec) {
60820         var s = rec.data.start_dt.clone().clearTime().getTime();
60821        // Roo.log(s);
60822         var e= rec.data.end_dt.clone().clearTime().getTime();
60823        // Roo.log(e);
60824         var ret = [];
60825         this.cells.each(function(c){
60826              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60827             
60828             if(c.dateValue > e){
60829                 return ;
60830             }
60831             if(c.dateValue < s){
60832                 return ;
60833             }
60834             ret.push(c);
60835         });
60836         
60837         return ret;    
60838     },
60839     
60840     findBestRow: function(cells)
60841     {
60842         var ret = 0;
60843         
60844         for (var i =0 ; i < cells.length;i++) {
60845             ret  = Math.max(cells[i].rows || 0,ret);
60846         }
60847         return ret;
60848         
60849     },
60850     
60851     
60852     addItem : function(rec)
60853     {
60854         // look for vertical location slot in
60855         var cells = this.findCells(rec);
60856         
60857         rec.row = this.findBestRow(cells);
60858         
60859         // work out the location.
60860         
60861         var crow = false;
60862         var rows = [];
60863         for(var i =0; i < cells.length; i++) {
60864             if (!crow) {
60865                 crow = {
60866                     start : cells[i],
60867                     end :  cells[i]
60868                 };
60869                 continue;
60870             }
60871             if (crow.start.getY() == cells[i].getY()) {
60872                 // on same row.
60873                 crow.end = cells[i];
60874                 continue;
60875             }
60876             // different row.
60877             rows.push(crow);
60878             crow = {
60879                 start: cells[i],
60880                 end : cells[i]
60881             };
60882             
60883         }
60884         
60885         rows.push(crow);
60886         rec.els = [];
60887         rec.rows = rows;
60888         rec.cells = cells;
60889         for (var i = 0; i < cells.length;i++) {
60890             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60891             
60892         }
60893         
60894         
60895     },
60896     
60897     clearEvents: function() {
60898         
60899         if (!this.eventStore.getCount()) {
60900             return;
60901         }
60902         // reset number of rows in cells.
60903         Roo.each(this.cells.elements, function(c){
60904             c.rows = 0;
60905         });
60906         
60907         this.eventStore.each(function(e) {
60908             this.clearEvent(e);
60909         },this);
60910         
60911     },
60912     
60913     clearEvent : function(ev)
60914     {
60915         if (ev.els) {
60916             Roo.each(ev.els, function(el) {
60917                 el.un('mouseenter' ,this.onEventEnter, this);
60918                 el.un('mouseleave' ,this.onEventLeave, this);
60919                 el.remove();
60920             },this);
60921             ev.els = [];
60922         }
60923     },
60924     
60925     
60926     renderEvent : function(ev,ctr) {
60927         if (!ctr) {
60928              ctr = this.view.el.select('.fc-event-container',true).first();
60929         }
60930         
60931          
60932         this.clearEvent(ev);
60933             //code
60934        
60935         
60936         
60937         ev.els = [];
60938         var cells = ev.cells;
60939         var rows = ev.rows;
60940         this.fireEvent('eventrender', this, ev);
60941         
60942         for(var i =0; i < rows.length; i++) {
60943             
60944             cls = '';
60945             if (i == 0) {
60946                 cls += ' fc-event-start';
60947             }
60948             if ((i+1) == rows.length) {
60949                 cls += ' fc-event-end';
60950             }
60951             
60952             //Roo.log(ev.data);
60953             // how many rows should it span..
60954             var cg = this.eventTmpl.append(ctr,Roo.apply({
60955                 fccls : cls
60956                 
60957             }, ev.data) , true);
60958             
60959             
60960             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60961             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60962             cg.on('click', this.onEventClick, this, ev);
60963             
60964             ev.els.push(cg);
60965             
60966             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60967             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60968             //Roo.log(cg);
60969              
60970             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60971             cg.setWidth(ebox.right - sbox.x -2);
60972         }
60973     },
60974     
60975     renderEvents: function()
60976     {   
60977         // first make sure there is enough space..
60978         
60979         if (!this.eventTmpl) {
60980             this.eventTmpl = new Roo.Template(
60981                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60982                     '<div class="fc-event-inner">' +
60983                         '<span class="fc-event-time">{time}</span>' +
60984                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60985                     '</div>' +
60986                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60987                 '</div>'
60988             );
60989                 
60990         }
60991                
60992         
60993         
60994         this.cells.each(function(c) {
60995             //Roo.log(c.select('.fc-day-content div',true).first());
60996             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60997         });
60998         
60999         var ctr = this.view.el.select('.fc-event-container',true).first();
61000         
61001         var cls;
61002         this.eventStore.each(function(ev){
61003             
61004             this.renderEvent(ev);
61005              
61006              
61007         }, this);
61008         this.view.layout();
61009         
61010     },
61011     
61012     onEventEnter: function (e, el,event,d) {
61013         this.fireEvent('evententer', this, el, event);
61014     },
61015     
61016     onEventLeave: function (e, el,event,d) {
61017         this.fireEvent('eventleave', this, el, event);
61018     },
61019     
61020     onEventClick: function (e, el,event,d) {
61021         this.fireEvent('eventclick', this, el, event);
61022     },
61023     
61024     onMonthChange: function () {
61025         this.store.load();
61026     },
61027     
61028     onLoad: function () {
61029         
61030         //Roo.log('calendar onload');
61031 //         
61032         if(this.eventStore.getCount() > 0){
61033             
61034            
61035             
61036             this.eventStore.each(function(d){
61037                 
61038                 
61039                 // FIXME..
61040                 var add =   d.data;
61041                 if (typeof(add.end_dt) == 'undefined')  {
61042                     Roo.log("Missing End time in calendar data: ");
61043                     Roo.log(d);
61044                     return;
61045                 }
61046                 if (typeof(add.start_dt) == 'undefined')  {
61047                     Roo.log("Missing Start time in calendar data: ");
61048                     Roo.log(d);
61049                     return;
61050                 }
61051                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61052                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61053                 add.id = add.id || d.id;
61054                 add.title = add.title || '??';
61055                 
61056                 this.addItem(d);
61057                 
61058              
61059             },this);
61060         }
61061         
61062         this.renderEvents();
61063     }
61064     
61065
61066 });
61067 /*
61068  grid : {
61069                 xtype: 'Grid',
61070                 xns: Roo.grid,
61071                 listeners : {
61072                     render : function ()
61073                     {
61074                         _this.grid = this;
61075                         
61076                         if (!this.view.el.hasClass('course-timesheet')) {
61077                             this.view.el.addClass('course-timesheet');
61078                         }
61079                         if (this.tsStyle) {
61080                             this.ds.load({});
61081                             return; 
61082                         }
61083                         Roo.log('width');
61084                         Roo.log(_this.grid.view.el.getWidth());
61085                         
61086                         
61087                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61088                             '.course-timesheet .x-grid-row' : {
61089                                 height: '80px'
61090                             },
61091                             '.x-grid-row td' : {
61092                                 'vertical-align' : 0
61093                             },
61094                             '.course-edit-link' : {
61095                                 'color' : 'blue',
61096                                 'text-overflow' : 'ellipsis',
61097                                 'overflow' : 'hidden',
61098                                 'white-space' : 'nowrap',
61099                                 'cursor' : 'pointer'
61100                             },
61101                             '.sub-link' : {
61102                                 'color' : 'green'
61103                             },
61104                             '.de-act-sup-link' : {
61105                                 'color' : 'purple',
61106                                 'text-decoration' : 'line-through'
61107                             },
61108                             '.de-act-link' : {
61109                                 'color' : 'red',
61110                                 'text-decoration' : 'line-through'
61111                             },
61112                             '.course-timesheet .course-highlight' : {
61113                                 'border-top-style': 'dashed !important',
61114                                 'border-bottom-bottom': 'dashed !important'
61115                             },
61116                             '.course-timesheet .course-item' : {
61117                                 'font-family'   : 'tahoma, arial, helvetica',
61118                                 'font-size'     : '11px',
61119                                 'overflow'      : 'hidden',
61120                                 'padding-left'  : '10px',
61121                                 'padding-right' : '10px',
61122                                 'padding-top' : '10px' 
61123                             }
61124                             
61125                         }, Roo.id());
61126                                 this.ds.load({});
61127                     }
61128                 },
61129                 autoWidth : true,
61130                 monitorWindowResize : false,
61131                 cellrenderer : function(v,x,r)
61132                 {
61133                     return v;
61134                 },
61135                 sm : {
61136                     xtype: 'CellSelectionModel',
61137                     xns: Roo.grid
61138                 },
61139                 dataSource : {
61140                     xtype: 'Store',
61141                     xns: Roo.data,
61142                     listeners : {
61143                         beforeload : function (_self, options)
61144                         {
61145                             options.params = options.params || {};
61146                             options.params._month = _this.monthField.getValue();
61147                             options.params.limit = 9999;
61148                             options.params['sort'] = 'when_dt';    
61149                             options.params['dir'] = 'ASC';    
61150                             this.proxy.loadResponse = this.loadResponse;
61151                             Roo.log("load?");
61152                             //this.addColumns();
61153                         },
61154                         load : function (_self, records, options)
61155                         {
61156                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61157                                 // if you click on the translation.. you can edit it...
61158                                 var el = Roo.get(this);
61159                                 var id = el.dom.getAttribute('data-id');
61160                                 var d = el.dom.getAttribute('data-date');
61161                                 var t = el.dom.getAttribute('data-time');
61162                                 //var id = this.child('span').dom.textContent;
61163                                 
61164                                 //Roo.log(this);
61165                                 Pman.Dialog.CourseCalendar.show({
61166                                     id : id,
61167                                     when_d : d,
61168                                     when_t : t,
61169                                     productitem_active : id ? 1 : 0
61170                                 }, function() {
61171                                     _this.grid.ds.load({});
61172                                 });
61173                            
61174                            });
61175                            
61176                            _this.panel.fireEvent('resize', [ '', '' ]);
61177                         }
61178                     },
61179                     loadResponse : function(o, success, response){
61180                             // this is overridden on before load..
61181                             
61182                             Roo.log("our code?");       
61183                             //Roo.log(success);
61184                             //Roo.log(response)
61185                             delete this.activeRequest;
61186                             if(!success){
61187                                 this.fireEvent("loadexception", this, o, response);
61188                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61189                                 return;
61190                             }
61191                             var result;
61192                             try {
61193                                 result = o.reader.read(response);
61194                             }catch(e){
61195                                 Roo.log("load exception?");
61196                                 this.fireEvent("loadexception", this, o, response, e);
61197                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61198                                 return;
61199                             }
61200                             Roo.log("ready...");        
61201                             // loop through result.records;
61202                             // and set this.tdate[date] = [] << array of records..
61203                             _this.tdata  = {};
61204                             Roo.each(result.records, function(r){
61205                                 //Roo.log(r.data);
61206                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61207                                     _this.tdata[r.data.when_dt.format('j')] = [];
61208                                 }
61209                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61210                             });
61211                             
61212                             //Roo.log(_this.tdata);
61213                             
61214                             result.records = [];
61215                             result.totalRecords = 6;
61216                     
61217                             // let's generate some duumy records for the rows.
61218                             //var st = _this.dateField.getValue();
61219                             
61220                             // work out monday..
61221                             //st = st.add(Date.DAY, -1 * st.format('w'));
61222                             
61223                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61224                             
61225                             var firstOfMonth = date.getFirstDayOfMonth();
61226                             var days = date.getDaysInMonth();
61227                             var d = 1;
61228                             var firstAdded = false;
61229                             for (var i = 0; i < result.totalRecords ; i++) {
61230                                 //var d= st.add(Date.DAY, i);
61231                                 var row = {};
61232                                 var added = 0;
61233                                 for(var w = 0 ; w < 7 ; w++){
61234                                     if(!firstAdded && firstOfMonth != w){
61235                                         continue;
61236                                     }
61237                                     if(d > days){
61238                                         continue;
61239                                     }
61240                                     firstAdded = true;
61241                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61242                                     row['weekday'+w] = String.format(
61243                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61244                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61245                                                     d,
61246                                                     date.format('Y-m-')+dd
61247                                                 );
61248                                     added++;
61249                                     if(typeof(_this.tdata[d]) != 'undefined'){
61250                                         Roo.each(_this.tdata[d], function(r){
61251                                             var is_sub = '';
61252                                             var deactive = '';
61253                                             var id = r.id;
61254                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61255                                             if(r.parent_id*1>0){
61256                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61257                                                 id = r.parent_id;
61258                                             }
61259                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61260                                                 deactive = 'de-act-link';
61261                                             }
61262                                             
61263                                             row['weekday'+w] += String.format(
61264                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61265                                                     id, //0
61266                                                     r.product_id_name, //1
61267                                                     r.when_dt.format('h:ia'), //2
61268                                                     is_sub, //3
61269                                                     deactive, //4
61270                                                     desc // 5
61271                                             );
61272                                         });
61273                                     }
61274                                     d++;
61275                                 }
61276                                 
61277                                 // only do this if something added..
61278                                 if(added > 0){ 
61279                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61280                                 }
61281                                 
61282                                 
61283                                 // push it twice. (second one with an hour..
61284                                 
61285                             }
61286                             //Roo.log(result);
61287                             this.fireEvent("load", this, o, o.request.arg);
61288                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61289                         },
61290                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61291                     proxy : {
61292                         xtype: 'HttpProxy',
61293                         xns: Roo.data,
61294                         method : 'GET',
61295                         url : baseURL + '/Roo/Shop_course.php'
61296                     },
61297                     reader : {
61298                         xtype: 'JsonReader',
61299                         xns: Roo.data,
61300                         id : 'id',
61301                         fields : [
61302                             {
61303                                 'name': 'id',
61304                                 'type': 'int'
61305                             },
61306                             {
61307                                 'name': 'when_dt',
61308                                 'type': 'string'
61309                             },
61310                             {
61311                                 'name': 'end_dt',
61312                                 'type': 'string'
61313                             },
61314                             {
61315                                 'name': 'parent_id',
61316                                 'type': 'int'
61317                             },
61318                             {
61319                                 'name': 'product_id',
61320                                 'type': 'int'
61321                             },
61322                             {
61323                                 'name': 'productitem_id',
61324                                 'type': 'int'
61325                             },
61326                             {
61327                                 'name': 'guid',
61328                                 'type': 'int'
61329                             }
61330                         ]
61331                     }
61332                 },
61333                 toolbar : {
61334                     xtype: 'Toolbar',
61335                     xns: Roo,
61336                     items : [
61337                         {
61338                             xtype: 'Button',
61339                             xns: Roo.Toolbar,
61340                             listeners : {
61341                                 click : function (_self, e)
61342                                 {
61343                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61344                                     sd.setMonth(sd.getMonth()-1);
61345                                     _this.monthField.setValue(sd.format('Y-m-d'));
61346                                     _this.grid.ds.load({});
61347                                 }
61348                             },
61349                             text : "Back"
61350                         },
61351                         {
61352                             xtype: 'Separator',
61353                             xns: Roo.Toolbar
61354                         },
61355                         {
61356                             xtype: 'MonthField',
61357                             xns: Roo.form,
61358                             listeners : {
61359                                 render : function (_self)
61360                                 {
61361                                     _this.monthField = _self;
61362                                    // _this.monthField.set  today
61363                                 },
61364                                 select : function (combo, date)
61365                                 {
61366                                     _this.grid.ds.load({});
61367                                 }
61368                             },
61369                             value : (function() { return new Date(); })()
61370                         },
61371                         {
61372                             xtype: 'Separator',
61373                             xns: Roo.Toolbar
61374                         },
61375                         {
61376                             xtype: 'TextItem',
61377                             xns: Roo.Toolbar,
61378                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61379                         },
61380                         {
61381                             xtype: 'Fill',
61382                             xns: Roo.Toolbar
61383                         },
61384                         {
61385                             xtype: 'Button',
61386                             xns: Roo.Toolbar,
61387                             listeners : {
61388                                 click : function (_self, e)
61389                                 {
61390                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61391                                     sd.setMonth(sd.getMonth()+1);
61392                                     _this.monthField.setValue(sd.format('Y-m-d'));
61393                                     _this.grid.ds.load({});
61394                                 }
61395                             },
61396                             text : "Next"
61397                         }
61398                     ]
61399                 },
61400                  
61401             }
61402         };
61403         
61404         *//*
61405  * Based on:
61406  * Ext JS Library 1.1.1
61407  * Copyright(c) 2006-2007, Ext JS, LLC.
61408  *
61409  * Originally Released Under LGPL - original licence link has changed is not relivant.
61410  *
61411  * Fork - LGPL
61412  * <script type="text/javascript">
61413  */
61414  
61415 /**
61416  * @class Roo.LoadMask
61417  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61418  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61419  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61420  * element's UpdateManager load indicator and will be destroyed after the initial load.
61421  * @constructor
61422  * Create a new LoadMask
61423  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61424  * @param {Object} config The config object
61425  */
61426 Roo.LoadMask = function(el, config){
61427     this.el = Roo.get(el);
61428     Roo.apply(this, config);
61429     if(this.store){
61430         this.store.on('beforeload', this.onBeforeLoad, this);
61431         this.store.on('load', this.onLoad, this);
61432         this.store.on('loadexception', this.onLoadException, this);
61433         this.removeMask = false;
61434     }else{
61435         var um = this.el.getUpdateManager();
61436         um.showLoadIndicator = false; // disable the default indicator
61437         um.on('beforeupdate', this.onBeforeLoad, this);
61438         um.on('update', this.onLoad, this);
61439         um.on('failure', this.onLoad, this);
61440         this.removeMask = true;
61441     }
61442 };
61443
61444 Roo.LoadMask.prototype = {
61445     /**
61446      * @cfg {Boolean} removeMask
61447      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61448      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61449      */
61450     /**
61451      * @cfg {String} msg
61452      * The text to display in a centered loading message box (defaults to 'Loading...')
61453      */
61454     msg : 'Loading...',
61455     /**
61456      * @cfg {String} msgCls
61457      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61458      */
61459     msgCls : 'x-mask-loading',
61460
61461     /**
61462      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61463      * @type Boolean
61464      */
61465     disabled: false,
61466
61467     /**
61468      * Disables the mask to prevent it from being displayed
61469      */
61470     disable : function(){
61471        this.disabled = true;
61472     },
61473
61474     /**
61475      * Enables the mask so that it can be displayed
61476      */
61477     enable : function(){
61478         this.disabled = false;
61479     },
61480     
61481     onLoadException : function()
61482     {
61483         Roo.log(arguments);
61484         
61485         if (typeof(arguments[3]) != 'undefined') {
61486             Roo.MessageBox.alert("Error loading",arguments[3]);
61487         } 
61488         /*
61489         try {
61490             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61491                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61492             }   
61493         } catch(e) {
61494             
61495         }
61496         */
61497     
61498         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61499     },
61500     // private
61501     onLoad : function()
61502     {
61503         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61504     },
61505
61506     // private
61507     onBeforeLoad : function(){
61508         if(!this.disabled){
61509             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61510         }
61511     },
61512
61513     // private
61514     destroy : function(){
61515         if(this.store){
61516             this.store.un('beforeload', this.onBeforeLoad, this);
61517             this.store.un('load', this.onLoad, this);
61518             this.store.un('loadexception', this.onLoadException, this);
61519         }else{
61520             var um = this.el.getUpdateManager();
61521             um.un('beforeupdate', this.onBeforeLoad, this);
61522             um.un('update', this.onLoad, this);
61523             um.un('failure', this.onLoad, this);
61524         }
61525     }
61526 };/*
61527  * Based on:
61528  * Ext JS Library 1.1.1
61529  * Copyright(c) 2006-2007, Ext JS, LLC.
61530  *
61531  * Originally Released Under LGPL - original licence link has changed is not relivant.
61532  *
61533  * Fork - LGPL
61534  * <script type="text/javascript">
61535  */
61536
61537
61538 /**
61539  * @class Roo.XTemplate
61540  * @extends Roo.Template
61541  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61542 <pre><code>
61543 var t = new Roo.XTemplate(
61544         '&lt;select name="{name}"&gt;',
61545                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61546         '&lt;/select&gt;'
61547 );
61548  
61549 // then append, applying the master template values
61550  </code></pre>
61551  *
61552  * Supported features:
61553  *
61554  *  Tags:
61555
61556 <pre><code>
61557       {a_variable} - output encoded.
61558       {a_variable.format:("Y-m-d")} - call a method on the variable
61559       {a_variable:raw} - unencoded output
61560       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61561       {a_variable:this.method_on_template(...)} - call a method on the template object.
61562  
61563 </code></pre>
61564  *  The tpl tag:
61565 <pre><code>
61566         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61567         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61568         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61569         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61570   
61571         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61572         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61573 </code></pre>
61574  *      
61575  */
61576 Roo.XTemplate = function()
61577 {
61578     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61579     if (this.html) {
61580         this.compile();
61581     }
61582 };
61583
61584
61585 Roo.extend(Roo.XTemplate, Roo.Template, {
61586
61587     /**
61588      * The various sub templates
61589      */
61590     tpls : false,
61591     /**
61592      *
61593      * basic tag replacing syntax
61594      * WORD:WORD()
61595      *
61596      * // you can fake an object call by doing this
61597      *  x.t:(test,tesT) 
61598      * 
61599      */
61600     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61601
61602     /**
61603      * compile the template
61604      *
61605      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61606      *
61607      */
61608     compile: function()
61609     {
61610         var s = this.html;
61611      
61612         s = ['<tpl>', s, '</tpl>'].join('');
61613     
61614         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61615             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61616             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61617             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61618             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61619             m,
61620             id     = 0,
61621             tpls   = [];
61622     
61623         while(true == !!(m = s.match(re))){
61624             var forMatch   = m[0].match(nameRe),
61625                 ifMatch   = m[0].match(ifRe),
61626                 execMatch   = m[0].match(execRe),
61627                 namedMatch   = m[0].match(namedRe),
61628                 
61629                 exp  = null, 
61630                 fn   = null,
61631                 exec = null,
61632                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61633                 
61634             if (ifMatch) {
61635                 // if - puts fn into test..
61636                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61637                 if(exp){
61638                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61639                 }
61640             }
61641             
61642             if (execMatch) {
61643                 // exec - calls a function... returns empty if true is  returned.
61644                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61645                 if(exp){
61646                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61647                 }
61648             }
61649             
61650             
61651             if (name) {
61652                 // for = 
61653                 switch(name){
61654                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61655                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61656                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61657                 }
61658             }
61659             var uid = namedMatch ? namedMatch[1] : id;
61660             
61661             
61662             tpls.push({
61663                 id:     namedMatch ? namedMatch[1] : id,
61664                 target: name,
61665                 exec:   exec,
61666                 test:   fn,
61667                 body:   m[1] || ''
61668             });
61669             if (namedMatch) {
61670                 s = s.replace(m[0], '');
61671             } else { 
61672                 s = s.replace(m[0], '{xtpl'+ id + '}');
61673             }
61674             ++id;
61675         }
61676         this.tpls = [];
61677         for(var i = tpls.length-1; i >= 0; --i){
61678             this.compileTpl(tpls[i]);
61679             this.tpls[tpls[i].id] = tpls[i];
61680         }
61681         this.master = tpls[tpls.length-1];
61682         return this;
61683     },
61684     /**
61685      * same as applyTemplate, except it's done to one of the subTemplates
61686      * when using named templates, you can do:
61687      *
61688      * var str = pl.applySubTemplate('your-name', values);
61689      *
61690      * 
61691      * @param {Number} id of the template
61692      * @param {Object} values to apply to template
61693      * @param {Object} parent (normaly the instance of this object)
61694      */
61695     applySubTemplate : function(id, values, parent)
61696     {
61697         
61698         
61699         var t = this.tpls[id];
61700         
61701         
61702         try { 
61703             if(t.test && !t.test.call(this, values, parent)){
61704                 return '';
61705             }
61706         } catch(e) {
61707             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61708             Roo.log(e.toString());
61709             Roo.log(t.test);
61710             return ''
61711         }
61712         try { 
61713             
61714             if(t.exec && t.exec.call(this, values, parent)){
61715                 return '';
61716             }
61717         } catch(e) {
61718             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61719             Roo.log(e.toString());
61720             Roo.log(t.exec);
61721             return ''
61722         }
61723         try {
61724             var vs = t.target ? t.target.call(this, values, parent) : values;
61725             parent = t.target ? values : parent;
61726             if(t.target && vs instanceof Array){
61727                 var buf = [];
61728                 for(var i = 0, len = vs.length; i < len; i++){
61729                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61730                 }
61731                 return buf.join('');
61732             }
61733             return t.compiled.call(this, vs, parent);
61734         } catch (e) {
61735             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61736             Roo.log(e.toString());
61737             Roo.log(t.compiled);
61738             return '';
61739         }
61740     },
61741
61742     compileTpl : function(tpl)
61743     {
61744         var fm = Roo.util.Format;
61745         var useF = this.disableFormats !== true;
61746         var sep = Roo.isGecko ? "+" : ",";
61747         var undef = function(str) {
61748             Roo.log("Property not found :"  + str);
61749             return '';
61750         };
61751         
61752         var fn = function(m, name, format, args)
61753         {
61754             //Roo.log(arguments);
61755             args = args ? args.replace(/\\'/g,"'") : args;
61756             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61757             if (typeof(format) == 'undefined') {
61758                 format= 'htmlEncode';
61759             }
61760             if (format == 'raw' ) {
61761                 format = false;
61762             }
61763             
61764             if(name.substr(0, 4) == 'xtpl'){
61765                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61766             }
61767             
61768             // build an array of options to determine if value is undefined..
61769             
61770             // basically get 'xxxx.yyyy' then do
61771             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61772             //    (function () { Roo.log("Property not found"); return ''; })() :
61773             //    ......
61774             
61775             var udef_ar = [];
61776             var lookfor = '';
61777             Roo.each(name.split('.'), function(st) {
61778                 lookfor += (lookfor.length ? '.': '') + st;
61779                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61780             });
61781             
61782             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61783             
61784             
61785             if(format && useF){
61786                 
61787                 args = args ? ',' + args : "";
61788                  
61789                 if(format.substr(0, 5) != "this."){
61790                     format = "fm." + format + '(';
61791                 }else{
61792                     format = 'this.call("'+ format.substr(5) + '", ';
61793                     args = ", values";
61794                 }
61795                 
61796                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61797             }
61798              
61799             if (args.length) {
61800                 // called with xxyx.yuu:(test,test)
61801                 // change to ()
61802                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61803             }
61804             // raw.. - :raw modifier..
61805             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61806             
61807         };
61808         var body;
61809         // branched to use + in gecko and [].join() in others
61810         if(Roo.isGecko){
61811             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61812                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61813                     "';};};";
61814         }else{
61815             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61816             body.push(tpl.body.replace(/(\r\n|\n)/g,
61817                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61818             body.push("'].join('');};};");
61819             body = body.join('');
61820         }
61821         
61822         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61823        
61824         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61825         eval(body);
61826         
61827         return this;
61828     },
61829
61830     applyTemplate : function(values){
61831         return this.master.compiled.call(this, values, {});
61832         //var s = this.subs;
61833     },
61834
61835     apply : function(){
61836         return this.applyTemplate.apply(this, arguments);
61837     }
61838
61839  });
61840
61841 Roo.XTemplate.from = function(el){
61842     el = Roo.getDom(el);
61843     return new Roo.XTemplate(el.value || el.innerHTML);
61844 };