sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4915 <p>
4916 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4917
4918 <p>
4919 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 var args = Array.prototype.slice.call(arguments, 0);                
6078                 for(var i = 0; i < len; i++){
6079                     var l = ls[i];
6080                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6081                         this.firing = false;
6082                         return false;
6083                     }
6084                 }
6085                 this.firing = false;
6086             }
6087             return true;
6088         }
6089     };
6090 })();/*
6091  * RooJS Library 
6092  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6093  *
6094  * Licence LGPL 
6095  *
6096  */
6097  
6098 /**
6099  * @class Roo.Document
6100  * @extends Roo.util.Observable
6101  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6102  * 
6103  * @param {Object} config the methods and properties of the 'base' class for the application.
6104  * 
6105  *  Generic Page handler - implement this to start your app..
6106  * 
6107  * eg.
6108  *  MyProject = new Roo.Document({
6109         events : {
6110             'load' : true // your events..
6111         },
6112         listeners : {
6113             'ready' : function() {
6114                 // fired on Roo.onReady()
6115             }
6116         }
6117  * 
6118  */
6119 Roo.Document = function(cfg) {
6120      
6121     this.addEvents({ 
6122         'ready' : true
6123     });
6124     Roo.util.Observable.call(this,cfg);
6125     
6126     var _this = this;
6127     
6128     Roo.onReady(function() {
6129         _this.fireEvent('ready');
6130     },null,false);
6131     
6132     
6133 }
6134
6135 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145
6146 /**
6147  * @class Roo.EventManager
6148  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6149  * several useful events directly.
6150  * See {@link Roo.EventObject} for more details on normalized event objects.
6151  * @singleton
6152  */
6153 Roo.EventManager = function(){
6154     var docReadyEvent, docReadyProcId, docReadyState = false;
6155     var resizeEvent, resizeTask, textEvent, textSize;
6156     var E = Roo.lib.Event;
6157     var D = Roo.lib.Dom;
6158
6159     
6160     
6161
6162     var fireDocReady = function(){
6163         if(!docReadyState){
6164             docReadyState = true;
6165             Roo.isReady = true;
6166             if(docReadyProcId){
6167                 clearInterval(docReadyProcId);
6168             }
6169             if(Roo.isGecko || Roo.isOpera) {
6170                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6171             }
6172             if(Roo.isIE){
6173                 var defer = document.getElementById("ie-deferred-loader");
6174                 if(defer){
6175                     defer.onreadystatechange = null;
6176                     defer.parentNode.removeChild(defer);
6177                 }
6178             }
6179             if(docReadyEvent){
6180                 docReadyEvent.fire();
6181                 docReadyEvent.clearListeners();
6182             }
6183         }
6184     };
6185     
6186     var initDocReady = function(){
6187         docReadyEvent = new Roo.util.Event();
6188         if(Roo.isGecko || Roo.isOpera) {
6189             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6190         }else if(Roo.isIE){
6191             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6192             var defer = document.getElementById("ie-deferred-loader");
6193             defer.onreadystatechange = function(){
6194                 if(this.readyState == "complete"){
6195                     fireDocReady();
6196                 }
6197             };
6198         }else if(Roo.isSafari){ 
6199             docReadyProcId = setInterval(function(){
6200                 var rs = document.readyState;
6201                 if(rs == "complete") {
6202                     fireDocReady();     
6203                  }
6204             }, 10);
6205         }
6206         // no matter what, make sure it fires on load
6207         E.on(window, "load", fireDocReady);
6208     };
6209
6210     var createBuffered = function(h, o){
6211         var task = new Roo.util.DelayedTask(h);
6212         return function(e){
6213             // create new event object impl so new events don't wipe out properties
6214             e = new Roo.EventObjectImpl(e);
6215             task.delay(o.buffer, h, null, [e]);
6216         };
6217     };
6218
6219     var createSingle = function(h, el, ename, fn){
6220         return function(e){
6221             Roo.EventManager.removeListener(el, ename, fn);
6222             h(e);
6223         };
6224     };
6225
6226     var createDelayed = function(h, o){
6227         return function(e){
6228             // create new event object impl so new events don't wipe out properties
6229             e = new Roo.EventObjectImpl(e);
6230             setTimeout(function(){
6231                 h(e);
6232             }, o.delay || 10);
6233         };
6234     };
6235     var transitionEndVal = false;
6236     
6237     var transitionEnd = function()
6238     {
6239         if (transitionEndVal) {
6240             return transitionEndVal;
6241         }
6242         var el = document.createElement('div');
6243
6244         var transEndEventNames = {
6245             WebkitTransition : 'webkitTransitionEnd',
6246             MozTransition    : 'transitionend',
6247             OTransition      : 'oTransitionEnd otransitionend',
6248             transition       : 'transitionend'
6249         };
6250     
6251         for (var name in transEndEventNames) {
6252             if (el.style[name] !== undefined) {
6253                 transitionEndVal = transEndEventNames[name];
6254                 return  transitionEndVal ;
6255             }
6256         }
6257     }
6258     
6259
6260     var listen = function(element, ename, opt, fn, scope){
6261         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6262         fn = fn || o.fn; scope = scope || o.scope;
6263         var el = Roo.getDom(element);
6264         
6265         
6266         if(!el){
6267             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6268         }
6269         
6270         if (ename == 'transitionend') {
6271             ename = transitionEnd();
6272         }
6273         var h = function(e){
6274             e = Roo.EventObject.setEvent(e);
6275             var t;
6276             if(o.delegate){
6277                 t = e.getTarget(o.delegate, el);
6278                 if(!t){
6279                     return;
6280                 }
6281             }else{
6282                 t = e.target;
6283             }
6284             if(o.stopEvent === true){
6285                 e.stopEvent();
6286             }
6287             if(o.preventDefault === true){
6288                e.preventDefault();
6289             }
6290             if(o.stopPropagation === true){
6291                 e.stopPropagation();
6292             }
6293
6294             if(o.normalized === false){
6295                 e = e.browserEvent;
6296             }
6297
6298             fn.call(scope || el, e, t, o);
6299         };
6300         if(o.delay){
6301             h = createDelayed(h, o);
6302         }
6303         if(o.single){
6304             h = createSingle(h, el, ename, fn);
6305         }
6306         if(o.buffer){
6307             h = createBuffered(h, o);
6308         }
6309         
6310         fn._handlers = fn._handlers || [];
6311         
6312         
6313         fn._handlers.push([Roo.id(el), ename, h]);
6314         
6315         
6316          
6317         E.on(el, ename, h);
6318         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6319             el.addEventListener("DOMMouseScroll", h, false);
6320             E.on(window, 'unload', function(){
6321                 el.removeEventListener("DOMMouseScroll", h, false);
6322             });
6323         }
6324         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6325             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6326         }
6327         return h;
6328     };
6329
6330     var stopListening = function(el, ename, fn){
6331         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6332         if(hds){
6333             for(var i = 0, len = hds.length; i < len; i++){
6334                 var h = hds[i];
6335                 if(h[0] == id && h[1] == ename){
6336                     hd = h[2];
6337                     hds.splice(i, 1);
6338                     break;
6339                 }
6340             }
6341         }
6342         E.un(el, ename, hd);
6343         el = Roo.getDom(el);
6344         if(ename == "mousewheel" && el.addEventListener){
6345             el.removeEventListener("DOMMouseScroll", hd, false);
6346         }
6347         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6348             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6349         }
6350     };
6351
6352     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6353     
6354     var pub = {
6355         
6356         
6357         /** 
6358          * Fix for doc tools
6359          * @scope Roo.EventManager
6360          */
6361         
6362         
6363         /** 
6364          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6365          * object with a Roo.EventObject
6366          * @param {Function} fn        The method the event invokes
6367          * @param {Object}   scope    An object that becomes the scope of the handler
6368          * @param {boolean}  override If true, the obj passed in becomes
6369          *                             the execution scope of the listener
6370          * @return {Function} The wrapped function
6371          * @deprecated
6372          */
6373         wrap : function(fn, scope, override){
6374             return function(e){
6375                 Roo.EventObject.setEvent(e);
6376                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6377             };
6378         },
6379         
6380         /**
6381      * Appends an event handler to an element (shorthand for addListener)
6382      * @param {String/HTMLElement}   element        The html element or id to assign the
6383      * @param {String}   eventName The type of event to listen for
6384      * @param {Function} handler The method the event invokes
6385      * @param {Object}   scope (optional) The scope in which to execute the handler
6386      * function. The handler function's "this" context.
6387      * @param {Object}   options (optional) An object containing handler configuration
6388      * properties. This may contain any of the following properties:<ul>
6389      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6390      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6391      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6392      * <li>preventDefault {Boolean} True to prevent the default action</li>
6393      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6394      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6395      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6396      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6397      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6398      * by the specified number of milliseconds. If the event fires again within that time, the original
6399      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6400      * </ul><br>
6401      * <p>
6402      * <b>Combining Options</b><br>
6403      * Using the options argument, it is possible to combine different types of listeners:<br>
6404      * <br>
6405      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6406      * Code:<pre><code>
6407 el.on('click', this.onClick, this, {
6408     single: true,
6409     delay: 100,
6410     stopEvent : true,
6411     forumId: 4
6412 });</code></pre>
6413      * <p>
6414      * <b>Attaching multiple handlers in 1 call</b><br>
6415       * The method also allows for a single argument to be passed which is a config object containing properties
6416      * which specify multiple handlers.
6417      * <p>
6418      * Code:<pre><code>
6419 el.on({
6420     'click' : {
6421         fn: this.onClick
6422         scope: this,
6423         delay: 100
6424     },
6425     'mouseover' : {
6426         fn: this.onMouseOver
6427         scope: this
6428     },
6429     'mouseout' : {
6430         fn: this.onMouseOut
6431         scope: this
6432     }
6433 });</code></pre>
6434      * <p>
6435      * Or a shorthand syntax:<br>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : this.onClick,
6439     'mouseover' : this.onMouseOver,
6440     'mouseout' : this.onMouseOut
6441     scope: this
6442 });</code></pre>
6443      */
6444         addListener : function(element, eventName, fn, scope, options){
6445             if(typeof eventName == "object"){
6446                 var o = eventName;
6447                 for(var e in o){
6448                     if(propRe.test(e)){
6449                         continue;
6450                     }
6451                     if(typeof o[e] == "function"){
6452                         // shared options
6453                         listen(element, e, o, o[e], o.scope);
6454                     }else{
6455                         // individual options
6456                         listen(element, e, o[e]);
6457                     }
6458                 }
6459                 return;
6460             }
6461             return listen(element, eventName, options, fn, scope);
6462         },
6463         
6464         /**
6465          * Removes an event handler
6466          *
6467          * @param {String/HTMLElement}   element        The id or html element to remove the 
6468          *                             event from
6469          * @param {String}   eventName     The type of event
6470          * @param {Function} fn
6471          * @return {Boolean} True if a listener was actually removed
6472          */
6473         removeListener : function(element, eventName, fn){
6474             return stopListening(element, eventName, fn);
6475         },
6476         
6477         /**
6478          * Fires when the document is ready (before onload and before images are loaded). Can be 
6479          * accessed shorthanded Roo.onReady().
6480          * @param {Function} fn        The method the event invokes
6481          * @param {Object}   scope    An  object that becomes the scope of the handler
6482          * @param {boolean}  options
6483          */
6484         onDocumentReady : function(fn, scope, options){
6485             if(docReadyState){ // if it already fired
6486                 docReadyEvent.addListener(fn, scope, options);
6487                 docReadyEvent.fire();
6488                 docReadyEvent.clearListeners();
6489                 return;
6490             }
6491             if(!docReadyEvent){
6492                 initDocReady();
6493             }
6494             docReadyEvent.addListener(fn, scope, options);
6495         },
6496         
6497         /**
6498          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6499          * @param {Function} fn        The method the event invokes
6500          * @param {Object}   scope    An object that becomes the scope of the handler
6501          * @param {boolean}  options
6502          */
6503         onWindowResize : function(fn, scope, options){
6504             if(!resizeEvent){
6505                 resizeEvent = new Roo.util.Event();
6506                 resizeTask = new Roo.util.DelayedTask(function(){
6507                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6508                 });
6509                 E.on(window, "resize", function(){
6510                     if(Roo.isIE){
6511                         resizeTask.delay(50);
6512                     }else{
6513                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6514                     }
6515                 });
6516             }
6517             resizeEvent.addListener(fn, scope, options);
6518         },
6519
6520         /**
6521          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6522          * @param {Function} fn        The method the event invokes
6523          * @param {Object}   scope    An object that becomes the scope of the handler
6524          * @param {boolean}  options
6525          */
6526         onTextResize : function(fn, scope, options){
6527             if(!textEvent){
6528                 textEvent = new Roo.util.Event();
6529                 var textEl = new Roo.Element(document.createElement('div'));
6530                 textEl.dom.className = 'x-text-resize';
6531                 textEl.dom.innerHTML = 'X';
6532                 textEl.appendTo(document.body);
6533                 textSize = textEl.dom.offsetHeight;
6534                 setInterval(function(){
6535                     if(textEl.dom.offsetHeight != textSize){
6536                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6537                     }
6538                 }, this.textResizeInterval);
6539             }
6540             textEvent.addListener(fn, scope, options);
6541         },
6542
6543         /**
6544          * Removes the passed window resize listener.
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    The scope of handler
6547          */
6548         removeResizeListener : function(fn, scope){
6549             if(resizeEvent){
6550                 resizeEvent.removeListener(fn, scope);
6551             }
6552         },
6553
6554         // private
6555         fireResize : function(){
6556             if(resizeEvent){
6557                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558             }   
6559         },
6560         /**
6561          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6562          */
6563         ieDeferSrc : false,
6564         /**
6565          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6566          */
6567         textResizeInterval : 50
6568     };
6569     
6570     /**
6571      * Fix for doc tools
6572      * @scopeAlias pub=Roo.EventManager
6573      */
6574     
6575      /**
6576      * Appends an event handler to an element (shorthand for addListener)
6577      * @param {String/HTMLElement}   element        The html element or id to assign the
6578      * @param {String}   eventName The type of event to listen for
6579      * @param {Function} handler The method the event invokes
6580      * @param {Object}   scope (optional) The scope in which to execute the handler
6581      * function. The handler function's "this" context.
6582      * @param {Object}   options (optional) An object containing handler configuration
6583      * properties. This may contain any of the following properties:<ul>
6584      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6585      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6586      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6587      * <li>preventDefault {Boolean} True to prevent the default action</li>
6588      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6589      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6590      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6591      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6592      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6593      * by the specified number of milliseconds. If the event fires again within that time, the original
6594      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6595      * </ul><br>
6596      * <p>
6597      * <b>Combining Options</b><br>
6598      * Using the options argument, it is possible to combine different types of listeners:<br>
6599      * <br>
6600      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6601      * Code:<pre><code>
6602 el.on('click', this.onClick, this, {
6603     single: true,
6604     delay: 100,
6605     stopEvent : true,
6606     forumId: 4
6607 });</code></pre>
6608      * <p>
6609      * <b>Attaching multiple handlers in 1 call</b><br>
6610       * The method also allows for a single argument to be passed which is a config object containing properties
6611      * which specify multiple handlers.
6612      * <p>
6613      * Code:<pre><code>
6614 el.on({
6615     'click' : {
6616         fn: this.onClick
6617         scope: this,
6618         delay: 100
6619     },
6620     'mouseover' : {
6621         fn: this.onMouseOver
6622         scope: this
6623     },
6624     'mouseout' : {
6625         fn: this.onMouseOut
6626         scope: this
6627     }
6628 });</code></pre>
6629      * <p>
6630      * Or a shorthand syntax:<br>
6631      * Code:<pre><code>
6632 el.on({
6633     'click' : this.onClick,
6634     'mouseover' : this.onMouseOver,
6635     'mouseout' : this.onMouseOut
6636     scope: this
6637 });</code></pre>
6638      */
6639     pub.on = pub.addListener;
6640     pub.un = pub.removeListener;
6641
6642     pub.stoppedMouseDownEvent = new Roo.util.Event();
6643     return pub;
6644 }();
6645 /**
6646   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6647   * @param {Function} fn        The method the event invokes
6648   * @param {Object}   scope    An  object that becomes the scope of the handler
6649   * @param {boolean}  override If true, the obj passed in becomes
6650   *                             the execution scope of the listener
6651   * @member Roo
6652   * @method onReady
6653  */
6654 Roo.onReady = Roo.EventManager.onDocumentReady;
6655
6656 Roo.onReady(function(){
6657     var bd = Roo.get(document.body);
6658     if(!bd){ return; }
6659
6660     var cls = [
6661             Roo.isIE ? "roo-ie"
6662             : Roo.isIE11 ? "roo-ie11"
6663             : Roo.isEdge ? "roo-edge"
6664             : Roo.isGecko ? "roo-gecko"
6665             : Roo.isOpera ? "roo-opera"
6666             : Roo.isSafari ? "roo-safari" : ""];
6667
6668     if(Roo.isMac){
6669         cls.push("roo-mac");
6670     }
6671     if(Roo.isLinux){
6672         cls.push("roo-linux");
6673     }
6674     if(Roo.isIOS){
6675         cls.push("roo-ios");
6676     }
6677     if(Roo.isTouch){
6678         cls.push("roo-touch");
6679     }
6680     if(Roo.isBorderBox){
6681         cls.push('roo-border-box');
6682     }
6683     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6684         var p = bd.dom.parentNode;
6685         if(p){
6686             p.className += ' roo-strict';
6687         }
6688     }
6689     bd.addClass(cls.join(' '));
6690 });
6691
6692 /**
6693  * @class Roo.EventObject
6694  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6695  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6696  * Example:
6697  * <pre><code>
6698  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6699     e.preventDefault();
6700     var target = e.getTarget();
6701     ...
6702  }
6703  var myDiv = Roo.get("myDiv");
6704  myDiv.on("click", handleClick);
6705  //or
6706  Roo.EventManager.on("myDiv", 'click', handleClick);
6707  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6708  </code></pre>
6709  * @singleton
6710  */
6711 Roo.EventObject = function(){
6712     
6713     var E = Roo.lib.Event;
6714     
6715     // safari keypress events for special keys return bad keycodes
6716     var safariKeys = {
6717         63234 : 37, // left
6718         63235 : 39, // right
6719         63232 : 38, // up
6720         63233 : 40, // down
6721         63276 : 33, // page up
6722         63277 : 34, // page down
6723         63272 : 46, // delete
6724         63273 : 36, // home
6725         63275 : 35  // end
6726     };
6727
6728     // normalize button clicks
6729     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6730                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6731
6732     Roo.EventObjectImpl = function(e){
6733         if(e){
6734             this.setEvent(e.browserEvent || e);
6735         }
6736     };
6737     Roo.EventObjectImpl.prototype = {
6738         /**
6739          * Used to fix doc tools.
6740          * @scope Roo.EventObject.prototype
6741          */
6742             
6743
6744         
6745         
6746         /** The normal browser event */
6747         browserEvent : null,
6748         /** The button pressed in a mouse event */
6749         button : -1,
6750         /** True if the shift key was down during the event */
6751         shiftKey : false,
6752         /** True if the control key was down during the event */
6753         ctrlKey : false,
6754         /** True if the alt key was down during the event */
6755         altKey : false,
6756
6757         /** Key constant 
6758         * @type Number */
6759         BACKSPACE : 8,
6760         /** Key constant 
6761         * @type Number */
6762         TAB : 9,
6763         /** Key constant 
6764         * @type Number */
6765         RETURN : 13,
6766         /** Key constant 
6767         * @type Number */
6768         ENTER : 13,
6769         /** Key constant 
6770         * @type Number */
6771         SHIFT : 16,
6772         /** Key constant 
6773         * @type Number */
6774         CONTROL : 17,
6775         /** Key constant 
6776         * @type Number */
6777         ESC : 27,
6778         /** Key constant 
6779         * @type Number */
6780         SPACE : 32,
6781         /** Key constant 
6782         * @type Number */
6783         PAGEUP : 33,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEDOWN : 34,
6787         /** Key constant 
6788         * @type Number */
6789         END : 35,
6790         /** Key constant 
6791         * @type Number */
6792         HOME : 36,
6793         /** Key constant 
6794         * @type Number */
6795         LEFT : 37,
6796         /** Key constant 
6797         * @type Number */
6798         UP : 38,
6799         /** Key constant 
6800         * @type Number */
6801         RIGHT : 39,
6802         /** Key constant 
6803         * @type Number */
6804         DOWN : 40,
6805         /** Key constant 
6806         * @type Number */
6807         DELETE : 46,
6808         /** Key constant 
6809         * @type Number */
6810         F5 : 116,
6811
6812            /** @private */
6813         setEvent : function(e){
6814             if(e == this || (e && e.browserEvent)){ // already wrapped
6815                 return e;
6816             }
6817             this.browserEvent = e;
6818             if(e){
6819                 // normalize buttons
6820                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6821                 if(e.type == 'click' && this.button == -1){
6822                     this.button = 0;
6823                 }
6824                 this.type = e.type;
6825                 this.shiftKey = e.shiftKey;
6826                 // mac metaKey behaves like ctrlKey
6827                 this.ctrlKey = e.ctrlKey || e.metaKey;
6828                 this.altKey = e.altKey;
6829                 // in getKey these will be normalized for the mac
6830                 this.keyCode = e.keyCode;
6831                 // keyup warnings on firefox.
6832                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6833                 // cache the target for the delayed and or buffered events
6834                 this.target = E.getTarget(e);
6835                 // same for XY
6836                 this.xy = E.getXY(e);
6837             }else{
6838                 this.button = -1;
6839                 this.shiftKey = false;
6840                 this.ctrlKey = false;
6841                 this.altKey = false;
6842                 this.keyCode = 0;
6843                 this.charCode =0;
6844                 this.target = null;
6845                 this.xy = [0, 0];
6846             }
6847             return this;
6848         },
6849
6850         /**
6851          * Stop the event (preventDefault and stopPropagation)
6852          */
6853         stopEvent : function(){
6854             if(this.browserEvent){
6855                 if(this.browserEvent.type == 'mousedown'){
6856                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6857                 }
6858                 E.stopEvent(this.browserEvent);
6859             }
6860         },
6861
6862         /**
6863          * Prevents the browsers default handling of the event.
6864          */
6865         preventDefault : function(){
6866             if(this.browserEvent){
6867                 E.preventDefault(this.browserEvent);
6868             }
6869         },
6870
6871         /** @private */
6872         isNavKeyPress : function(){
6873             var k = this.keyCode;
6874             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6875             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6876         },
6877
6878         isSpecialKey : function(){
6879             var k = this.keyCode;
6880             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6881             (k == 16) || (k == 17) ||
6882             (k >= 18 && k <= 20) ||
6883             (k >= 33 && k <= 35) ||
6884             (k >= 36 && k <= 39) ||
6885             (k >= 44 && k <= 45);
6886         },
6887         /**
6888          * Cancels bubbling of the event.
6889          */
6890         stopPropagation : function(){
6891             if(this.browserEvent){
6892                 if(this.type == 'mousedown'){
6893                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6894                 }
6895                 E.stopPropagation(this.browserEvent);
6896             }
6897         },
6898
6899         /**
6900          * Gets the key code for the event.
6901          * @return {Number}
6902          */
6903         getCharCode : function(){
6904             return this.charCode || this.keyCode;
6905         },
6906
6907         /**
6908          * Returns a normalized keyCode for the event.
6909          * @return {Number} The key code
6910          */
6911         getKey : function(){
6912             var k = this.keyCode || this.charCode;
6913             return Roo.isSafari ? (safariKeys[k] || k) : k;
6914         },
6915
6916         /**
6917          * Gets the x coordinate of the event.
6918          * @return {Number}
6919          */
6920         getPageX : function(){
6921             return this.xy[0];
6922         },
6923
6924         /**
6925          * Gets the y coordinate of the event.
6926          * @return {Number}
6927          */
6928         getPageY : function(){
6929             return this.xy[1];
6930         },
6931
6932         /**
6933          * Gets the time of the event.
6934          * @return {Number}
6935          */
6936         getTime : function(){
6937             if(this.browserEvent){
6938                 return E.getTime(this.browserEvent);
6939             }
6940             return null;
6941         },
6942
6943         /**
6944          * Gets the page coordinates of the event.
6945          * @return {Array} The xy values like [x, y]
6946          */
6947         getXY : function(){
6948             return this.xy;
6949         },
6950
6951         /**
6952          * Gets the target for the event.
6953          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6954          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6955                 search as a number or element (defaults to 10 || document.body)
6956          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6957          * @return {HTMLelement}
6958          */
6959         getTarget : function(selector, maxDepth, returnEl){
6960             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6961         },
6962         /**
6963          * Gets the related target.
6964          * @return {HTMLElement}
6965          */
6966         getRelatedTarget : function(){
6967             if(this.browserEvent){
6968                 return E.getRelatedTarget(this.browserEvent);
6969             }
6970             return null;
6971         },
6972
6973         /**
6974          * Normalizes mouse wheel delta across browsers
6975          * @return {Number} The delta
6976          */
6977         getWheelDelta : function(){
6978             var e = this.browserEvent;
6979             var delta = 0;
6980             if(e.wheelDelta){ /* IE/Opera. */
6981                 delta = e.wheelDelta/120;
6982             }else if(e.detail){ /* Mozilla case. */
6983                 delta = -e.detail/3;
6984             }
6985             return delta;
6986         },
6987
6988         /**
6989          * Returns true if the control, meta, shift or alt key was pressed during this event.
6990          * @return {Boolean}
6991          */
6992         hasModifier : function(){
6993             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6994         },
6995
6996         /**
6997          * Returns true if the target of this event equals el or is a child of el
6998          * @param {String/HTMLElement/Element} el
6999          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7000          * @return {Boolean}
7001          */
7002         within : function(el, related){
7003             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7004             return t && Roo.fly(el).contains(t);
7005         },
7006
7007         getPoint : function(){
7008             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7009         }
7010     };
7011
7012     return new Roo.EventObjectImpl();
7013 }();
7014             
7015     /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026  
7027 // was in Composite Element!??!?!
7028  
7029 (function(){
7030     var D = Roo.lib.Dom;
7031     var E = Roo.lib.Event;
7032     var A = Roo.lib.Anim;
7033
7034     // local style camelizing for speed
7035     var propCache = {};
7036     var camelRe = /(-[a-z])/gi;
7037     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7038     var view = document.defaultView;
7039
7040 /**
7041  * @class Roo.Element
7042  * Represents an Element in the DOM.<br><br>
7043  * Usage:<br>
7044 <pre><code>
7045 var el = Roo.get("my-div");
7046
7047 // or with getEl
7048 var el = getEl("my-div");
7049
7050 // or with a DOM element
7051 var el = Roo.get(myDivElement);
7052 </code></pre>
7053  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7054  * each call instead of constructing a new one.<br><br>
7055  * <b>Animations</b><br />
7056  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7057  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7058 <pre>
7059 Option    Default   Description
7060 --------- --------  ---------------------------------------------
7061 duration  .35       The duration of the animation in seconds
7062 easing    easeOut   The YUI easing method
7063 callback  none      A function to execute when the anim completes
7064 scope     this      The scope (this) of the callback function
7065 </pre>
7066 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7067 * manipulate the animation. Here's an example:
7068 <pre><code>
7069 var el = Roo.get("my-div");
7070
7071 // no animation
7072 el.setWidth(100);
7073
7074 // default animation
7075 el.setWidth(100, true);
7076
7077 // animation with some options set
7078 el.setWidth(100, {
7079     duration: 1,
7080     callback: this.foo,
7081     scope: this
7082 });
7083
7084 // using the "anim" property to get the Anim object
7085 var opt = {
7086     duration: 1,
7087     callback: this.foo,
7088     scope: this
7089 };
7090 el.setWidth(100, opt);
7091 ...
7092 if(opt.anim.isAnimated()){
7093     opt.anim.stop();
7094 }
7095 </code></pre>
7096 * <b> Composite (Collections of) Elements</b><br />
7097  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7098  * @constructor Create a new Element directly.
7099  * @param {String/HTMLElement} element
7100  * @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).
7101  */
7102     Roo.Element = function(element, forceNew){
7103         var dom = typeof element == "string" ?
7104                 document.getElementById(element) : element;
7105         if(!dom){ // invalid id/element
7106             return null;
7107         }
7108         var id = dom.id;
7109         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7110             return Roo.Element.cache[id];
7111         }
7112
7113         /**
7114          * The DOM element
7115          * @type HTMLElement
7116          */
7117         this.dom = dom;
7118
7119         /**
7120          * The DOM element ID
7121          * @type String
7122          */
7123         this.id = id || Roo.id(dom);
7124     };
7125
7126     var El = Roo.Element;
7127
7128     El.prototype = {
7129         /**
7130          * The element's default display mode  (defaults to "")
7131          * @type String
7132          */
7133         originalDisplay : "",
7134
7135         visibilityMode : 1,
7136         /**
7137          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7138          * @type String
7139          */
7140         defaultUnit : "px",
7141         
7142         /**
7143          * Sets the element's visibility mode. When setVisible() is called it
7144          * will use this to determine whether to set the visibility or the display property.
7145          * @param visMode Element.VISIBILITY or Element.DISPLAY
7146          * @return {Roo.Element} this
7147          */
7148         setVisibilityMode : function(visMode){
7149             this.visibilityMode = visMode;
7150             return this;
7151         },
7152         /**
7153          * Convenience method for setVisibilityMode(Element.DISPLAY)
7154          * @param {String} display (optional) What to set display to when visible
7155          * @return {Roo.Element} this
7156          */
7157         enableDisplayMode : function(display){
7158             this.setVisibilityMode(El.DISPLAY);
7159             if(typeof display != "undefined") { this.originalDisplay = display; }
7160             return this;
7161         },
7162
7163         /**
7164          * 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)
7165          * @param {String} selector The simple selector to test
7166          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7167                 search as a number or element (defaults to 10 || document.body)
7168          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7169          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7170          */
7171         findParent : function(simpleSelector, maxDepth, returnEl){
7172             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7173             maxDepth = maxDepth || 50;
7174             if(typeof maxDepth != "number"){
7175                 stopEl = Roo.getDom(maxDepth);
7176                 maxDepth = 10;
7177             }
7178             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7179                 if(dq.is(p, simpleSelector)){
7180                     return returnEl ? Roo.get(p) : p;
7181                 }
7182                 depth++;
7183                 p = p.parentNode;
7184             }
7185             return null;
7186         },
7187
7188
7189         /**
7190          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7191          * @param {String} selector The simple selector to test
7192          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7193                 search as a number or element (defaults to 10 || document.body)
7194          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7195          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7196          */
7197         findParentNode : function(simpleSelector, maxDepth, returnEl){
7198             var p = Roo.fly(this.dom.parentNode, '_internal');
7199             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7200         },
7201         
7202         /**
7203          * Looks at  the scrollable parent element
7204          */
7205         findScrollableParent : function()
7206         {
7207             var overflowRegex = /(auto|scroll)/;
7208             
7209             if(this.getStyle('position') === 'fixed'){
7210                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7211             }
7212             
7213             var excludeStaticParent = this.getStyle('position') === "absolute";
7214             
7215             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7216                 
7217                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7218                     continue;
7219                 }
7220                 
7221                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7222                     return parent;
7223                 }
7224                 
7225                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7226                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7227                 }
7228             }
7229             
7230             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7231         },
7232
7233         /**
7234          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7235          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7236          * @param {String} selector The simple selector to test
7237          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7238                 search as a number or element (defaults to 10 || document.body)
7239          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7240          */
7241         up : function(simpleSelector, maxDepth){
7242             return this.findParentNode(simpleSelector, maxDepth, true);
7243         },
7244
7245
7246
7247         /**
7248          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7249          * @param {String} selector The simple selector to test
7250          * @return {Boolean} True if this element matches the selector, else false
7251          */
7252         is : function(simpleSelector){
7253             return Roo.DomQuery.is(this.dom, simpleSelector);
7254         },
7255
7256         /**
7257          * Perform animation on this element.
7258          * @param {Object} args The YUI animation control args
7259          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7260          * @param {Function} onComplete (optional) Function to call when animation completes
7261          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7262          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7263          * @return {Roo.Element} this
7264          */
7265         animate : function(args, duration, onComplete, easing, animType){
7266             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7267             return this;
7268         },
7269
7270         /*
7271          * @private Internal animation call
7272          */
7273         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7274             animType = animType || 'run';
7275             opt = opt || {};
7276             var anim = Roo.lib.Anim[animType](
7277                 this.dom, args,
7278                 (opt.duration || defaultDur) || .35,
7279                 (opt.easing || defaultEase) || 'easeOut',
7280                 function(){
7281                     Roo.callback(cb, this);
7282                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7283                 },
7284                 this
7285             );
7286             opt.anim = anim;
7287             return anim;
7288         },
7289
7290         // private legacy anim prep
7291         preanim : function(a, i){
7292             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7293         },
7294
7295         /**
7296          * Removes worthless text nodes
7297          * @param {Boolean} forceReclean (optional) By default the element
7298          * keeps track if it has been cleaned already so
7299          * you can call this over and over. However, if you update the element and
7300          * need to force a reclean, you can pass true.
7301          */
7302         clean : function(forceReclean){
7303             if(this.isCleaned && forceReclean !== true){
7304                 return this;
7305             }
7306             var ns = /\S/;
7307             var d = this.dom, n = d.firstChild, ni = -1;
7308             while(n){
7309                 var nx = n.nextSibling;
7310                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7311                     d.removeChild(n);
7312                 }else{
7313                     n.nodeIndex = ++ni;
7314                 }
7315                 n = nx;
7316             }
7317             this.isCleaned = true;
7318             return this;
7319         },
7320
7321         // private
7322         calcOffsetsTo : function(el){
7323             el = Roo.get(el);
7324             var d = el.dom;
7325             var restorePos = false;
7326             if(el.getStyle('position') == 'static'){
7327                 el.position('relative');
7328                 restorePos = true;
7329             }
7330             var x = 0, y =0;
7331             var op = this.dom;
7332             while(op && op != d && op.tagName != 'HTML'){
7333                 x+= op.offsetLeft;
7334                 y+= op.offsetTop;
7335                 op = op.offsetParent;
7336             }
7337             if(restorePos){
7338                 el.position('static');
7339             }
7340             return [x, y];
7341         },
7342
7343         /**
7344          * Scrolls this element into view within the passed container.
7345          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7346          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7347          * @return {Roo.Element} this
7348          */
7349         scrollIntoView : function(container, hscroll){
7350             var c = Roo.getDom(container) || document.body;
7351             var el = this.dom;
7352
7353             var o = this.calcOffsetsTo(c),
7354                 l = o[0],
7355                 t = o[1],
7356                 b = t+el.offsetHeight,
7357                 r = l+el.offsetWidth;
7358
7359             var ch = c.clientHeight;
7360             var ct = parseInt(c.scrollTop, 10);
7361             var cl = parseInt(c.scrollLeft, 10);
7362             var cb = ct + ch;
7363             var cr = cl + c.clientWidth;
7364
7365             if(t < ct){
7366                 c.scrollTop = t;
7367             }else if(b > cb){
7368                 c.scrollTop = b-ch;
7369             }
7370
7371             if(hscroll !== false){
7372                 if(l < cl){
7373                     c.scrollLeft = l;
7374                 }else if(r > cr){
7375                     c.scrollLeft = r-c.clientWidth;
7376                 }
7377             }
7378             return this;
7379         },
7380
7381         // private
7382         scrollChildIntoView : function(child, hscroll){
7383             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7384         },
7385
7386         /**
7387          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7388          * the new height may not be available immediately.
7389          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7390          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7391          * @param {Function} onComplete (optional) Function to call when animation completes
7392          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7393          * @return {Roo.Element} this
7394          */
7395         autoHeight : function(animate, duration, onComplete, easing){
7396             var oldHeight = this.getHeight();
7397             this.clip();
7398             this.setHeight(1); // force clipping
7399             setTimeout(function(){
7400                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7401                 if(!animate){
7402                     this.setHeight(height);
7403                     this.unclip();
7404                     if(typeof onComplete == "function"){
7405                         onComplete();
7406                     }
7407                 }else{
7408                     this.setHeight(oldHeight); // restore original height
7409                     this.setHeight(height, animate, duration, function(){
7410                         this.unclip();
7411                         if(typeof onComplete == "function") { onComplete(); }
7412                     }.createDelegate(this), easing);
7413                 }
7414             }.createDelegate(this), 0);
7415             return this;
7416         },
7417
7418         /**
7419          * Returns true if this element is an ancestor of the passed element
7420          * @param {HTMLElement/String} el The element to check
7421          * @return {Boolean} True if this element is an ancestor of el, else false
7422          */
7423         contains : function(el){
7424             if(!el){return false;}
7425             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7426         },
7427
7428         /**
7429          * Checks whether the element is currently visible using both visibility and display properties.
7430          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7431          * @return {Boolean} True if the element is currently visible, else false
7432          */
7433         isVisible : function(deep) {
7434             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7435             if(deep !== true || !vis){
7436                 return vis;
7437             }
7438             var p = this.dom.parentNode;
7439             while(p && p.tagName.toLowerCase() != "body"){
7440                 if(!Roo.fly(p, '_isVisible').isVisible()){
7441                     return false;
7442                 }
7443                 p = p.parentNode;
7444             }
7445             return true;
7446         },
7447
7448         /**
7449          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7450          * @param {String} selector The CSS selector
7451          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7452          * @return {CompositeElement/CompositeElementLite} The composite element
7453          */
7454         select : function(selector, unique){
7455             return El.select(selector, unique, this.dom);
7456         },
7457
7458         /**
7459          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7460          * @param {String} selector The CSS selector
7461          * @return {Array} An array of the matched nodes
7462          */
7463         query : function(selector, unique){
7464             return Roo.DomQuery.select(selector, this.dom);
7465         },
7466
7467         /**
7468          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7469          * @param {String} selector The CSS selector
7470          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7471          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7472          */
7473         child : function(selector, returnDom){
7474             var n = Roo.DomQuery.selectNode(selector, this.dom);
7475             return returnDom ? n : Roo.get(n);
7476         },
7477
7478         /**
7479          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7480          * @param {String} selector The CSS selector
7481          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7482          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7483          */
7484         down : function(selector, returnDom){
7485             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7486             return returnDom ? n : Roo.get(n);
7487         },
7488
7489         /**
7490          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7491          * @param {String} group The group the DD object is member of
7492          * @param {Object} config The DD config object
7493          * @param {Object} overrides An object containing methods to override/implement on the DD object
7494          * @return {Roo.dd.DD} The DD object
7495          */
7496         initDD : function(group, config, overrides){
7497             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7498             return Roo.apply(dd, overrides);
7499         },
7500
7501         /**
7502          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7503          * @param {String} group The group the DDProxy object is member of
7504          * @param {Object} config The DDProxy config object
7505          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7506          * @return {Roo.dd.DDProxy} The DDProxy object
7507          */
7508         initDDProxy : function(group, config, overrides){
7509             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7510             return Roo.apply(dd, overrides);
7511         },
7512
7513         /**
7514          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7515          * @param {String} group The group the DDTarget object is member of
7516          * @param {Object} config The DDTarget config object
7517          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7518          * @return {Roo.dd.DDTarget} The DDTarget object
7519          */
7520         initDDTarget : function(group, config, overrides){
7521             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7522             return Roo.apply(dd, overrides);
7523         },
7524
7525         /**
7526          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7527          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7528          * @param {Boolean} visible Whether the element is visible
7529          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7530          * @return {Roo.Element} this
7531          */
7532          setVisible : function(visible, animate){
7533             if(!animate || !A){
7534                 if(this.visibilityMode == El.DISPLAY){
7535                     this.setDisplayed(visible);
7536                 }else{
7537                     this.fixDisplay();
7538                     this.dom.style.visibility = visible ? "visible" : "hidden";
7539                 }
7540             }else{
7541                 // closure for composites
7542                 var dom = this.dom;
7543                 var visMode = this.visibilityMode;
7544                 if(visible){
7545                     this.setOpacity(.01);
7546                     this.setVisible(true);
7547                 }
7548                 this.anim({opacity: { to: (visible?1:0) }},
7549                       this.preanim(arguments, 1),
7550                       null, .35, 'easeIn', function(){
7551                          if(!visible){
7552                              if(visMode == El.DISPLAY){
7553                                  dom.style.display = "none";
7554                              }else{
7555                                  dom.style.visibility = "hidden";
7556                              }
7557                              Roo.get(dom).setOpacity(1);
7558                          }
7559                      });
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * Returns true if display is not "none"
7566          * @return {Boolean}
7567          */
7568         isDisplayed : function() {
7569             return this.getStyle("display") != "none";
7570         },
7571
7572         /**
7573          * Toggles the element's visibility or display, depending on visibility mode.
7574          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7575          * @return {Roo.Element} this
7576          */
7577         toggle : function(animate){
7578             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7579             return this;
7580         },
7581
7582         /**
7583          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7584          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7585          * @return {Roo.Element} this
7586          */
7587         setDisplayed : function(value) {
7588             if(typeof value == "boolean"){
7589                value = value ? this.originalDisplay : "none";
7590             }
7591             this.setStyle("display", value);
7592             return this;
7593         },
7594
7595         /**
7596          * Tries to focus the element. Any exceptions are caught and ignored.
7597          * @return {Roo.Element} this
7598          */
7599         focus : function() {
7600             try{
7601                 this.dom.focus();
7602             }catch(e){}
7603             return this;
7604         },
7605
7606         /**
7607          * Tries to blur the element. Any exceptions are caught and ignored.
7608          * @return {Roo.Element} this
7609          */
7610         blur : function() {
7611             try{
7612                 this.dom.blur();
7613             }catch(e){}
7614             return this;
7615         },
7616
7617         /**
7618          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7619          * @param {String/Array} className The CSS class to add, or an array of classes
7620          * @return {Roo.Element} this
7621          */
7622         addClass : function(className){
7623             if(className instanceof Array){
7624                 for(var i = 0, len = className.length; i < len; i++) {
7625                     this.addClass(className[i]);
7626                 }
7627             }else{
7628                 if(className && !this.hasClass(className)){
7629                     this.dom.className = this.dom.className + " " + className;
7630                 }
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7637          * @param {String/Array} className The CSS class to add, or an array of classes
7638          * @return {Roo.Element} this
7639          */
7640         radioClass : function(className){
7641             var siblings = this.dom.parentNode.childNodes;
7642             for(var i = 0; i < siblings.length; i++) {
7643                 var s = siblings[i];
7644                 if(s.nodeType == 1){
7645                     Roo.get(s).removeClass(className);
7646                 }
7647             }
7648             this.addClass(className);
7649             return this;
7650         },
7651
7652         /**
7653          * Removes one or more CSS classes from the element.
7654          * @param {String/Array} className The CSS class to remove, or an array of classes
7655          * @return {Roo.Element} this
7656          */
7657         removeClass : function(className){
7658             if(!className || !this.dom.className){
7659                 return this;
7660             }
7661             if(className instanceof Array){
7662                 for(var i = 0, len = className.length; i < len; i++) {
7663                     this.removeClass(className[i]);
7664                 }
7665             }else{
7666                 if(this.hasClass(className)){
7667                     var re = this.classReCache[className];
7668                     if (!re) {
7669                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7670                        this.classReCache[className] = re;
7671                     }
7672                     this.dom.className =
7673                         this.dom.className.replace(re, " ");
7674                 }
7675             }
7676             return this;
7677         },
7678
7679         // private
7680         classReCache: {},
7681
7682         /**
7683          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7684          * @param {String} className The CSS class to toggle
7685          * @return {Roo.Element} this
7686          */
7687         toggleClass : function(className){
7688             if(this.hasClass(className)){
7689                 this.removeClass(className);
7690             }else{
7691                 this.addClass(className);
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Checks if the specified CSS class exists on this element's DOM node.
7698          * @param {String} className The CSS class to check for
7699          * @return {Boolean} True if the class exists, else false
7700          */
7701         hasClass : function(className){
7702             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7703         },
7704
7705         /**
7706          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7707          * @param {String} oldClassName The CSS class to replace
7708          * @param {String} newClassName The replacement CSS class
7709          * @return {Roo.Element} this
7710          */
7711         replaceClass : function(oldClassName, newClassName){
7712             this.removeClass(oldClassName);
7713             this.addClass(newClassName);
7714             return this;
7715         },
7716
7717         /**
7718          * Returns an object with properties matching the styles requested.
7719          * For example, el.getStyles('color', 'font-size', 'width') might return
7720          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7721          * @param {String} style1 A style name
7722          * @param {String} style2 A style name
7723          * @param {String} etc.
7724          * @return {Object} The style object
7725          */
7726         getStyles : function(){
7727             var a = arguments, len = a.length, r = {};
7728             for(var i = 0; i < len; i++){
7729                 r[a[i]] = this.getStyle(a[i]);
7730             }
7731             return r;
7732         },
7733
7734         /**
7735          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7736          * @param {String} property The style property whose value is returned.
7737          * @return {String} The current value of the style property for this element.
7738          */
7739         getStyle : function(){
7740             return view && view.getComputedStyle ?
7741                 function(prop){
7742                     var el = this.dom, v, cs, camel;
7743                     if(prop == 'float'){
7744                         prop = "cssFloat";
7745                     }
7746                     if(el.style && (v = el.style[prop])){
7747                         return v;
7748                     }
7749                     if(cs = view.getComputedStyle(el, "")){
7750                         if(!(camel = propCache[prop])){
7751                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7752                         }
7753                         return cs[camel];
7754                     }
7755                     return null;
7756                 } :
7757                 function(prop){
7758                     var el = this.dom, v, cs, camel;
7759                     if(prop == 'opacity'){
7760                         if(typeof el.style.filter == 'string'){
7761                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7762                             if(m){
7763                                 var fv = parseFloat(m[1]);
7764                                 if(!isNaN(fv)){
7765                                     return fv ? fv / 100 : 0;
7766                                 }
7767                             }
7768                         }
7769                         return 1;
7770                     }else if(prop == 'float'){
7771                         prop = "styleFloat";
7772                     }
7773                     if(!(camel = propCache[prop])){
7774                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7775                     }
7776                     if(v = el.style[camel]){
7777                         return v;
7778                     }
7779                     if(cs = el.currentStyle){
7780                         return cs[camel];
7781                     }
7782                     return null;
7783                 };
7784         }(),
7785
7786         /**
7787          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7788          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7789          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7790          * @return {Roo.Element} this
7791          */
7792         setStyle : function(prop, value){
7793             if(typeof prop == "string"){
7794                 
7795                 if (prop == 'float') {
7796                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7797                     return this;
7798                 }
7799                 
7800                 var camel;
7801                 if(!(camel = propCache[prop])){
7802                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7803                 }
7804                 
7805                 if(camel == 'opacity') {
7806                     this.setOpacity(value);
7807                 }else{
7808                     this.dom.style[camel] = value;
7809                 }
7810             }else{
7811                 for(var style in prop){
7812                     if(typeof prop[style] != "function"){
7813                        this.setStyle(style, prop[style]);
7814                     }
7815                 }
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * More flexible version of {@link #setStyle} for setting style properties.
7822          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7823          * a function which returns such a specification.
7824          * @return {Roo.Element} this
7825          */
7826         applyStyles : function(style){
7827             Roo.DomHelper.applyStyles(this.dom, style);
7828             return this;
7829         },
7830
7831         /**
7832           * 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).
7833           * @return {Number} The X position of the element
7834           */
7835         getX : function(){
7836             return D.getX(this.dom);
7837         },
7838
7839         /**
7840           * 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).
7841           * @return {Number} The Y position of the element
7842           */
7843         getY : function(){
7844             return D.getY(this.dom);
7845         },
7846
7847         /**
7848           * 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).
7849           * @return {Array} The XY position of the element
7850           */
7851         getXY : function(){
7852             return D.getXY(this.dom);
7853         },
7854
7855         /**
7856          * 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).
7857          * @param {Number} The X position of the element
7858          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setX : function(x, animate){
7862             if(!animate || !A){
7863                 D.setX(this.dom, x);
7864             }else{
7865                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7866             }
7867             return this;
7868         },
7869
7870         /**
7871          * 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).
7872          * @param {Number} The Y position of the element
7873          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7874          * @return {Roo.Element} this
7875          */
7876         setY : function(y, animate){
7877             if(!animate || !A){
7878                 D.setY(this.dom, y);
7879             }else{
7880                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7887          * @param {String} left The left CSS property value
7888          * @return {Roo.Element} this
7889          */
7890         setLeft : function(left){
7891             this.setStyle("left", this.addUnits(left));
7892             return this;
7893         },
7894
7895         /**
7896          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7897          * @param {String} top The top CSS property value
7898          * @return {Roo.Element} this
7899          */
7900         setTop : function(top){
7901             this.setStyle("top", this.addUnits(top));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the element's CSS right style.
7907          * @param {String} right The right CSS property value
7908          * @return {Roo.Element} this
7909          */
7910         setRight : function(right){
7911             this.setStyle("right", this.addUnits(right));
7912             return this;
7913         },
7914
7915         /**
7916          * Sets the element's CSS bottom style.
7917          * @param {String} bottom The bottom CSS property value
7918          * @return {Roo.Element} this
7919          */
7920         setBottom : function(bottom){
7921             this.setStyle("bottom", this.addUnits(bottom));
7922             return this;
7923         },
7924
7925         /**
7926          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7927          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7928          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7929          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7930          * @return {Roo.Element} this
7931          */
7932         setXY : function(pos, animate){
7933             if(!animate || !A){
7934                 D.setXY(this.dom, pos);
7935             }else{
7936                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7937             }
7938             return this;
7939         },
7940
7941         /**
7942          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7943          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7944          * @param {Number} x X value for new position (coordinates are page-based)
7945          * @param {Number} y Y value for new position (coordinates are page-based)
7946          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7947          * @return {Roo.Element} this
7948          */
7949         setLocation : function(x, y, animate){
7950             this.setXY([x, y], this.preanim(arguments, 2));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7956          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7957          * @param {Number} x X value for new position (coordinates are page-based)
7958          * @param {Number} y Y value for new position (coordinates are page-based)
7959          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962         moveTo : function(x, y, animate){
7963             this.setXY([x, y], this.preanim(arguments, 2));
7964             return this;
7965         },
7966
7967         /**
7968          * Returns the region of the given element.
7969          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7970          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7971          */
7972         getRegion : function(){
7973             return D.getRegion(this.dom);
7974         },
7975
7976         /**
7977          * Returns the offset height of the element
7978          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7979          * @return {Number} The element's height
7980          */
7981         getHeight : function(contentHeight){
7982             var h = this.dom.offsetHeight || 0;
7983             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7984         },
7985
7986         /**
7987          * Returns the offset width of the element
7988          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7989          * @return {Number} The element's width
7990          */
7991         getWidth : function(contentWidth){
7992             var w = this.dom.offsetWidth || 0;
7993             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7994         },
7995
7996         /**
7997          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7998          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7999          * if a height has not been set using CSS.
8000          * @return {Number}
8001          */
8002         getComputedHeight : function(){
8003             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8004             if(!h){
8005                 h = parseInt(this.getStyle('height'), 10) || 0;
8006                 if(!this.isBorderBox()){
8007                     h += this.getFrameWidth('tb');
8008                 }
8009             }
8010             return h;
8011         },
8012
8013         /**
8014          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8015          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8016          * if a width has not been set using CSS.
8017          * @return {Number}
8018          */
8019         getComputedWidth : function(){
8020             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8021             if(!w){
8022                 w = parseInt(this.getStyle('width'), 10) || 0;
8023                 if(!this.isBorderBox()){
8024                     w += this.getFrameWidth('lr');
8025                 }
8026             }
8027             return w;
8028         },
8029
8030         /**
8031          * Returns the size of the element.
8032          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8033          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8034          */
8035         getSize : function(contentSize){
8036             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8037         },
8038
8039         /**
8040          * Returns the width and height of the viewport.
8041          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8042          */
8043         getViewSize : function(){
8044             var d = this.dom, doc = document, aw = 0, ah = 0;
8045             if(d == doc || d == doc.body){
8046                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8047             }else{
8048                 return {
8049                     width : d.clientWidth,
8050                     height: d.clientHeight
8051                 };
8052             }
8053         },
8054
8055         /**
8056          * Returns the value of the "value" attribute
8057          * @param {Boolean} asNumber true to parse the value as a number
8058          * @return {String/Number}
8059          */
8060         getValue : function(asNumber){
8061             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8062         },
8063
8064         // private
8065         adjustWidth : function(width){
8066             if(typeof width == "number"){
8067                 if(this.autoBoxAdjust && !this.isBorderBox()){
8068                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8069                 }
8070                 if(width < 0){
8071                     width = 0;
8072                 }
8073             }
8074             return width;
8075         },
8076
8077         // private
8078         adjustHeight : function(height){
8079             if(typeof height == "number"){
8080                if(this.autoBoxAdjust && !this.isBorderBox()){
8081                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8082                }
8083                if(height < 0){
8084                    height = 0;
8085                }
8086             }
8087             return height;
8088         },
8089
8090         /**
8091          * Set the width of the element
8092          * @param {Number} width The new width
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096         setWidth : function(width, animate){
8097             width = this.adjustWidth(width);
8098             if(!animate || !A){
8099                 this.dom.style.width = this.addUnits(width);
8100             }else{
8101                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8102             }
8103             return this;
8104         },
8105
8106         /**
8107          * Set the height of the element
8108          * @param {Number} height The new height
8109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8110          * @return {Roo.Element} this
8111          */
8112          setHeight : function(height, animate){
8113             height = this.adjustHeight(height);
8114             if(!animate || !A){
8115                 this.dom.style.height = this.addUnits(height);
8116             }else{
8117                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8118             }
8119             return this;
8120         },
8121
8122         /**
8123          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8124          * @param {Number} width The new width
8125          * @param {Number} height The new height
8126          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8127          * @return {Roo.Element} this
8128          */
8129          setSize : function(width, height, animate){
8130             if(typeof width == "object"){ // in case of object from getSize()
8131                 height = width.height; width = width.width;
8132             }
8133             width = this.adjustWidth(width); height = this.adjustHeight(height);
8134             if(!animate || !A){
8135                 this.dom.style.width = this.addUnits(width);
8136                 this.dom.style.height = this.addUnits(height);
8137             }else{
8138                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8145          * @param {Number} x X value for new position (coordinates are page-based)
8146          * @param {Number} y Y value for new position (coordinates are page-based)
8147          * @param {Number} width The new width
8148          * @param {Number} height The new height
8149          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8150          * @return {Roo.Element} this
8151          */
8152         setBounds : function(x, y, width, height, animate){
8153             if(!animate || !A){
8154                 this.setSize(width, height);
8155                 this.setLocation(x, y);
8156             }else{
8157                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8158                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8159                               this.preanim(arguments, 4), 'motion');
8160             }
8161             return this;
8162         },
8163
8164         /**
8165          * 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.
8166          * @param {Roo.lib.Region} region The region to fill
8167          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8168          * @return {Roo.Element} this
8169          */
8170         setRegion : function(region, animate){
8171             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8172             return this;
8173         },
8174
8175         /**
8176          * Appends an event handler
8177          *
8178          * @param {String}   eventName     The type of event to append
8179          * @param {Function} fn        The method the event invokes
8180          * @param {Object} scope       (optional) The scope (this object) of the fn
8181          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8182          */
8183         addListener : function(eventName, fn, scope, options){
8184             if (this.dom) {
8185                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8186             }
8187         },
8188
8189         /**
8190          * Removes an event handler from this element
8191          * @param {String} eventName the type of event to remove
8192          * @param {Function} fn the method the event invokes
8193          * @return {Roo.Element} this
8194          */
8195         removeListener : function(eventName, fn){
8196             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8197             return this;
8198         },
8199
8200         /**
8201          * Removes all previous added listeners from this element
8202          * @return {Roo.Element} this
8203          */
8204         removeAllListeners : function(){
8205             E.purgeElement(this.dom);
8206             return this;
8207         },
8208
8209         relayEvent : function(eventName, observable){
8210             this.on(eventName, function(e){
8211                 observable.fireEvent(eventName, e);
8212             });
8213         },
8214
8215         /**
8216          * Set the opacity of the element
8217          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8218          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8219          * @return {Roo.Element} this
8220          */
8221          setOpacity : function(opacity, animate){
8222             if(!animate || !A){
8223                 var s = this.dom.style;
8224                 if(Roo.isIE){
8225                     s.zoom = 1;
8226                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8227                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8228                 }else{
8229                     s.opacity = opacity;
8230                 }
8231             }else{
8232                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8233             }
8234             return this;
8235         },
8236
8237         /**
8238          * Gets the left X coordinate
8239          * @param {Boolean} local True to get the local css position instead of page coordinate
8240          * @return {Number}
8241          */
8242         getLeft : function(local){
8243             if(!local){
8244                 return this.getX();
8245             }else{
8246                 return parseInt(this.getStyle("left"), 10) || 0;
8247             }
8248         },
8249
8250         /**
8251          * Gets the right X coordinate of the element (element X position + element width)
8252          * @param {Boolean} local True to get the local css position instead of page coordinate
8253          * @return {Number}
8254          */
8255         getRight : function(local){
8256             if(!local){
8257                 return this.getX() + this.getWidth();
8258             }else{
8259                 return (this.getLeft(true) + this.getWidth()) || 0;
8260             }
8261         },
8262
8263         /**
8264          * Gets the top Y coordinate
8265          * @param {Boolean} local True to get the local css position instead of page coordinate
8266          * @return {Number}
8267          */
8268         getTop : function(local) {
8269             if(!local){
8270                 return this.getY();
8271             }else{
8272                 return parseInt(this.getStyle("top"), 10) || 0;
8273             }
8274         },
8275
8276         /**
8277          * Gets the bottom Y coordinate of the element (element Y position + element height)
8278          * @param {Boolean} local True to get the local css position instead of page coordinate
8279          * @return {Number}
8280          */
8281         getBottom : function(local){
8282             if(!local){
8283                 return this.getY() + this.getHeight();
8284             }else{
8285                 return (this.getTop(true) + this.getHeight()) || 0;
8286             }
8287         },
8288
8289         /**
8290         * Initializes positioning on this element. If a desired position is not passed, it will make the
8291         * the element positioned relative IF it is not already positioned.
8292         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8293         * @param {Number} zIndex (optional) The zIndex to apply
8294         * @param {Number} x (optional) Set the page X position
8295         * @param {Number} y (optional) Set the page Y position
8296         */
8297         position : function(pos, zIndex, x, y){
8298             if(!pos){
8299                if(this.getStyle('position') == 'static'){
8300                    this.setStyle('position', 'relative');
8301                }
8302             }else{
8303                 this.setStyle("position", pos);
8304             }
8305             if(zIndex){
8306                 this.setStyle("z-index", zIndex);
8307             }
8308             if(x !== undefined && y !== undefined){
8309                 this.setXY([x, y]);
8310             }else if(x !== undefined){
8311                 this.setX(x);
8312             }else if(y !== undefined){
8313                 this.setY(y);
8314             }
8315         },
8316
8317         /**
8318         * Clear positioning back to the default when the document was loaded
8319         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8320         * @return {Roo.Element} this
8321          */
8322         clearPositioning : function(value){
8323             value = value ||'';
8324             this.setStyle({
8325                 "left": value,
8326                 "right": value,
8327                 "top": value,
8328                 "bottom": value,
8329                 "z-index": "",
8330                 "position" : "static"
8331             });
8332             return this;
8333         },
8334
8335         /**
8336         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8337         * snapshot before performing an update and then restoring the element.
8338         * @return {Object}
8339         */
8340         getPositioning : function(){
8341             var l = this.getStyle("left");
8342             var t = this.getStyle("top");
8343             return {
8344                 "position" : this.getStyle("position"),
8345                 "left" : l,
8346                 "right" : l ? "" : this.getStyle("right"),
8347                 "top" : t,
8348                 "bottom" : t ? "" : this.getStyle("bottom"),
8349                 "z-index" : this.getStyle("z-index")
8350             };
8351         },
8352
8353         /**
8354          * Gets the width of the border(s) for the specified side(s)
8355          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8356          * passing lr would get the border (l)eft width + the border (r)ight width.
8357          * @return {Number} The width of the sides passed added together
8358          */
8359         getBorderWidth : function(side){
8360             return this.addStyles(side, El.borders);
8361         },
8362
8363         /**
8364          * Gets the width of the padding(s) for the specified side(s)
8365          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8366          * passing lr would get the padding (l)eft + the padding (r)ight.
8367          * @return {Number} The padding of the sides passed added together
8368          */
8369         getPadding : function(side){
8370             return this.addStyles(side, El.paddings);
8371         },
8372
8373         /**
8374         * Set positioning with an object returned by getPositioning().
8375         * @param {Object} posCfg
8376         * @return {Roo.Element} this
8377          */
8378         setPositioning : function(pc){
8379             this.applyStyles(pc);
8380             if(pc.right == "auto"){
8381                 this.dom.style.right = "";
8382             }
8383             if(pc.bottom == "auto"){
8384                 this.dom.style.bottom = "";
8385             }
8386             return this;
8387         },
8388
8389         // private
8390         fixDisplay : function(){
8391             if(this.getStyle("display") == "none"){
8392                 this.setStyle("visibility", "hidden");
8393                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8394                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8395                     this.setStyle("display", "block");
8396                 }
8397             }
8398         },
8399
8400         /**
8401          * Quick set left and top adding default units
8402          * @param {String} left The left CSS property value
8403          * @param {String} top The top CSS property value
8404          * @return {Roo.Element} this
8405          */
8406          setLeftTop : function(left, top){
8407             this.dom.style.left = this.addUnits(left);
8408             this.dom.style.top = this.addUnits(top);
8409             return this;
8410         },
8411
8412         /**
8413          * Move this element relative to its current position.
8414          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8415          * @param {Number} distance How far to move the element in pixels
8416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8417          * @return {Roo.Element} this
8418          */
8419          move : function(direction, distance, animate){
8420             var xy = this.getXY();
8421             direction = direction.toLowerCase();
8422             switch(direction){
8423                 case "l":
8424                 case "left":
8425                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8426                     break;
8427                case "r":
8428                case "right":
8429                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8430                     break;
8431                case "t":
8432                case "top":
8433                case "up":
8434                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8435                     break;
8436                case "b":
8437                case "bottom":
8438                case "down":
8439                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8440                     break;
8441             }
8442             return this;
8443         },
8444
8445         /**
8446          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8447          * @return {Roo.Element} this
8448          */
8449         clip : function(){
8450             if(!this.isClipped){
8451                this.isClipped = true;
8452                this.originalClip = {
8453                    "o": this.getStyle("overflow"),
8454                    "x": this.getStyle("overflow-x"),
8455                    "y": this.getStyle("overflow-y")
8456                };
8457                this.setStyle("overflow", "hidden");
8458                this.setStyle("overflow-x", "hidden");
8459                this.setStyle("overflow-y", "hidden");
8460             }
8461             return this;
8462         },
8463
8464         /**
8465          *  Return clipping (overflow) to original clipping before clip() was called
8466          * @return {Roo.Element} this
8467          */
8468         unclip : function(){
8469             if(this.isClipped){
8470                 this.isClipped = false;
8471                 var o = this.originalClip;
8472                 if(o.o){this.setStyle("overflow", o.o);}
8473                 if(o.x){this.setStyle("overflow-x", o.x);}
8474                 if(o.y){this.setStyle("overflow-y", o.y);}
8475             }
8476             return this;
8477         },
8478
8479
8480         /**
8481          * Gets the x,y coordinates specified by the anchor position on the element.
8482          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8483          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8484          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8485          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8486          * @return {Array} [x, y] An array containing the element's x and y coordinates
8487          */
8488         getAnchorXY : function(anchor, local, s){
8489             //Passing a different size is useful for pre-calculating anchors,
8490             //especially for anchored animations that change the el size.
8491
8492             var w, h, vp = false;
8493             if(!s){
8494                 var d = this.dom;
8495                 if(d == document.body || d == document){
8496                     vp = true;
8497                     w = D.getViewWidth(); h = D.getViewHeight();
8498                 }else{
8499                     w = this.getWidth(); h = this.getHeight();
8500                 }
8501             }else{
8502                 w = s.width;  h = s.height;
8503             }
8504             var x = 0, y = 0, r = Math.round;
8505             switch((anchor || "tl").toLowerCase()){
8506                 case "c":
8507                     x = r(w*.5);
8508                     y = r(h*.5);
8509                 break;
8510                 case "t":
8511                     x = r(w*.5);
8512                     y = 0;
8513                 break;
8514                 case "l":
8515                     x = 0;
8516                     y = r(h*.5);
8517                 break;
8518                 case "r":
8519                     x = w;
8520                     y = r(h*.5);
8521                 break;
8522                 case "b":
8523                     x = r(w*.5);
8524                     y = h;
8525                 break;
8526                 case "tl":
8527                     x = 0;
8528                     y = 0;
8529                 break;
8530                 case "bl":
8531                     x = 0;
8532                     y = h;
8533                 break;
8534                 case "br":
8535                     x = w;
8536                     y = h;
8537                 break;
8538                 case "tr":
8539                     x = w;
8540                     y = 0;
8541                 break;
8542             }
8543             if(local === true){
8544                 return [x, y];
8545             }
8546             if(vp){
8547                 var sc = this.getScroll();
8548                 return [x + sc.left, y + sc.top];
8549             }
8550             //Add the element's offset xy
8551             var o = this.getXY();
8552             return [x+o[0], y+o[1]];
8553         },
8554
8555         /**
8556          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8557          * supported position values.
8558          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8559          * @param {String} position The position to align to.
8560          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8561          * @return {Array} [x, y]
8562          */
8563         getAlignToXY : function(el, p, o){
8564             el = Roo.get(el);
8565             var d = this.dom;
8566             if(!el.dom){
8567                 throw "Element.alignTo with an element that doesn't exist";
8568             }
8569             var c = false; //constrain to viewport
8570             var p1 = "", p2 = "";
8571             o = o || [0,0];
8572
8573             if(!p){
8574                 p = "tl-bl";
8575             }else if(p == "?"){
8576                 p = "tl-bl?";
8577             }else if(p.indexOf("-") == -1){
8578                 p = "tl-" + p;
8579             }
8580             p = p.toLowerCase();
8581             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8582             if(!m){
8583                throw "Element.alignTo with an invalid alignment " + p;
8584             }
8585             p1 = m[1]; p2 = m[2]; c = !!m[3];
8586
8587             //Subtract the aligned el's internal xy from the target's offset xy
8588             //plus custom offset to get the aligned el's new offset xy
8589             var a1 = this.getAnchorXY(p1, true);
8590             var a2 = el.getAnchorXY(p2, false);
8591             var x = a2[0] - a1[0] + o[0];
8592             var y = a2[1] - a1[1] + o[1];
8593             if(c){
8594                 //constrain the aligned el to viewport if necessary
8595                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8596                 // 5px of margin for ie
8597                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8598
8599                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8600                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8601                 //otherwise swap the aligned el to the opposite border of the target.
8602                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8603                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8604                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8605                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8606
8607                var doc = document;
8608                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8609                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8610
8611                if((x+w) > dw + scrollX){
8612                     x = swapX ? r.left-w : dw+scrollX-w;
8613                 }
8614                if(x < scrollX){
8615                    x = swapX ? r.right : scrollX;
8616                }
8617                if((y+h) > dh + scrollY){
8618                     y = swapY ? r.top-h : dh+scrollY-h;
8619                 }
8620                if (y < scrollY){
8621                    y = swapY ? r.bottom : scrollY;
8622                }
8623             }
8624             return [x,y];
8625         },
8626
8627         // private
8628         getConstrainToXY : function(){
8629             var os = {top:0, left:0, bottom:0, right: 0};
8630
8631             return function(el, local, offsets, proposedXY){
8632                 el = Roo.get(el);
8633                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8634
8635                 var vw, vh, vx = 0, vy = 0;
8636                 if(el.dom == document.body || el.dom == document){
8637                     vw = Roo.lib.Dom.getViewWidth();
8638                     vh = Roo.lib.Dom.getViewHeight();
8639                 }else{
8640                     vw = el.dom.clientWidth;
8641                     vh = el.dom.clientHeight;
8642                     if(!local){
8643                         var vxy = el.getXY();
8644                         vx = vxy[0];
8645                         vy = vxy[1];
8646                     }
8647                 }
8648
8649                 var s = el.getScroll();
8650
8651                 vx += offsets.left + s.left;
8652                 vy += offsets.top + s.top;
8653
8654                 vw -= offsets.right;
8655                 vh -= offsets.bottom;
8656
8657                 var vr = vx+vw;
8658                 var vb = vy+vh;
8659
8660                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8661                 var x = xy[0], y = xy[1];
8662                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8663
8664                 // only move it if it needs it
8665                 var moved = false;
8666
8667                 // first validate right/bottom
8668                 if((x + w) > vr){
8669                     x = vr - w;
8670                     moved = true;
8671                 }
8672                 if((y + h) > vb){
8673                     y = vb - h;
8674                     moved = true;
8675                 }
8676                 // then make sure top/left isn't negative
8677                 if(x < vx){
8678                     x = vx;
8679                     moved = true;
8680                 }
8681                 if(y < vy){
8682                     y = vy;
8683                     moved = true;
8684                 }
8685                 return moved ? [x, y] : false;
8686             };
8687         }(),
8688
8689         // private
8690         adjustForConstraints : function(xy, parent, offsets){
8691             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8692         },
8693
8694         /**
8695          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8696          * document it aligns it to the viewport.
8697          * The position parameter is optional, and can be specified in any one of the following formats:
8698          * <ul>
8699          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8700          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8701          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8702          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8703          *   <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
8704          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8705          * </ul>
8706          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8707          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8708          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8709          * that specified in order to enforce the viewport constraints.
8710          * Following are all of the supported anchor positions:
8711     <pre>
8712     Value  Description
8713     -----  -----------------------------
8714     tl     The top left corner (default)
8715     t      The center of the top edge
8716     tr     The top right corner
8717     l      The center of the left edge
8718     c      In the center of the element
8719     r      The center of the right edge
8720     bl     The bottom left corner
8721     b      The center of the bottom edge
8722     br     The bottom right corner
8723     </pre>
8724     Example Usage:
8725     <pre><code>
8726     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8727     el.alignTo("other-el");
8728
8729     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8730     el.alignTo("other-el", "tr?");
8731
8732     // align the bottom right corner of el with the center left edge of other-el
8733     el.alignTo("other-el", "br-l?");
8734
8735     // align the center of el with the bottom left corner of other-el and
8736     // adjust the x position by -6 pixels (and the y position by 0)
8737     el.alignTo("other-el", "c-bl", [-6, 0]);
8738     </code></pre>
8739          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8740          * @param {String} position The position to align to.
8741          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8743          * @return {Roo.Element} this
8744          */
8745         alignTo : function(element, position, offsets, animate){
8746             var xy = this.getAlignToXY(element, position, offsets);
8747             this.setXY(xy, this.preanim(arguments, 3));
8748             return this;
8749         },
8750
8751         /**
8752          * Anchors an element to another element and realigns it when the window is resized.
8753          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8754          * @param {String} position The position to align to.
8755          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8756          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8757          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8758          * is a number, it is used as the buffer delay (defaults to 50ms).
8759          * @param {Function} callback The function to call after the animation finishes
8760          * @return {Roo.Element} this
8761          */
8762         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8763             var action = function(){
8764                 this.alignTo(el, alignment, offsets, animate);
8765                 Roo.callback(callback, this);
8766             };
8767             Roo.EventManager.onWindowResize(action, this);
8768             var tm = typeof monitorScroll;
8769             if(tm != 'undefined'){
8770                 Roo.EventManager.on(window, 'scroll', action, this,
8771                     {buffer: tm == 'number' ? monitorScroll : 50});
8772             }
8773             action.call(this); // align immediately
8774             return this;
8775         },
8776         /**
8777          * Clears any opacity settings from this element. Required in some cases for IE.
8778          * @return {Roo.Element} this
8779          */
8780         clearOpacity : function(){
8781             if (window.ActiveXObject) {
8782                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8783                     this.dom.style.filter = "";
8784                 }
8785             } else {
8786                 this.dom.style.opacity = "";
8787                 this.dom.style["-moz-opacity"] = "";
8788                 this.dom.style["-khtml-opacity"] = "";
8789             }
8790             return this;
8791         },
8792
8793         /**
8794          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8796          * @return {Roo.Element} this
8797          */
8798         hide : function(animate){
8799             this.setVisible(false, this.preanim(arguments, 0));
8800             return this;
8801         },
8802
8803         /**
8804         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8805         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8806          * @return {Roo.Element} this
8807          */
8808         show : function(animate){
8809             this.setVisible(true, this.preanim(arguments, 0));
8810             return this;
8811         },
8812
8813         /**
8814          * @private Test if size has a unit, otherwise appends the default
8815          */
8816         addUnits : function(size){
8817             return Roo.Element.addUnits(size, this.defaultUnit);
8818         },
8819
8820         /**
8821          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8822          * @return {Roo.Element} this
8823          */
8824         beginMeasure : function(){
8825             var el = this.dom;
8826             if(el.offsetWidth || el.offsetHeight){
8827                 return this; // offsets work already
8828             }
8829             var changed = [];
8830             var p = this.dom, b = document.body; // start with this element
8831             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8832                 var pe = Roo.get(p);
8833                 if(pe.getStyle('display') == 'none'){
8834                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8835                     p.style.visibility = "hidden";
8836                     p.style.display = "block";
8837                 }
8838                 p = p.parentNode;
8839             }
8840             this._measureChanged = changed;
8841             return this;
8842
8843         },
8844
8845         /**
8846          * Restores displays to before beginMeasure was called
8847          * @return {Roo.Element} this
8848          */
8849         endMeasure : function(){
8850             var changed = this._measureChanged;
8851             if(changed){
8852                 for(var i = 0, len = changed.length; i < len; i++) {
8853                     var r = changed[i];
8854                     r.el.style.visibility = r.visibility;
8855                     r.el.style.display = "none";
8856                 }
8857                 this._measureChanged = null;
8858             }
8859             return this;
8860         },
8861
8862         /**
8863         * Update the innerHTML of this element, optionally searching for and processing scripts
8864         * @param {String} html The new HTML
8865         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8866         * @param {Function} callback For async script loading you can be noticed when the update completes
8867         * @return {Roo.Element} this
8868          */
8869         update : function(html, loadScripts, callback){
8870             if(typeof html == "undefined"){
8871                 html = "";
8872             }
8873             if(loadScripts !== true){
8874                 this.dom.innerHTML = html;
8875                 if(typeof callback == "function"){
8876                     callback();
8877                 }
8878                 return this;
8879             }
8880             var id = Roo.id();
8881             var dom = this.dom;
8882
8883             html += '<span id="' + id + '"></span>';
8884
8885             E.onAvailable(id, function(){
8886                 var hd = document.getElementsByTagName("head")[0];
8887                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8888                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8889                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8890
8891                 var match;
8892                 while(match = re.exec(html)){
8893                     var attrs = match[1];
8894                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8895                     if(srcMatch && srcMatch[2]){
8896                        var s = document.createElement("script");
8897                        s.src = srcMatch[2];
8898                        var typeMatch = attrs.match(typeRe);
8899                        if(typeMatch && typeMatch[2]){
8900                            s.type = typeMatch[2];
8901                        }
8902                        hd.appendChild(s);
8903                     }else if(match[2] && match[2].length > 0){
8904                         if(window.execScript) {
8905                            window.execScript(match[2]);
8906                         } else {
8907                             /**
8908                              * eval:var:id
8909                              * eval:var:dom
8910                              * eval:var:html
8911                              * 
8912                              */
8913                            window.eval(match[2]);
8914                         }
8915                     }
8916                 }
8917                 var el = document.getElementById(id);
8918                 if(el){el.parentNode.removeChild(el);}
8919                 if(typeof callback == "function"){
8920                     callback();
8921                 }
8922             });
8923             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8924             return this;
8925         },
8926
8927         /**
8928          * Direct access to the UpdateManager update() method (takes the same parameters).
8929          * @param {String/Function} url The url for this request or a function to call to get the url
8930          * @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}
8931          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8932          * @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.
8933          * @return {Roo.Element} this
8934          */
8935         load : function(){
8936             var um = this.getUpdateManager();
8937             um.update.apply(um, arguments);
8938             return this;
8939         },
8940
8941         /**
8942         * Gets this element's UpdateManager
8943         * @return {Roo.UpdateManager} The UpdateManager
8944         */
8945         getUpdateManager : function(){
8946             if(!this.updateManager){
8947                 this.updateManager = new Roo.UpdateManager(this);
8948             }
8949             return this.updateManager;
8950         },
8951
8952         /**
8953          * Disables text selection for this element (normalized across browsers)
8954          * @return {Roo.Element} this
8955          */
8956         unselectable : function(){
8957             this.dom.unselectable = "on";
8958             this.swallowEvent("selectstart", true);
8959             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8960             this.addClass("x-unselectable");
8961             return this;
8962         },
8963
8964         /**
8965         * Calculates the x, y to center this element on the screen
8966         * @return {Array} The x, y values [x, y]
8967         */
8968         getCenterXY : function(){
8969             return this.getAlignToXY(document, 'c-c');
8970         },
8971
8972         /**
8973         * Centers the Element in either the viewport, or another Element.
8974         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8975         */
8976         center : function(centerIn){
8977             this.alignTo(centerIn || document, 'c-c');
8978             return this;
8979         },
8980
8981         /**
8982          * Tests various css rules/browsers to determine if this element uses a border box
8983          * @return {Boolean}
8984          */
8985         isBorderBox : function(){
8986             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8987         },
8988
8989         /**
8990          * Return a box {x, y, width, height} that can be used to set another elements
8991          * size/location to match this element.
8992          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8993          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8994          * @return {Object} box An object in the format {x, y, width, height}
8995          */
8996         getBox : function(contentBox, local){
8997             var xy;
8998             if(!local){
8999                 xy = this.getXY();
9000             }else{
9001                 var left = parseInt(this.getStyle("left"), 10) || 0;
9002                 var top = parseInt(this.getStyle("top"), 10) || 0;
9003                 xy = [left, top];
9004             }
9005             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9006             if(!contentBox){
9007                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9008             }else{
9009                 var l = this.getBorderWidth("l")+this.getPadding("l");
9010                 var r = this.getBorderWidth("r")+this.getPadding("r");
9011                 var t = this.getBorderWidth("t")+this.getPadding("t");
9012                 var b = this.getBorderWidth("b")+this.getPadding("b");
9013                 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)};
9014             }
9015             bx.right = bx.x + bx.width;
9016             bx.bottom = bx.y + bx.height;
9017             return bx;
9018         },
9019
9020         /**
9021          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9022          for more information about the sides.
9023          * @param {String} sides
9024          * @return {Number}
9025          */
9026         getFrameWidth : function(sides, onlyContentBox){
9027             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9028         },
9029
9030         /**
9031          * 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.
9032          * @param {Object} box The box to fill {x, y, width, height}
9033          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9035          * @return {Roo.Element} this
9036          */
9037         setBox : function(box, adjust, animate){
9038             var w = box.width, h = box.height;
9039             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9040                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9041                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9042             }
9043             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9044             return this;
9045         },
9046
9047         /**
9048          * Forces the browser to repaint this element
9049          * @return {Roo.Element} this
9050          */
9051          repaint : function(){
9052             var dom = this.dom;
9053             this.addClass("x-repaint");
9054             setTimeout(function(){
9055                 Roo.get(dom).removeClass("x-repaint");
9056             }, 1);
9057             return this;
9058         },
9059
9060         /**
9061          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9062          * then it returns the calculated width of the sides (see getPadding)
9063          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9064          * @return {Object/Number}
9065          */
9066         getMargins : function(side){
9067             if(!side){
9068                 return {
9069                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9070                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9071                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9072                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9073                 };
9074             }else{
9075                 return this.addStyles(side, El.margins);
9076              }
9077         },
9078
9079         // private
9080         addStyles : function(sides, styles){
9081             var val = 0, v, w;
9082             for(var i = 0, len = sides.length; i < len; i++){
9083                 v = this.getStyle(styles[sides.charAt(i)]);
9084                 if(v){
9085                      w = parseInt(v, 10);
9086                      if(w){ val += w; }
9087                 }
9088             }
9089             return val;
9090         },
9091
9092         /**
9093          * Creates a proxy element of this element
9094          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9095          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9096          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9097          * @return {Roo.Element} The new proxy element
9098          */
9099         createProxy : function(config, renderTo, matchBox){
9100             if(renderTo){
9101                 renderTo = Roo.getDom(renderTo);
9102             }else{
9103                 renderTo = document.body;
9104             }
9105             config = typeof config == "object" ?
9106                 config : {tag : "div", cls: config};
9107             var proxy = Roo.DomHelper.append(renderTo, config, true);
9108             if(matchBox){
9109                proxy.setBox(this.getBox());
9110             }
9111             return proxy;
9112         },
9113
9114         /**
9115          * Puts a mask over this element to disable user interaction. Requires core.css.
9116          * This method can only be applied to elements which accept child nodes.
9117          * @param {String} msg (optional) A message to display in the mask
9118          * @param {String} msgCls (optional) A css class to apply to the msg element
9119          * @return {Element} The mask  element
9120          */
9121         mask : function(msg, msgCls)
9122         {
9123             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9124                 this.setStyle("position", "relative");
9125             }
9126             if(!this._mask){
9127                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9128             }
9129             
9130             this.addClass("x-masked");
9131             this._mask.setDisplayed(true);
9132             
9133             // we wander
9134             var z = 0;
9135             var dom = this.dom;
9136             while (dom && dom.style) {
9137                 if (!isNaN(parseInt(dom.style.zIndex))) {
9138                     z = Math.max(z, parseInt(dom.style.zIndex));
9139                 }
9140                 dom = dom.parentNode;
9141             }
9142             // if we are masking the body - then it hides everything..
9143             if (this.dom == document.body) {
9144                 z = 1000000;
9145                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9146                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9147             }
9148            
9149             if(typeof msg == 'string'){
9150                 if(!this._maskMsg){
9151                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9152                         cls: "roo-el-mask-msg", 
9153                         cn: [
9154                             {
9155                                 tag: 'i',
9156                                 cls: 'fa fa-spinner fa-spin'
9157                             },
9158                             {
9159                                 tag: 'div'
9160                             }   
9161                         ]
9162                     }, true);
9163                 }
9164                 var mm = this._maskMsg;
9165                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9166                 if (mm.dom.lastChild) { // weird IE issue?
9167                     mm.dom.lastChild.innerHTML = msg;
9168                 }
9169                 mm.setDisplayed(true);
9170                 mm.center(this);
9171                 mm.setStyle('z-index', z + 102);
9172             }
9173             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9174                 this._mask.setHeight(this.getHeight());
9175             }
9176             this._mask.setStyle('z-index', z + 100);
9177             
9178             return this._mask;
9179         },
9180
9181         /**
9182          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9183          * it is cached for reuse.
9184          */
9185         unmask : function(removeEl){
9186             if(this._mask){
9187                 if(removeEl === true){
9188                     this._mask.remove();
9189                     delete this._mask;
9190                     if(this._maskMsg){
9191                         this._maskMsg.remove();
9192                         delete this._maskMsg;
9193                     }
9194                 }else{
9195                     this._mask.setDisplayed(false);
9196                     if(this._maskMsg){
9197                         this._maskMsg.setDisplayed(false);
9198                     }
9199                 }
9200             }
9201             this.removeClass("x-masked");
9202         },
9203
9204         /**
9205          * Returns true if this element is masked
9206          * @return {Boolean}
9207          */
9208         isMasked : function(){
9209             return this._mask && this._mask.isVisible();
9210         },
9211
9212         /**
9213          * Creates an iframe shim for this element to keep selects and other windowed objects from
9214          * showing through.
9215          * @return {Roo.Element} The new shim element
9216          */
9217         createShim : function(){
9218             var el = document.createElement('iframe');
9219             el.frameBorder = 'no';
9220             el.className = 'roo-shim';
9221             if(Roo.isIE && Roo.isSecure){
9222                 el.src = Roo.SSL_SECURE_URL;
9223             }
9224             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9225             shim.autoBoxAdjust = false;
9226             return shim;
9227         },
9228
9229         /**
9230          * Removes this element from the DOM and deletes it from the cache
9231          */
9232         remove : function(){
9233             if(this.dom.parentNode){
9234                 this.dom.parentNode.removeChild(this.dom);
9235             }
9236             delete El.cache[this.dom.id];
9237         },
9238
9239         /**
9240          * Sets up event handlers to add and remove a css class when the mouse is over this element
9241          * @param {String} className
9242          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9243          * mouseout events for children elements
9244          * @return {Roo.Element} this
9245          */
9246         addClassOnOver : function(className, preventFlicker){
9247             this.on("mouseover", function(){
9248                 Roo.fly(this, '_internal').addClass(className);
9249             }, this.dom);
9250             var removeFn = function(e){
9251                 if(preventFlicker !== true || !e.within(this, true)){
9252                     Roo.fly(this, '_internal').removeClass(className);
9253                 }
9254             };
9255             this.on("mouseout", removeFn, this.dom);
9256             return this;
9257         },
9258
9259         /**
9260          * Sets up event handlers to add and remove a css class when this element has the focus
9261          * @param {String} className
9262          * @return {Roo.Element} this
9263          */
9264         addClassOnFocus : function(className){
9265             this.on("focus", function(){
9266                 Roo.fly(this, '_internal').addClass(className);
9267             }, this.dom);
9268             this.on("blur", function(){
9269                 Roo.fly(this, '_internal').removeClass(className);
9270             }, this.dom);
9271             return this;
9272         },
9273         /**
9274          * 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)
9275          * @param {String} className
9276          * @return {Roo.Element} this
9277          */
9278         addClassOnClick : function(className){
9279             var dom = this.dom;
9280             this.on("mousedown", function(){
9281                 Roo.fly(dom, '_internal').addClass(className);
9282                 var d = Roo.get(document);
9283                 var fn = function(){
9284                     Roo.fly(dom, '_internal').removeClass(className);
9285                     d.removeListener("mouseup", fn);
9286                 };
9287                 d.on("mouseup", fn);
9288             });
9289             return this;
9290         },
9291
9292         /**
9293          * Stops the specified event from bubbling and optionally prevents the default action
9294          * @param {String} eventName
9295          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9296          * @return {Roo.Element} this
9297          */
9298         swallowEvent : function(eventName, preventDefault){
9299             var fn = function(e){
9300                 e.stopPropagation();
9301                 if(preventDefault){
9302                     e.preventDefault();
9303                 }
9304             };
9305             if(eventName instanceof Array){
9306                 for(var i = 0, len = eventName.length; i < len; i++){
9307                      this.on(eventName[i], fn);
9308                 }
9309                 return this;
9310             }
9311             this.on(eventName, fn);
9312             return this;
9313         },
9314
9315         /**
9316          * @private
9317          */
9318       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9319
9320         /**
9321          * Sizes this element to its parent element's dimensions performing
9322          * neccessary box adjustments.
9323          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9324          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9325          * @return {Roo.Element} this
9326          */
9327         fitToParent : function(monitorResize, targetParent) {
9328           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9329           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9330           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9331             return;
9332           }
9333           var p = Roo.get(targetParent || this.dom.parentNode);
9334           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9335           if (monitorResize === true) {
9336             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9337             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9338           }
9339           return this;
9340         },
9341
9342         /**
9343          * Gets the next sibling, skipping text nodes
9344          * @return {HTMLElement} The next sibling or null
9345          */
9346         getNextSibling : function(){
9347             var n = this.dom.nextSibling;
9348             while(n && n.nodeType != 1){
9349                 n = n.nextSibling;
9350             }
9351             return n;
9352         },
9353
9354         /**
9355          * Gets the previous sibling, skipping text nodes
9356          * @return {HTMLElement} The previous sibling or null
9357          */
9358         getPrevSibling : function(){
9359             var n = this.dom.previousSibling;
9360             while(n && n.nodeType != 1){
9361                 n = n.previousSibling;
9362             }
9363             return n;
9364         },
9365
9366
9367         /**
9368          * Appends the passed element(s) to this element
9369          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9370          * @return {Roo.Element} this
9371          */
9372         appendChild: function(el){
9373             el = Roo.get(el);
9374             el.appendTo(this);
9375             return this;
9376         },
9377
9378         /**
9379          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9380          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9381          * automatically generated with the specified attributes.
9382          * @param {HTMLElement} insertBefore (optional) a child element of this element
9383          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9384          * @return {Roo.Element} The new child element
9385          */
9386         createChild: function(config, insertBefore, returnDom){
9387             config = config || {tag:'div'};
9388             if(insertBefore){
9389                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9390             }
9391             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9392         },
9393
9394         /**
9395          * Appends this element to the passed element
9396          * @param {String/HTMLElement/Element} el The new parent element
9397          * @return {Roo.Element} this
9398          */
9399         appendTo: function(el){
9400             el = Roo.getDom(el);
9401             el.appendChild(this.dom);
9402             return this;
9403         },
9404
9405         /**
9406          * Inserts this element before the passed element in the DOM
9407          * @param {String/HTMLElement/Element} el The element to insert before
9408          * @return {Roo.Element} this
9409          */
9410         insertBefore: function(el){
9411             el = Roo.getDom(el);
9412             el.parentNode.insertBefore(this.dom, el);
9413             return this;
9414         },
9415
9416         /**
9417          * Inserts this element after the passed element in the DOM
9418          * @param {String/HTMLElement/Element} el The element to insert after
9419          * @return {Roo.Element} this
9420          */
9421         insertAfter: function(el){
9422             el = Roo.getDom(el);
9423             el.parentNode.insertBefore(this.dom, el.nextSibling);
9424             return this;
9425         },
9426
9427         /**
9428          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9429          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9430          * @return {Roo.Element} The new child
9431          */
9432         insertFirst: function(el, returnDom){
9433             el = el || {};
9434             if(typeof el == 'object' && !el.nodeType){ // dh config
9435                 return this.createChild(el, this.dom.firstChild, returnDom);
9436             }else{
9437                 el = Roo.getDom(el);
9438                 this.dom.insertBefore(el, this.dom.firstChild);
9439                 return !returnDom ? Roo.get(el) : el;
9440             }
9441         },
9442
9443         /**
9444          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9445          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9446          * @param {String} where (optional) 'before' or 'after' defaults to before
9447          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9448          * @return {Roo.Element} the inserted Element
9449          */
9450         insertSibling: function(el, where, returnDom){
9451             where = where ? where.toLowerCase() : 'before';
9452             el = el || {};
9453             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9454
9455             if(typeof el == 'object' && !el.nodeType){ // dh config
9456                 if(where == 'after' && !this.dom.nextSibling){
9457                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9458                 }else{
9459                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9460                 }
9461
9462             }else{
9463                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9464                             where == 'before' ? this.dom : this.dom.nextSibling);
9465                 if(!returnDom){
9466                     rt = Roo.get(rt);
9467                 }
9468             }
9469             return rt;
9470         },
9471
9472         /**
9473          * Creates and wraps this element with another element
9474          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9475          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9476          * @return {HTMLElement/Element} The newly created wrapper element
9477          */
9478         wrap: function(config, returnDom){
9479             if(!config){
9480                 config = {tag: "div"};
9481             }
9482             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9483             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9484             return newEl;
9485         },
9486
9487         /**
9488          * Replaces the passed element with this element
9489          * @param {String/HTMLElement/Element} el The element to replace
9490          * @return {Roo.Element} this
9491          */
9492         replace: function(el){
9493             el = Roo.get(el);
9494             this.insertBefore(el);
9495             el.remove();
9496             return this;
9497         },
9498
9499         /**
9500          * Inserts an html fragment into this element
9501          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9502          * @param {String} html The HTML fragment
9503          * @param {Boolean} returnEl True to return an Roo.Element
9504          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9505          */
9506         insertHtml : function(where, html, returnEl){
9507             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9508             return returnEl ? Roo.get(el) : el;
9509         },
9510
9511         /**
9512          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9513          * @param {Object} o The object with the attributes
9514          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9515          * @return {Roo.Element} this
9516          */
9517         set : function(o, useSet){
9518             var el = this.dom;
9519             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9520             for(var attr in o){
9521                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9522                 if(attr=="cls"){
9523                     el.className = o["cls"];
9524                 }else{
9525                     if(useSet) {
9526                         el.setAttribute(attr, o[attr]);
9527                     } else {
9528                         el[attr] = o[attr];
9529                     }
9530                 }
9531             }
9532             if(o.style){
9533                 Roo.DomHelper.applyStyles(el, o.style);
9534             }
9535             return this;
9536         },
9537
9538         /**
9539          * Convenience method for constructing a KeyMap
9540          * @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:
9541          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9542          * @param {Function} fn The function to call
9543          * @param {Object} scope (optional) The scope of the function
9544          * @return {Roo.KeyMap} The KeyMap created
9545          */
9546         addKeyListener : function(key, fn, scope){
9547             var config;
9548             if(typeof key != "object" || key instanceof Array){
9549                 config = {
9550                     key: key,
9551                     fn: fn,
9552                     scope: scope
9553                 };
9554             }else{
9555                 config = {
9556                     key : key.key,
9557                     shift : key.shift,
9558                     ctrl : key.ctrl,
9559                     alt : key.alt,
9560                     fn: fn,
9561                     scope: scope
9562                 };
9563             }
9564             return new Roo.KeyMap(this, config);
9565         },
9566
9567         /**
9568          * Creates a KeyMap for this element
9569          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9570          * @return {Roo.KeyMap} The KeyMap created
9571          */
9572         addKeyMap : function(config){
9573             return new Roo.KeyMap(this, config);
9574         },
9575
9576         /**
9577          * Returns true if this element is scrollable.
9578          * @return {Boolean}
9579          */
9580          isScrollable : function(){
9581             var dom = this.dom;
9582             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9583         },
9584
9585         /**
9586          * 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().
9587          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9588          * @param {Number} value The new scroll value
9589          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9590          * @return {Element} this
9591          */
9592
9593         scrollTo : function(side, value, animate){
9594             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9595             if(!animate || !A){
9596                 this.dom[prop] = value;
9597             }else{
9598                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9599                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9600             }
9601             return this;
9602         },
9603
9604         /**
9605          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9606          * within this element's scrollable range.
9607          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9608          * @param {Number} distance How far to scroll the element in pixels
9609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9610          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9611          * was scrolled as far as it could go.
9612          */
9613          scroll : function(direction, distance, animate){
9614              if(!this.isScrollable()){
9615                  return;
9616              }
9617              var el = this.dom;
9618              var l = el.scrollLeft, t = el.scrollTop;
9619              var w = el.scrollWidth, h = el.scrollHeight;
9620              var cw = el.clientWidth, ch = el.clientHeight;
9621              direction = direction.toLowerCase();
9622              var scrolled = false;
9623              var a = this.preanim(arguments, 2);
9624              switch(direction){
9625                  case "l":
9626                  case "left":
9627                      if(w - l > cw){
9628                          var v = Math.min(l + distance, w-cw);
9629                          this.scrollTo("left", v, a);
9630                          scrolled = true;
9631                      }
9632                      break;
9633                 case "r":
9634                 case "right":
9635                      if(l > 0){
9636                          var v = Math.max(l - distance, 0);
9637                          this.scrollTo("left", v, a);
9638                          scrolled = true;
9639                      }
9640                      break;
9641                 case "t":
9642                 case "top":
9643                 case "up":
9644                      if(t > 0){
9645                          var v = Math.max(t - distance, 0);
9646                          this.scrollTo("top", v, a);
9647                          scrolled = true;
9648                      }
9649                      break;
9650                 case "b":
9651                 case "bottom":
9652                 case "down":
9653                      if(h - t > ch){
9654                          var v = Math.min(t + distance, h-ch);
9655                          this.scrollTo("top", v, a);
9656                          scrolled = true;
9657                      }
9658                      break;
9659              }
9660              return scrolled;
9661         },
9662
9663         /**
9664          * Translates the passed page coordinates into left/top css values for this element
9665          * @param {Number/Array} x The page x or an array containing [x, y]
9666          * @param {Number} y The page y
9667          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9668          */
9669         translatePoints : function(x, y){
9670             if(typeof x == 'object' || x instanceof Array){
9671                 y = x[1]; x = x[0];
9672             }
9673             var p = this.getStyle('position');
9674             var o = this.getXY();
9675
9676             var l = parseInt(this.getStyle('left'), 10);
9677             var t = parseInt(this.getStyle('top'), 10);
9678
9679             if(isNaN(l)){
9680                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9681             }
9682             if(isNaN(t)){
9683                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9684             }
9685
9686             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9687         },
9688
9689         /**
9690          * Returns the current scroll position of the element.
9691          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9692          */
9693         getScroll : function(){
9694             var d = this.dom, doc = document;
9695             if(d == doc || d == doc.body){
9696                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9697                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9698                 return {left: l, top: t};
9699             }else{
9700                 return {left: d.scrollLeft, top: d.scrollTop};
9701             }
9702         },
9703
9704         /**
9705          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9706          * are convert to standard 6 digit hex color.
9707          * @param {String} attr The css attribute
9708          * @param {String} defaultValue The default value to use when a valid color isn't found
9709          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9710          * YUI color anims.
9711          */
9712         getColor : function(attr, defaultValue, prefix){
9713             var v = this.getStyle(attr);
9714             if(!v || v == "transparent" || v == "inherit") {
9715                 return defaultValue;
9716             }
9717             var color = typeof prefix == "undefined" ? "#" : prefix;
9718             if(v.substr(0, 4) == "rgb("){
9719                 var rvs = v.slice(4, v.length -1).split(",");
9720                 for(var i = 0; i < 3; i++){
9721                     var h = parseInt(rvs[i]).toString(16);
9722                     if(h < 16){
9723                         h = "0" + h;
9724                     }
9725                     color += h;
9726                 }
9727             } else {
9728                 if(v.substr(0, 1) == "#"){
9729                     if(v.length == 4) {
9730                         for(var i = 1; i < 4; i++){
9731                             var c = v.charAt(i);
9732                             color +=  c + c;
9733                         }
9734                     }else if(v.length == 7){
9735                         color += v.substr(1);
9736                     }
9737                 }
9738             }
9739             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9740         },
9741
9742         /**
9743          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9744          * gradient background, rounded corners and a 4-way shadow.
9745          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9746          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9747          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9748          * @return {Roo.Element} this
9749          */
9750         boxWrap : function(cls){
9751             cls = cls || 'x-box';
9752             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9753             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9754             return el;
9755         },
9756
9757         /**
9758          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9759          * @param {String} namespace The namespace in which to look for the attribute
9760          * @param {String} name The attribute name
9761          * @return {String} The attribute value
9762          */
9763         getAttributeNS : Roo.isIE ? function(ns, name){
9764             var d = this.dom;
9765             var type = typeof d[ns+":"+name];
9766             if(type != 'undefined' && type != 'unknown'){
9767                 return d[ns+":"+name];
9768             }
9769             return d[name];
9770         } : function(ns, name){
9771             var d = this.dom;
9772             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9773         },
9774         
9775         
9776         /**
9777          * Sets or Returns the value the dom attribute value
9778          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9779          * @param {String} value (optional) The value to set the attribute to
9780          * @return {String} The attribute value
9781          */
9782         attr : function(name){
9783             if (arguments.length > 1) {
9784                 this.dom.setAttribute(name, arguments[1]);
9785                 return arguments[1];
9786             }
9787             if (typeof(name) == 'object') {
9788                 for(var i in name) {
9789                     this.attr(i, name[i]);
9790                 }
9791                 return name;
9792             }
9793             
9794             
9795             if (!this.dom.hasAttribute(name)) {
9796                 return undefined;
9797             }
9798             return this.dom.getAttribute(name);
9799         }
9800         
9801         
9802         
9803     };
9804
9805     var ep = El.prototype;
9806
9807     /**
9808      * Appends an event handler (Shorthand for addListener)
9809      * @param {String}   eventName     The type of event to append
9810      * @param {Function} fn        The method the event invokes
9811      * @param {Object} scope       (optional) The scope (this object) of the fn
9812      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9813      * @method
9814      */
9815     ep.on = ep.addListener;
9816         // backwards compat
9817     ep.mon = ep.addListener;
9818
9819     /**
9820      * Removes an event handler from this element (shorthand for removeListener)
9821      * @param {String} eventName the type of event to remove
9822      * @param {Function} fn the method the event invokes
9823      * @return {Roo.Element} this
9824      * @method
9825      */
9826     ep.un = ep.removeListener;
9827
9828     /**
9829      * true to automatically adjust width and height settings for box-model issues (default to true)
9830      */
9831     ep.autoBoxAdjust = true;
9832
9833     // private
9834     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9835
9836     // private
9837     El.addUnits = function(v, defaultUnit){
9838         if(v === "" || v == "auto"){
9839             return v;
9840         }
9841         if(v === undefined){
9842             return '';
9843         }
9844         if(typeof v == "number" || !El.unitPattern.test(v)){
9845             return v + (defaultUnit || 'px');
9846         }
9847         return v;
9848     };
9849
9850     // special markup used throughout Roo when box wrapping elements
9851     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>';
9852     /**
9853      * Visibility mode constant - Use visibility to hide element
9854      * @static
9855      * @type Number
9856      */
9857     El.VISIBILITY = 1;
9858     /**
9859      * Visibility mode constant - Use display to hide element
9860      * @static
9861      * @type Number
9862      */
9863     El.DISPLAY = 2;
9864
9865     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9866     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9867     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9868
9869
9870
9871     /**
9872      * @private
9873      */
9874     El.cache = {};
9875
9876     var docEl;
9877
9878     /**
9879      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9880      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @static
9884      */
9885     El.get = function(el){
9886         var ex, elm, id;
9887         if(!el){ return null; }
9888         if(typeof el == "string"){ // element id
9889             if(!(elm = document.getElementById(el))){
9890                 return null;
9891             }
9892             if(ex = El.cache[el]){
9893                 ex.dom = elm;
9894             }else{
9895                 ex = El.cache[el] = new El(elm);
9896             }
9897             return ex;
9898         }else if(el.tagName){ // dom element
9899             if(!(id = el.id)){
9900                 id = Roo.id(el);
9901             }
9902             if(ex = El.cache[id]){
9903                 ex.dom = el;
9904             }else{
9905                 ex = El.cache[id] = new El(el);
9906             }
9907             return ex;
9908         }else if(el instanceof El){
9909             if(el != docEl){
9910                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9911                                                               // catch case where it hasn't been appended
9912                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9913             }
9914             return el;
9915         }else if(el.isComposite){
9916             return el;
9917         }else if(el instanceof Array){
9918             return El.select(el);
9919         }else if(el == document){
9920             // create a bogus element object representing the document object
9921             if(!docEl){
9922                 var f = function(){};
9923                 f.prototype = El.prototype;
9924                 docEl = new f();
9925                 docEl.dom = document;
9926             }
9927             return docEl;
9928         }
9929         return null;
9930     };
9931
9932     // private
9933     El.uncache = function(el){
9934         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9935             if(a[i]){
9936                 delete El.cache[a[i].id || a[i]];
9937             }
9938         }
9939     };
9940
9941     // private
9942     // Garbage collection - uncache elements/purge listeners on orphaned elements
9943     // so we don't hold a reference and cause the browser to retain them
9944     El.garbageCollect = function(){
9945         if(!Roo.enableGarbageCollector){
9946             clearInterval(El.collectorThread);
9947             return;
9948         }
9949         for(var eid in El.cache){
9950             var el = El.cache[eid], d = el.dom;
9951             // -------------------------------------------------------
9952             // Determining what is garbage:
9953             // -------------------------------------------------------
9954             // !d
9955             // dom node is null, definitely garbage
9956             // -------------------------------------------------------
9957             // !d.parentNode
9958             // no parentNode == direct orphan, definitely garbage
9959             // -------------------------------------------------------
9960             // !d.offsetParent && !document.getElementById(eid)
9961             // display none elements have no offsetParent so we will
9962             // also try to look it up by it's id. However, check
9963             // offsetParent first so we don't do unneeded lookups.
9964             // This enables collection of elements that are not orphans
9965             // directly, but somewhere up the line they have an orphan
9966             // parent.
9967             // -------------------------------------------------------
9968             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9969                 delete El.cache[eid];
9970                 if(d && Roo.enableListenerCollection){
9971                     E.purgeElement(d);
9972                 }
9973             }
9974         }
9975     }
9976     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9977
9978
9979     // dom is optional
9980     El.Flyweight = function(dom){
9981         this.dom = dom;
9982     };
9983     El.Flyweight.prototype = El.prototype;
9984
9985     El._flyweights = {};
9986     /**
9987      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9988      * the dom node can be overwritten by other code.
9989      * @param {String/HTMLElement} el The dom node or id
9990      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9991      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9992      * @static
9993      * @return {Element} The shared Element object
9994      */
9995     El.fly = function(el, named){
9996         named = named || '_global';
9997         el = Roo.getDom(el);
9998         if(!el){
9999             return null;
10000         }
10001         if(!El._flyweights[named]){
10002             El._flyweights[named] = new El.Flyweight();
10003         }
10004         El._flyweights[named].dom = el;
10005         return El._flyweights[named];
10006     };
10007
10008     /**
10009      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10010      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10011      * Shorthand of {@link Roo.Element#get}
10012      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10013      * @return {Element} The Element object
10014      * @member Roo
10015      * @method get
10016      */
10017     Roo.get = El.get;
10018     /**
10019      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10020      * the dom node can be overwritten by other code.
10021      * Shorthand of {@link Roo.Element#fly}
10022      * @param {String/HTMLElement} el The dom node or id
10023      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10024      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10025      * @static
10026      * @return {Element} The shared Element object
10027      * @member Roo
10028      * @method fly
10029      */
10030     Roo.fly = El.fly;
10031
10032     // speedy lookup for elements never to box adjust
10033     var noBoxAdjust = Roo.isStrict ? {
10034         select:1
10035     } : {
10036         input:1, select:1, textarea:1
10037     };
10038     if(Roo.isIE || Roo.isGecko){
10039         noBoxAdjust['button'] = 1;
10040     }
10041
10042
10043     Roo.EventManager.on(window, 'unload', function(){
10044         delete El.cache;
10045         delete El._flyweights;
10046     });
10047 })();
10048
10049
10050
10051
10052 if(Roo.DomQuery){
10053     Roo.Element.selectorFunction = Roo.DomQuery.select;
10054 }
10055
10056 Roo.Element.select = function(selector, unique, root){
10057     var els;
10058     if(typeof selector == "string"){
10059         els = Roo.Element.selectorFunction(selector, root);
10060     }else if(selector.length !== undefined){
10061         els = selector;
10062     }else{
10063         throw "Invalid selector";
10064     }
10065     if(unique === true){
10066         return new Roo.CompositeElement(els);
10067     }else{
10068         return new Roo.CompositeElementLite(els);
10069     }
10070 };
10071 /**
10072  * Selects elements based on the passed CSS selector to enable working on them as 1.
10073  * @param {String/Array} selector The CSS selector or an array of elements
10074  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10075  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10076  * @return {CompositeElementLite/CompositeElement}
10077  * @member Roo
10078  * @method select
10079  */
10080 Roo.select = Roo.Element.select;
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095 /*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105
10106
10107
10108 //Notifies Element that fx methods are available
10109 Roo.enableFx = true;
10110
10111 /**
10112  * @class Roo.Fx
10113  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10114  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10115  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10116  * Element effects to work.</p><br/>
10117  *
10118  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10119  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10120  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10121  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10122  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10123  * expected results and should be done with care.</p><br/>
10124  *
10125  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10126  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10127 <pre>
10128 Value  Description
10129 -----  -----------------------------
10130 tl     The top left corner
10131 t      The center of the top edge
10132 tr     The top right corner
10133 l      The center of the left edge
10134 r      The center of the right edge
10135 bl     The bottom left corner
10136 b      The center of the bottom edge
10137 br     The bottom right corner
10138 </pre>
10139  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10140  * below are common options that can be passed to any Fx method.</b>
10141  * @cfg {Function} callback A function called when the effect is finished
10142  * @cfg {Object} scope The scope of the effect function
10143  * @cfg {String} easing A valid Easing value for the effect
10144  * @cfg {String} afterCls A css class to apply after the effect
10145  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10146  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10147  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10148  * effects that end with the element being visually hidden, ignored otherwise)
10149  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10150  * a function which returns such a specification that will be applied to the Element after the effect finishes
10151  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10152  * @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
10153  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10154  */
10155 Roo.Fx = {
10156         /**
10157          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10158          * origin for the slide effect.  This function automatically handles wrapping the element with
10159          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10160          * Usage:
10161          *<pre><code>
10162 // default: slide the element in from the top
10163 el.slideIn();
10164
10165 // custom: slide the element in from the right with a 2-second duration
10166 el.slideIn('r', { duration: 2 });
10167
10168 // common config options shown with default values
10169 el.slideIn('t', {
10170     easing: 'easeOut',
10171     duration: .5
10172 });
10173 </code></pre>
10174          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10175          * @param {Object} options (optional) Object literal with any of the Fx config options
10176          * @return {Roo.Element} The Element
10177          */
10178     slideIn : function(anchor, o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181
10182         el.queueFx(o, function(){
10183
10184             anchor = anchor || "t";
10185
10186             // fix display to visibility
10187             this.fixDisplay();
10188
10189             // restore values after effect
10190             var r = this.getFxRestore();
10191             var b = this.getBox();
10192             // fixed size for slide
10193             this.setSize(b);
10194
10195             // wrap if needed
10196             var wrap = this.fxWrap(r.pos, o, "hidden");
10197
10198             var st = this.dom.style;
10199             st.visibility = "visible";
10200             st.position = "absolute";
10201
10202             // clear out temp styles after slide and unwrap
10203             var after = function(){
10204                 el.fxUnwrap(wrap, r.pos, o);
10205                 st.width = r.width;
10206                 st.height = r.height;
10207                 el.afterFx(o);
10208             };
10209             // time to calc the positions
10210             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10211
10212             switch(anchor.toLowerCase()){
10213                 case "t":
10214                     wrap.setSize(b.width, 0);
10215                     st.left = st.bottom = "0";
10216                     a = {height: bh};
10217                 break;
10218                 case "l":
10219                     wrap.setSize(0, b.height);
10220                     st.right = st.top = "0";
10221                     a = {width: bw};
10222                 break;
10223                 case "r":
10224                     wrap.setSize(0, b.height);
10225                     wrap.setX(b.right);
10226                     st.left = st.top = "0";
10227                     a = {width: bw, points: pt};
10228                 break;
10229                 case "b":
10230                     wrap.setSize(b.width, 0);
10231                     wrap.setY(b.bottom);
10232                     st.left = st.top = "0";
10233                     a = {height: bh, points: pt};
10234                 break;
10235                 case "tl":
10236                     wrap.setSize(0, 0);
10237                     st.right = st.bottom = "0";
10238                     a = {width: bw, height: bh};
10239                 break;
10240                 case "bl":
10241                     wrap.setSize(0, 0);
10242                     wrap.setY(b.y+b.height);
10243                     st.right = st.top = "0";
10244                     a = {width: bw, height: bh, points: pt};
10245                 break;
10246                 case "br":
10247                     wrap.setSize(0, 0);
10248                     wrap.setXY([b.right, b.bottom]);
10249                     st.left = st.top = "0";
10250                     a = {width: bw, height: bh, points: pt};
10251                 break;
10252                 case "tr":
10253                     wrap.setSize(0, 0);
10254                     wrap.setX(b.x+b.width);
10255                     st.left = st.bottom = "0";
10256                     a = {width: bw, height: bh, points: pt};
10257                 break;
10258             }
10259             this.dom.style.visibility = "visible";
10260             wrap.show();
10261
10262             arguments.callee.anim = wrap.fxanim(a,
10263                 o,
10264                 'motion',
10265                 .5,
10266                 'easeOut', after);
10267         });
10268         return this;
10269     },
10270     
10271         /**
10272          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10273          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10274          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10275          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10276          * wrapping the element with 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 out to the top
10280 el.slideOut();
10281
10282 // custom: slide the element out to the right with a 2-second duration
10283 el.slideOut('r', { duration: 2 });
10284
10285 // common config options shown with default values
10286 el.slideOut('t', {
10287     easing: 'easeOut',
10288     duration: .5,
10289     remove: false,
10290     useDisplay: false
10291 });
10292 </code></pre>
10293          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10294          * @param {Object} options (optional) Object literal with any of the Fx config options
10295          * @return {Roo.Element} The Element
10296          */
10297     slideOut : function(anchor, o){
10298         var el = this.getFxEl();
10299         o = o || {};
10300
10301         el.queueFx(o, function(){
10302
10303             anchor = anchor || "t";
10304
10305             // restore values after effect
10306             var r = this.getFxRestore();
10307             
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, "visible");
10314
10315             var st = this.dom.style;
10316             st.visibility = "visible";
10317             st.position = "absolute";
10318
10319             wrap.setSize(b);
10320
10321             var after = function(){
10322                 if(o.useDisplay){
10323                     el.setDisplayed(false);
10324                 }else{
10325                     el.hide();
10326                 }
10327
10328                 el.fxUnwrap(wrap, r.pos, o);
10329
10330                 st.width = r.width;
10331                 st.height = r.height;
10332
10333                 el.afterFx(o);
10334             };
10335
10336             var a, zero = {to: 0};
10337             switch(anchor.toLowerCase()){
10338                 case "t":
10339                     st.left = st.bottom = "0";
10340                     a = {height: zero};
10341                 break;
10342                 case "l":
10343                     st.right = st.top = "0";
10344                     a = {width: zero};
10345                 break;
10346                 case "r":
10347                     st.left = st.top = "0";
10348                     a = {width: zero, points: {to:[b.right, b.y]}};
10349                 break;
10350                 case "b":
10351                     st.left = st.top = "0";
10352                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10353                 break;
10354                 case "tl":
10355                     st.right = st.bottom = "0";
10356                     a = {width: zero, height: zero};
10357                 break;
10358                 case "bl":
10359                     st.right = st.top = "0";
10360                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10361                 break;
10362                 case "br":
10363                     st.left = st.top = "0";
10364                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10365                 break;
10366                 case "tr":
10367                     st.left = st.bottom = "0";
10368                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10369                 break;
10370             }
10371
10372             arguments.callee.anim = wrap.fxanim(a,
10373                 o,
10374                 'motion',
10375                 .5,
10376                 "easeOut", after);
10377         });
10378         return this;
10379     },
10380
10381         /**
10382          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10383          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10384          * The element must be removed from the DOM using the 'remove' config option if desired.
10385          * Usage:
10386          *<pre><code>
10387 // default
10388 el.puff();
10389
10390 // common config options shown with default values
10391 el.puff({
10392     easing: 'easeOut',
10393     duration: .5,
10394     remove: false,
10395     useDisplay: false
10396 });
10397 </code></pre>
10398          * @param {Object} options (optional) Object literal with any of the Fx config options
10399          * @return {Roo.Element} The Element
10400          */
10401     puff : function(o){
10402         var el = this.getFxEl();
10403         o = o || {};
10404
10405         el.queueFx(o, function(){
10406             this.clearOpacity();
10407             this.show();
10408
10409             // restore values after effect
10410             var r = this.getFxRestore();
10411             var st = this.dom.style;
10412
10413             var after = function(){
10414                 if(o.useDisplay){
10415                     el.setDisplayed(false);
10416                 }else{
10417                     el.hide();
10418                 }
10419
10420                 el.clearOpacity();
10421
10422                 el.setPositioning(r.pos);
10423                 st.width = r.width;
10424                 st.height = r.height;
10425                 st.fontSize = '';
10426                 el.afterFx(o);
10427             };
10428
10429             var width = this.getWidth();
10430             var height = this.getHeight();
10431
10432             arguments.callee.anim = this.fxanim({
10433                     width : {to: this.adjustWidth(width * 2)},
10434                     height : {to: this.adjustHeight(height * 2)},
10435                     points : {by: [-(width * .5), -(height * .5)]},
10436                     opacity : {to: 0},
10437                     fontSize: {to:200, unit: "%"}
10438                 },
10439                 o,
10440                 'motion',
10441                 .5,
10442                 "easeOut", after);
10443         });
10444         return this;
10445     },
10446
10447         /**
10448          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10449          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10450          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10451          * Usage:
10452          *<pre><code>
10453 // default
10454 el.switchOff();
10455
10456 // all config options shown with default values
10457 el.switchOff({
10458     easing: 'easeIn',
10459     duration: .3,
10460     remove: false,
10461     useDisplay: false
10462 });
10463 </code></pre>
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     switchOff : function(o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             this.clearOpacity();
10473             this.clip();
10474
10475             // restore values after effect
10476             var r = this.getFxRestore();
10477             var st = this.dom.style;
10478
10479             var after = function(){
10480                 if(o.useDisplay){
10481                     el.setDisplayed(false);
10482                 }else{
10483                     el.hide();
10484                 }
10485
10486                 el.clearOpacity();
10487                 el.setPositioning(r.pos);
10488                 st.width = r.width;
10489                 st.height = r.height;
10490
10491                 el.afterFx(o);
10492             };
10493
10494             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10495                 this.clearOpacity();
10496                 (function(){
10497                     this.fxanim({
10498                         height:{to:1},
10499                         points:{by:[0, this.getHeight() * .5]}
10500                     }, o, 'motion', 0.3, 'easeIn', after);
10501                 }).defer(100, this);
10502             });
10503         });
10504         return this;
10505     },
10506
10507     /**
10508      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10509      * changed using the "attr" config option) and then fading back to the original color. If no original
10510      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10511      * Usage:
10512 <pre><code>
10513 // default: highlight background to yellow
10514 el.highlight();
10515
10516 // custom: highlight foreground text to blue for 2 seconds
10517 el.highlight("0000ff", { attr: 'color', duration: 2 });
10518
10519 // common config options shown with default values
10520 el.highlight("ffff9c", {
10521     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10522     endColor: (current color) or "ffffff",
10523     easing: 'easeIn',
10524     duration: 1
10525 });
10526 </code></pre>
10527      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10528      * @param {Object} options (optional) Object literal with any of the Fx config options
10529      * @return {Roo.Element} The Element
10530      */ 
10531     highlight : function(color, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             color = color || "ffff9c";
10537             attr = o.attr || "backgroundColor";
10538
10539             this.clearOpacity();
10540             this.show();
10541
10542             var origColor = this.getColor(attr);
10543             var restoreColor = this.dom.style[attr];
10544             endColor = (o.endColor || origColor) || "ffffff";
10545
10546             var after = function(){
10547                 el.dom.style[attr] = restoreColor;
10548                 el.afterFx(o);
10549             };
10550
10551             var a = {};
10552             a[attr] = {from: color, to: endColor};
10553             arguments.callee.anim = this.fxanim(a,
10554                 o,
10555                 'color',
10556                 1,
10557                 'easeIn', after);
10558         });
10559         return this;
10560     },
10561
10562    /**
10563     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10564     * Usage:
10565 <pre><code>
10566 // default: a single light blue ripple
10567 el.frame();
10568
10569 // custom: 3 red ripples lasting 3 seconds total
10570 el.frame("ff0000", 3, { duration: 3 });
10571
10572 // common config options shown with default values
10573 el.frame("C3DAF9", 1, {
10574     duration: 1 //duration of entire animation (not each individual ripple)
10575     // Note: Easing is not configurable and will be ignored if included
10576 });
10577 </code></pre>
10578     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10579     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     frame : function(color, count, o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586
10587         el.queueFx(o, function(){
10588             color = color || "#C3DAF9";
10589             if(color.length == 6){
10590                 color = "#" + color;
10591             }
10592             count = count || 1;
10593             duration = o.duration || 1;
10594             this.show();
10595
10596             var b = this.getBox();
10597             var animFn = function(){
10598                 var proxy = this.createProxy({
10599
10600                      style:{
10601                         visbility:"hidden",
10602                         position:"absolute",
10603                         "z-index":"35000", // yee haw
10604                         border:"0px solid " + color
10605                      }
10606                   });
10607                 var scale = Roo.isBorderBox ? 2 : 1;
10608                 proxy.animate({
10609                     top:{from:b.y, to:b.y - 20},
10610                     left:{from:b.x, to:b.x - 20},
10611                     borderWidth:{from:0, to:10},
10612                     opacity:{from:1, to:0},
10613                     height:{from:b.height, to:(b.height + (20*scale))},
10614                     width:{from:b.width, to:(b.width + (20*scale))}
10615                 }, duration, function(){
10616                     proxy.remove();
10617                 });
10618                 if(--count > 0){
10619                      animFn.defer((duration/2)*1000, this);
10620                 }else{
10621                     el.afterFx(o);
10622                 }
10623             };
10624             animFn.call(this);
10625         });
10626         return this;
10627     },
10628
10629    /**
10630     * Creates a pause before any subsequent queued effects begin.  If there are
10631     * no effects queued after the pause it will have no effect.
10632     * Usage:
10633 <pre><code>
10634 el.pause(1);
10635 </code></pre>
10636     * @param {Number} seconds The length of time to pause (in seconds)
10637     * @return {Roo.Element} The Element
10638     */
10639     pause : function(seconds){
10640         var el = this.getFxEl();
10641         var o = {};
10642
10643         el.queueFx(o, function(){
10644             setTimeout(function(){
10645                 el.afterFx(o);
10646             }, seconds * 1000);
10647         });
10648         return this;
10649     },
10650
10651    /**
10652     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10653     * using the "endOpacity" config option.
10654     * Usage:
10655 <pre><code>
10656 // default: fade in from opacity 0 to 100%
10657 el.fadeIn();
10658
10659 // custom: fade in from opacity 0 to 75% over 2 seconds
10660 el.fadeIn({ endOpacity: .75, duration: 2});
10661
10662 // common config options shown with default values
10663 el.fadeIn({
10664     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10665     easing: 'easeOut',
10666     duration: .5
10667 });
10668 </code></pre>
10669     * @param {Object} options (optional) Object literal with any of the Fx config options
10670     * @return {Roo.Element} The Element
10671     */
10672     fadeIn : function(o){
10673         var el = this.getFxEl();
10674         o = o || {};
10675         el.queueFx(o, function(){
10676             this.setOpacity(0);
10677             this.fixDisplay();
10678             this.dom.style.visibility = 'visible';
10679             var to = o.endOpacity || 1;
10680             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10681                 o, null, .5, "easeOut", function(){
10682                 if(to == 1){
10683                     this.clearOpacity();
10684                 }
10685                 el.afterFx(o);
10686             });
10687         });
10688         return this;
10689     },
10690
10691    /**
10692     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10693     * using the "endOpacity" config option.
10694     * Usage:
10695 <pre><code>
10696 // default: fade out from the element's current opacity to 0
10697 el.fadeOut();
10698
10699 // custom: fade out from the element's current opacity to 25% over 2 seconds
10700 el.fadeOut({ endOpacity: .25, duration: 2});
10701
10702 // common config options shown with default values
10703 el.fadeOut({
10704     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10705     easing: 'easeOut',
10706     duration: .5
10707     remove: false,
10708     useDisplay: false
10709 });
10710 </code></pre>
10711     * @param {Object} options (optional) Object literal with any of the Fx config options
10712     * @return {Roo.Element} The Element
10713     */
10714     fadeOut : function(o){
10715         var el = this.getFxEl();
10716         o = o || {};
10717         el.queueFx(o, function(){
10718             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10719                 o, null, .5, "easeOut", function(){
10720                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10721                      this.dom.style.display = "none";
10722                 }else{
10723                      this.dom.style.visibility = "hidden";
10724                 }
10725                 this.clearOpacity();
10726                 el.afterFx(o);
10727             });
10728         });
10729         return this;
10730     },
10731
10732    /**
10733     * Animates the transition of an element's dimensions from a starting height/width
10734     * to an ending height/width.
10735     * Usage:
10736 <pre><code>
10737 // change height and width to 100x100 pixels
10738 el.scale(100, 100);
10739
10740 // common config options shown with default values.  The height and width will default to
10741 // the element's existing values if passed as null.
10742 el.scale(
10743     [element's width],
10744     [element's height], {
10745     easing: 'easeOut',
10746     duration: .35
10747 });
10748 </code></pre>
10749     * @param {Number} width  The new width (pass undefined to keep the original width)
10750     * @param {Number} height  The new height (pass undefined to keep the original height)
10751     * @param {Object} options (optional) Object literal with any of the Fx config options
10752     * @return {Roo.Element} The Element
10753     */
10754     scale : function(w, h, o){
10755         this.shift(Roo.apply({}, o, {
10756             width: w,
10757             height: h
10758         }));
10759         return this;
10760     },
10761
10762    /**
10763     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10764     * Any of these properties not specified in the config object will not be changed.  This effect 
10765     * requires that at least one new dimension, position or opacity setting must be passed in on
10766     * the config object in order for the function to have any effect.
10767     * Usage:
10768 <pre><code>
10769 // slide the element horizontally to x position 200 while changing the height and opacity
10770 el.shift({ x: 200, height: 50, opacity: .8 });
10771
10772 // common config options shown with default values.
10773 el.shift({
10774     width: [element's width],
10775     height: [element's height],
10776     x: [element's x position],
10777     y: [element's y position],
10778     opacity: [element's opacity],
10779     easing: 'easeOut',
10780     duration: .35
10781 });
10782 </code></pre>
10783     * @param {Object} options  Object literal with any of the Fx config options
10784     * @return {Roo.Element} The Element
10785     */
10786     shift : function(o){
10787         var el = this.getFxEl();
10788         o = o || {};
10789         el.queueFx(o, function(){
10790             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10791             if(w !== undefined){
10792                 a.width = {to: this.adjustWidth(w)};
10793             }
10794             if(h !== undefined){
10795                 a.height = {to: this.adjustHeight(h)};
10796             }
10797             if(x !== undefined || y !== undefined){
10798                 a.points = {to: [
10799                     x !== undefined ? x : this.getX(),
10800                     y !== undefined ? y : this.getY()
10801                 ]};
10802             }
10803             if(op !== undefined){
10804                 a.opacity = {to: op};
10805             }
10806             if(o.xy !== undefined){
10807                 a.points = {to: o.xy};
10808             }
10809             arguments.callee.anim = this.fxanim(a,
10810                 o, 'motion', .35, "easeOut", function(){
10811                 el.afterFx(o);
10812             });
10813         });
10814         return this;
10815     },
10816
10817         /**
10818          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10819          * ending point of the effect.
10820          * Usage:
10821          *<pre><code>
10822 // default: slide the element downward while fading out
10823 el.ghost();
10824
10825 // custom: slide the element out to the right with a 2-second duration
10826 el.ghost('r', { duration: 2 });
10827
10828 // common config options shown with default values
10829 el.ghost('b', {
10830     easing: 'easeOut',
10831     duration: .5
10832     remove: false,
10833     useDisplay: false
10834 });
10835 </code></pre>
10836          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10837          * @param {Object} options (optional) Object literal with any of the Fx config options
10838          * @return {Roo.Element} The Element
10839          */
10840     ghost : function(anchor, o){
10841         var el = this.getFxEl();
10842         o = o || {};
10843
10844         el.queueFx(o, function(){
10845             anchor = anchor || "b";
10846
10847             // restore values after effect
10848             var r = this.getFxRestore();
10849             var w = this.getWidth(),
10850                 h = this.getHeight();
10851
10852             var st = this.dom.style;
10853
10854             var after = function(){
10855                 if(o.useDisplay){
10856                     el.setDisplayed(false);
10857                 }else{
10858                     el.hide();
10859                 }
10860
10861                 el.clearOpacity();
10862                 el.setPositioning(r.pos);
10863                 st.width = r.width;
10864                 st.height = r.height;
10865
10866                 el.afterFx(o);
10867             };
10868
10869             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10870             switch(anchor.toLowerCase()){
10871                 case "t":
10872                     pt.by = [0, -h];
10873                 break;
10874                 case "l":
10875                     pt.by = [-w, 0];
10876                 break;
10877                 case "r":
10878                     pt.by = [w, 0];
10879                 break;
10880                 case "b":
10881                     pt.by = [0, h];
10882                 break;
10883                 case "tl":
10884                     pt.by = [-w, -h];
10885                 break;
10886                 case "bl":
10887                     pt.by = [-w, h];
10888                 break;
10889                 case "br":
10890                     pt.by = [w, h];
10891                 break;
10892                 case "tr":
10893                     pt.by = [w, -h];
10894                 break;
10895             }
10896
10897             arguments.callee.anim = this.fxanim(a,
10898                 o,
10899                 'motion',
10900                 .5,
10901                 "easeOut", after);
10902         });
10903         return this;
10904     },
10905
10906         /**
10907          * Ensures that all effects queued after syncFx is called on the element are
10908          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10909          * @return {Roo.Element} The Element
10910          */
10911     syncFx : function(){
10912         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10913             block : false,
10914             concurrent : true,
10915             stopFx : false
10916         });
10917         return this;
10918     },
10919
10920         /**
10921          * Ensures that all effects queued after sequenceFx is called on the element are
10922          * run in sequence.  This is the opposite of {@link #syncFx}.
10923          * @return {Roo.Element} The Element
10924          */
10925     sequenceFx : function(){
10926         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10927             block : false,
10928             concurrent : false,
10929             stopFx : false
10930         });
10931         return this;
10932     },
10933
10934         /* @private */
10935     nextFx : function(){
10936         var ef = this.fxQueue[0];
10937         if(ef){
10938             ef.call(this);
10939         }
10940     },
10941
10942         /**
10943          * Returns true if the element has any effects actively running or queued, else returns false.
10944          * @return {Boolean} True if element has active effects, else false
10945          */
10946     hasActiveFx : function(){
10947         return this.fxQueue && this.fxQueue[0];
10948     },
10949
10950         /**
10951          * Stops any running effects and clears the element's internal effects queue if it contains
10952          * any additional effects that haven't started yet.
10953          * @return {Roo.Element} The Element
10954          */
10955     stopFx : function(){
10956         if(this.hasActiveFx()){
10957             var cur = this.fxQueue[0];
10958             if(cur && cur.anim && cur.anim.isAnimated()){
10959                 this.fxQueue = [cur]; // clear out others
10960                 cur.anim.stop(true);
10961             }
10962         }
10963         return this;
10964     },
10965
10966         /* @private */
10967     beforeFx : function(o){
10968         if(this.hasActiveFx() && !o.concurrent){
10969            if(o.stopFx){
10970                this.stopFx();
10971                return true;
10972            }
10973            return false;
10974         }
10975         return true;
10976     },
10977
10978         /**
10979          * Returns true if the element is currently blocking so that no other effect can be queued
10980          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10981          * used to ensure that an effect initiated by a user action runs to completion prior to the
10982          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10983          * @return {Boolean} True if blocking, else false
10984          */
10985     hasFxBlock : function(){
10986         var q = this.fxQueue;
10987         return q && q[0] && q[0].block;
10988     },
10989
10990         /* @private */
10991     queueFx : function(o, fn){
10992         if(!this.fxQueue){
10993             this.fxQueue = [];
10994         }
10995         if(!this.hasFxBlock()){
10996             Roo.applyIf(o, this.fxDefaults);
10997             if(!o.concurrent){
10998                 var run = this.beforeFx(o);
10999                 fn.block = o.block;
11000                 this.fxQueue.push(fn);
11001                 if(run){
11002                     this.nextFx();
11003                 }
11004             }else{
11005                 fn.call(this);
11006             }
11007         }
11008         return this;
11009     },
11010
11011         /* @private */
11012     fxWrap : function(pos, o, vis){
11013         var wrap;
11014         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11015             var wrapXY;
11016             if(o.fixPosition){
11017                 wrapXY = this.getXY();
11018             }
11019             var div = document.createElement("div");
11020             div.style.visibility = vis;
11021             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11022             wrap.setPositioning(pos);
11023             if(wrap.getStyle("position") == "static"){
11024                 wrap.position("relative");
11025             }
11026             this.clearPositioning('auto');
11027             wrap.clip();
11028             wrap.dom.appendChild(this.dom);
11029             if(wrapXY){
11030                 wrap.setXY(wrapXY);
11031             }
11032         }
11033         return wrap;
11034     },
11035
11036         /* @private */
11037     fxUnwrap : function(wrap, pos, o){
11038         this.clearPositioning();
11039         this.setPositioning(pos);
11040         if(!o.wrap){
11041             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11042             wrap.remove();
11043         }
11044     },
11045
11046         /* @private */
11047     getFxRestore : function(){
11048         var st = this.dom.style;
11049         return {pos: this.getPositioning(), width: st.width, height : st.height};
11050     },
11051
11052         /* @private */
11053     afterFx : function(o){
11054         if(o.afterStyle){
11055             this.applyStyles(o.afterStyle);
11056         }
11057         if(o.afterCls){
11058             this.addClass(o.afterCls);
11059         }
11060         if(o.remove === true){
11061             this.remove();
11062         }
11063         Roo.callback(o.callback, o.scope, [this]);
11064         if(!o.concurrent){
11065             this.fxQueue.shift();
11066             this.nextFx();
11067         }
11068     },
11069
11070         /* @private */
11071     getFxEl : function(){ // support for composite element fx
11072         return Roo.get(this.dom);
11073     },
11074
11075         /* @private */
11076     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11077         animType = animType || 'run';
11078         opt = opt || {};
11079         var anim = Roo.lib.Anim[animType](
11080             this.dom, args,
11081             (opt.duration || defaultDur) || .35,
11082             (opt.easing || defaultEase) || 'easeOut',
11083             function(){
11084                 Roo.callback(cb, this);
11085             },
11086             this
11087         );
11088         opt.anim = anim;
11089         return anim;
11090     }
11091 };
11092
11093 // backwords compat
11094 Roo.Fx.resize = Roo.Fx.scale;
11095
11096 //When included, Roo.Fx is automatically applied to Element so that all basic
11097 //effects are available directly via the Element API
11098 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11099  * Based on:
11100  * Ext JS Library 1.1.1
11101  * Copyright(c) 2006-2007, Ext JS, LLC.
11102  *
11103  * Originally Released Under LGPL - original licence link has changed is not relivant.
11104  *
11105  * Fork - LGPL
11106  * <script type="text/javascript">
11107  */
11108
11109
11110 /**
11111  * @class Roo.CompositeElement
11112  * Standard composite class. Creates a Roo.Element for every element in the collection.
11113  * <br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  * <br><br>
11117  * All methods return <i>this</i> and can be chained.
11118  <pre><code>
11119  var els = Roo.select("#some-el div.some-class", true);
11120  // or select directly from an existing element
11121  var el = Roo.get('some-el');
11122  el.select('div.some-class', true);
11123
11124  els.setWidth(100); // all elements become 100 width
11125  els.hide(true); // all elements fade out and hide
11126  // or
11127  els.setWidth(100).hide(true);
11128  </code></pre>
11129  */
11130 Roo.CompositeElement = function(els){
11131     this.elements = [];
11132     this.addElements(els);
11133 };
11134 Roo.CompositeElement.prototype = {
11135     isComposite: true,
11136     addElements : function(els){
11137         if(!els) {
11138             return this;
11139         }
11140         if(typeof els == "string"){
11141             els = Roo.Element.selectorFunction(els);
11142         }
11143         var yels = this.elements;
11144         var index = yels.length-1;
11145         for(var i = 0, len = els.length; i < len; i++) {
11146                 yels[++index] = Roo.get(els[i]);
11147         }
11148         return this;
11149     },
11150
11151     /**
11152     * Clears this composite and adds the elements returned by the passed selector.
11153     * @param {String/Array} els A string CSS selector, an array of elements or an element
11154     * @return {CompositeElement} this
11155     */
11156     fill : function(els){
11157         this.elements = [];
11158         this.add(els);
11159         return this;
11160     },
11161
11162     /**
11163     * Filters this composite to only elements that match the passed selector.
11164     * @param {String} selector A string CSS selector
11165     * @param {Boolean} inverse return inverse filter (not matches)
11166     * @return {CompositeElement} this
11167     */
11168     filter : function(selector, inverse){
11169         var els = [];
11170         inverse = inverse || false;
11171         this.each(function(el){
11172             var match = inverse ? !el.is(selector) : el.is(selector);
11173             if(match){
11174                 els[els.length] = el.dom;
11175             }
11176         });
11177         this.fill(els);
11178         return this;
11179     },
11180
11181     invoke : function(fn, args){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184                 Roo.Element.prototype[fn].apply(els[i], args);
11185         }
11186         return this;
11187     },
11188     /**
11189     * Adds elements to this composite.
11190     * @param {String/Array} els A string CSS selector, an array of elements or an element
11191     * @return {CompositeElement} this
11192     */
11193     add : function(els){
11194         if(typeof els == "string"){
11195             this.addElements(Roo.Element.selectorFunction(els));
11196         }else if(els.length !== undefined){
11197             this.addElements(els);
11198         }else{
11199             this.addElements([els]);
11200         }
11201         return this;
11202     },
11203     /**
11204     * Calls the passed function passing (el, this, index) for each element in this composite.
11205     * @param {Function} fn The function to call
11206     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11207     * @return {CompositeElement} this
11208     */
11209     each : function(fn, scope){
11210         var els = this.elements;
11211         for(var i = 0, len = els.length; i < len; i++){
11212             if(fn.call(scope || els[i], els[i], this, i) === false) {
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     /**
11220      * Returns the Element object at the specified index
11221      * @param {Number} index
11222      * @return {Roo.Element}
11223      */
11224     item : function(index){
11225         return this.elements[index] || null;
11226     },
11227
11228     /**
11229      * Returns the first Element
11230      * @return {Roo.Element}
11231      */
11232     first : function(){
11233         return this.item(0);
11234     },
11235
11236     /**
11237      * Returns the last Element
11238      * @return {Roo.Element}
11239      */
11240     last : function(){
11241         return this.item(this.elements.length-1);
11242     },
11243
11244     /**
11245      * Returns the number of elements in this composite
11246      * @return Number
11247      */
11248     getCount : function(){
11249         return this.elements.length;
11250     },
11251
11252     /**
11253      * Returns true if this composite contains the passed element
11254      * @return Boolean
11255      */
11256     contains : function(el){
11257         return this.indexOf(el) !== -1;
11258     },
11259
11260     /**
11261      * Returns true if this composite contains the passed element
11262      * @return Boolean
11263      */
11264     indexOf : function(el){
11265         return this.elements.indexOf(Roo.get(el));
11266     },
11267
11268
11269     /**
11270     * Removes the specified element(s).
11271     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11272     * or an array of any of those.
11273     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11274     * @return {CompositeElement} this
11275     */
11276     removeElement : function(el, removeDom){
11277         if(el instanceof Array){
11278             for(var i = 0, len = el.length; i < len; i++){
11279                 this.removeElement(el[i]);
11280             }
11281             return this;
11282         }
11283         var index = typeof el == 'number' ? el : this.indexOf(el);
11284         if(index !== -1){
11285             if(removeDom){
11286                 var d = this.elements[index];
11287                 if(d.dom){
11288                     d.remove();
11289                 }else{
11290                     d.parentNode.removeChild(d);
11291                 }
11292             }
11293             this.elements.splice(index, 1);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Replaces the specified element with the passed element.
11300     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11301     * to replace.
11302     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11303     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11304     * @return {CompositeElement} this
11305     */
11306     replaceElement : function(el, replacement, domReplace){
11307         var index = typeof el == 'number' ? el : this.indexOf(el);
11308         if(index !== -1){
11309             if(domReplace){
11310                 this.elements[index].replaceWith(replacement);
11311             }else{
11312                 this.elements.splice(index, 1, Roo.get(replacement))
11313             }
11314         }
11315         return this;
11316     },
11317
11318     /**
11319      * Removes all elements.
11320      */
11321     clear : function(){
11322         this.elements = [];
11323     }
11324 };
11325 (function(){
11326     Roo.CompositeElement.createCall = function(proto, fnName){
11327         if(!proto[fnName]){
11328             proto[fnName] = function(){
11329                 return this.invoke(fnName, arguments);
11330             };
11331         }
11332     };
11333     for(var fnName in Roo.Element.prototype){
11334         if(typeof Roo.Element.prototype[fnName] == "function"){
11335             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11336         }
11337     };
11338 })();
11339 /*
11340  * Based on:
11341  * Ext JS Library 1.1.1
11342  * Copyright(c) 2006-2007, Ext JS, LLC.
11343  *
11344  * Originally Released Under LGPL - original licence link has changed is not relivant.
11345  *
11346  * Fork - LGPL
11347  * <script type="text/javascript">
11348  */
11349
11350 /**
11351  * @class Roo.CompositeElementLite
11352  * @extends Roo.CompositeElement
11353  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11354  <pre><code>
11355  var els = Roo.select("#some-el div.some-class");
11356  // or select directly from an existing element
11357  var el = Roo.get('some-el');
11358  el.select('div.some-class');
11359
11360  els.setWidth(100); // all elements become 100 width
11361  els.hide(true); // all elements fade out and hide
11362  // or
11363  els.setWidth(100).hide(true);
11364  </code></pre><br><br>
11365  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11366  * actions will be performed on all the elements in this collection.</b>
11367  */
11368 Roo.CompositeElementLite = function(els){
11369     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11370     this.el = new Roo.Element.Flyweight();
11371 };
11372 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11373     addElements : function(els){
11374         if(els){
11375             if(els instanceof Array){
11376                 this.elements = this.elements.concat(els);
11377             }else{
11378                 var yels = this.elements;
11379                 var index = yels.length-1;
11380                 for(var i = 0, len = els.length; i < len; i++) {
11381                     yels[++index] = els[i];
11382                 }
11383             }
11384         }
11385         return this;
11386     },
11387     invoke : function(fn, args){
11388         var els = this.elements;
11389         var el = this.el;
11390         for(var i = 0, len = els.length; i < len; i++) {
11391             el.dom = els[i];
11392                 Roo.Element.prototype[fn].apply(el, args);
11393         }
11394         return this;
11395     },
11396     /**
11397      * Returns a flyweight Element of the dom element object at the specified index
11398      * @param {Number} index
11399      * @return {Roo.Element}
11400      */
11401     item : function(index){
11402         if(!this.elements[index]){
11403             return null;
11404         }
11405         this.el.dom = this.elements[index];
11406         return this.el;
11407     },
11408
11409     // fixes scope with flyweight
11410     addListener : function(eventName, handler, scope, opt){
11411         var els = this.elements;
11412         for(var i = 0, len = els.length; i < len; i++) {
11413             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11414         }
11415         return this;
11416     },
11417
11418     /**
11419     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11420     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11421     * a reference to the dom node, use el.dom.</b>
11422     * @param {Function} fn The function to call
11423     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11424     * @return {CompositeElement} this
11425     */
11426     each : function(fn, scope){
11427         var els = this.elements;
11428         var el = this.el;
11429         for(var i = 0, len = els.length; i < len; i++){
11430             el.dom = els[i];
11431                 if(fn.call(scope || el, el, this, i) === false){
11432                 break;
11433             }
11434         }
11435         return this;
11436     },
11437
11438     indexOf : function(el){
11439         return this.elements.indexOf(Roo.getDom(el));
11440     },
11441
11442     replaceElement : function(el, replacement, domReplace){
11443         var index = typeof el == 'number' ? el : this.indexOf(el);
11444         if(index !== -1){
11445             replacement = Roo.getDom(replacement);
11446             if(domReplace){
11447                 var d = this.elements[index];
11448                 d.parentNode.insertBefore(replacement, d);
11449                 d.parentNode.removeChild(d);
11450             }
11451             this.elements.splice(index, 1, replacement);
11452         }
11453         return this;
11454     }
11455 });
11456 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11457
11458 /*
11459  * Based on:
11460  * Ext JS Library 1.1.1
11461  * Copyright(c) 2006-2007, Ext JS, LLC.
11462  *
11463  * Originally Released Under LGPL - original licence link has changed is not relivant.
11464  *
11465  * Fork - LGPL
11466  * <script type="text/javascript">
11467  */
11468
11469  
11470
11471 /**
11472  * @class Roo.data.Connection
11473  * @extends Roo.util.Observable
11474  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11475  * either to a configured URL, or to a URL specified at request time. 
11476  * 
11477  * Requests made by this class are asynchronous, and will return immediately. No data from
11478  * the server will be available to the statement immediately following the {@link #request} call.
11479  * To process returned data, use a callback in the request options object, or an event listener.
11480  * 
11481  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11482  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11483  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11484  * property and, if present, the IFRAME's XML document as the responseXML property.
11485  * 
11486  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11487  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11488  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11489  * standard DOM methods.
11490  * @constructor
11491  * @param {Object} config a configuration object.
11492  */
11493 Roo.data.Connection = function(config){
11494     Roo.apply(this, config);
11495     this.addEvents({
11496         /**
11497          * @event beforerequest
11498          * Fires before a network request is made to retrieve a data object.
11499          * @param {Connection} conn This Connection object.
11500          * @param {Object} options The options config object passed to the {@link #request} method.
11501          */
11502         "beforerequest" : true,
11503         /**
11504          * @event requestcomplete
11505          * Fires if the request was successfully completed.
11506          * @param {Connection} conn This Connection object.
11507          * @param {Object} response The XHR object containing the response data.
11508          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11509          * @param {Object} options The options config object passed to the {@link #request} method.
11510          */
11511         "requestcomplete" : true,
11512         /**
11513          * @event requestexception
11514          * Fires if an error HTTP status was returned from the server.
11515          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11516          * @param {Connection} conn This Connection object.
11517          * @param {Object} response The XHR object containing the response data.
11518          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11519          * @param {Object} options The options config object passed to the {@link #request} method.
11520          */
11521         "requestexception" : true
11522     });
11523     Roo.data.Connection.superclass.constructor.call(this);
11524 };
11525
11526 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11527     /**
11528      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11529      */
11530     /**
11531      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      */
11534     /**
11535      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11536      *  to each request made by this object. (defaults to undefined)
11537      */
11538     /**
11539      * @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)
11540      */
11541     /**
11542      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11543      */
11544     timeout : 30000,
11545     /**
11546      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11547      * @type Boolean
11548      */
11549     autoAbort:false,
11550
11551     /**
11552      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11553      * @type Boolean
11554      */
11555     disableCaching: true,
11556
11557     /**
11558      * Sends an HTTP request to a remote server.
11559      * @param {Object} options An object which may contain the following properties:<ul>
11560      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11561      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11562      * request, a url encoded string or a function to call to get either.</li>
11563      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11564      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11565      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11566      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11567      * <li>options {Object} The parameter to the request call.</li>
11568      * <li>success {Boolean} True if the request succeeded.</li>
11569      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11570      * </ul></li>
11571      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11572      * The callback is passed the following parameters:<ul>
11573      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11574      * <li>options {Object} The parameter to the request call.</li>
11575      * </ul></li>
11576      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11577      * The callback is passed the following parameters:<ul>
11578      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11579      * <li>options {Object} The parameter to the request call.</li>
11580      * </ul></li>
11581      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11582      * for the callback function. Defaults to the browser window.</li>
11583      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11584      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11585      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11586      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11587      * params for the post data. Any params will be appended to the URL.</li>
11588      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11589      * </ul>
11590      * @return {Number} transactionId
11591      */
11592     request : function(o){
11593         if(this.fireEvent("beforerequest", this, o) !== false){
11594             var p = o.params;
11595
11596             if(typeof p == "function"){
11597                 p = p.call(o.scope||window, o);
11598             }
11599             if(typeof p == "object"){
11600                 p = Roo.urlEncode(o.params);
11601             }
11602             if(this.extraParams){
11603                 var extras = Roo.urlEncode(this.extraParams);
11604                 p = p ? (p + '&' + extras) : extras;
11605             }
11606
11607             var url = o.url || this.url;
11608             if(typeof url == 'function'){
11609                 url = url.call(o.scope||window, o);
11610             }
11611
11612             if(o.form){
11613                 var form = Roo.getDom(o.form);
11614                 url = url || form.action;
11615
11616                 var enctype = form.getAttribute("enctype");
11617                 
11618                 if (o.formData) {
11619                     return this.doFormDataUpload(o,p,url);
11620                 }
11621                 
11622                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11623                     return this.doFormUpload(o, p, url);
11624                 }
11625                 var f = Roo.lib.Ajax.serializeForm(form);
11626                 p = p ? (p + '&' + f) : f;
11627             }
11628
11629             var hs = o.headers;
11630             if(this.defaultHeaders){
11631                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11632                 if(!o.headers){
11633                     o.headers = hs;
11634                 }
11635             }
11636
11637             var cb = {
11638                 success: this.handleResponse,
11639                 failure: this.handleFailure,
11640                 scope: this,
11641                 argument: {options: o},
11642                 timeout : o.timeout || this.timeout
11643             };
11644
11645             var method = o.method||this.method||(p ? "POST" : "GET");
11646
11647             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11648                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11649             }
11650
11651             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11652                 if(o.autoAbort){
11653                     this.abort();
11654                 }
11655             }else if(this.autoAbort !== false){
11656                 this.abort();
11657             }
11658
11659             if((method == 'GET' && p) || o.xmlData){
11660                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11661                 p = '';
11662             }
11663             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11664             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11665             Roo.lib.Ajax.useDefaultHeader == true;
11666             return this.transId;
11667         }else{
11668             Roo.callback(o.callback, o.scope, [o, null, null]);
11669             return null;
11670         }
11671     },
11672
11673     /**
11674      * Determine whether this object has a request outstanding.
11675      * @param {Number} transactionId (Optional) defaults to the last transaction
11676      * @return {Boolean} True if there is an outstanding request.
11677      */
11678     isLoading : function(transId){
11679         if(transId){
11680             return Roo.lib.Ajax.isCallInProgress(transId);
11681         }else{
11682             return this.transId ? true : false;
11683         }
11684     },
11685
11686     /**
11687      * Aborts any outstanding request.
11688      * @param {Number} transactionId (Optional) defaults to the last transaction
11689      */
11690     abort : function(transId){
11691         if(transId || this.isLoading()){
11692             Roo.lib.Ajax.abort(transId || this.transId);
11693         }
11694     },
11695
11696     // private
11697     handleResponse : function(response){
11698         this.transId = false;
11699         var options = response.argument.options;
11700         response.argument = options ? options.argument : null;
11701         this.fireEvent("requestcomplete", this, response, options);
11702         Roo.callback(options.success, options.scope, [response, options]);
11703         Roo.callback(options.callback, options.scope, [options, true, response]);
11704     },
11705
11706     // private
11707     handleFailure : function(response, e){
11708         this.transId = false;
11709         var options = response.argument.options;
11710         response.argument = options ? options.argument : null;
11711         this.fireEvent("requestexception", this, response, options, e);
11712         Roo.callback(options.failure, options.scope, [response, options]);
11713         Roo.callback(options.callback, options.scope, [options, false, response]);
11714     },
11715
11716     // private
11717     doFormUpload : function(o, ps, url){
11718         var id = Roo.id();
11719         var frame = document.createElement('iframe');
11720         frame.id = id;
11721         frame.name = id;
11722         frame.className = 'x-hidden';
11723         if(Roo.isIE){
11724             frame.src = Roo.SSL_SECURE_URL;
11725         }
11726         document.body.appendChild(frame);
11727
11728         if(Roo.isIE){
11729            document.frames[id].name = id;
11730         }
11731
11732         var form = Roo.getDom(o.form);
11733         form.target = id;
11734         form.method = 'POST';
11735         form.enctype = form.encoding = 'multipart/form-data';
11736         if(url){
11737             form.action = url;
11738         }
11739
11740         var hiddens, hd;
11741         if(ps){ // add dynamic params
11742             hiddens = [];
11743             ps = Roo.urlDecode(ps, false);
11744             for(var k in ps){
11745                 if(ps.hasOwnProperty(k)){
11746                     hd = document.createElement('input');
11747                     hd.type = 'hidden';
11748                     hd.name = k;
11749                     hd.value = ps[k];
11750                     form.appendChild(hd);
11751                     hiddens.push(hd);
11752                 }
11753             }
11754         }
11755
11756         function cb(){
11757             var r = {  // bogus response object
11758                 responseText : '',
11759                 responseXML : null
11760             };
11761
11762             r.argument = o ? o.argument : null;
11763
11764             try { //
11765                 var doc;
11766                 if(Roo.isIE){
11767                     doc = frame.contentWindow.document;
11768                 }else {
11769                     doc = (frame.contentDocument || window.frames[id].document);
11770                 }
11771                 if(doc && doc.body){
11772                     r.responseText = doc.body.innerHTML;
11773                 }
11774                 if(doc && doc.XMLDocument){
11775                     r.responseXML = doc.XMLDocument;
11776                 }else {
11777                     r.responseXML = doc;
11778                 }
11779             }
11780             catch(e) {
11781                 // ignore
11782             }
11783
11784             Roo.EventManager.removeListener(frame, 'load', cb, this);
11785
11786             this.fireEvent("requestcomplete", this, r, o);
11787             Roo.callback(o.success, o.scope, [r, o]);
11788             Roo.callback(o.callback, o.scope, [o, true, r]);
11789
11790             setTimeout(function(){document.body.removeChild(frame);}, 100);
11791         }
11792
11793         Roo.EventManager.on(frame, 'load', cb, this);
11794         form.submit();
11795
11796         if(hiddens){ // remove dynamic params
11797             for(var i = 0, len = hiddens.length; i < len; i++){
11798                 form.removeChild(hiddens[i]);
11799             }
11800         }
11801     },
11802     // this is a 'formdata version???'
11803     
11804     
11805     doFormDataUpload : function(o, ps, url)
11806     {
11807         var form = Roo.getDom(o.form);
11808         form.enctype = form.encoding = 'multipart/form-data';
11809         var formData = o.formData === true ? new FormData(form) : o.formData;
11810       
11811         var cb = {
11812             success: this.handleResponse,
11813             failure: this.handleFailure,
11814             scope: this,
11815             argument: {options: o},
11816             timeout : o.timeout || this.timeout
11817         };
11818  
11819         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11820             if(o.autoAbort){
11821                 this.abort();
11822             }
11823         }else if(this.autoAbort !== false){
11824             this.abort();
11825         }
11826
11827         //Roo.lib.Ajax.defaultPostHeader = null;
11828         Roo.lib.Ajax.useDefaultHeader = false;
11829         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11830         Roo.lib.Ajax.useDefaultHeader = true;
11831  
11832          
11833     }
11834     
11835 });
11836 /*
11837  * Based on:
11838  * Ext JS Library 1.1.1
11839  * Copyright(c) 2006-2007, Ext JS, LLC.
11840  *
11841  * Originally Released Under LGPL - original licence link has changed is not relivant.
11842  *
11843  * Fork - LGPL
11844  * <script type="text/javascript">
11845  */
11846  
11847 /**
11848  * Global Ajax request class.
11849  * 
11850  * @class Roo.Ajax
11851  * @extends Roo.data.Connection
11852  * @static
11853  * 
11854  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11855  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11856  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11857  * @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)
11858  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11859  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11860  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11861  */
11862 Roo.Ajax = new Roo.data.Connection({
11863     // fix up the docs
11864     /**
11865      * @scope Roo.Ajax
11866      * @type {Boolear} 
11867      */
11868     autoAbort : false,
11869
11870     /**
11871      * Serialize the passed form into a url encoded string
11872      * @scope Roo.Ajax
11873      * @param {String/HTMLElement} form
11874      * @return {String}
11875      */
11876     serializeForm : function(form){
11877         return Roo.lib.Ajax.serializeForm(form);
11878     }
11879 });/*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889
11890  
11891 /**
11892  * @class Roo.UpdateManager
11893  * @extends Roo.util.Observable
11894  * Provides AJAX-style update for Element object.<br><br>
11895  * Usage:<br>
11896  * <pre><code>
11897  * // Get it from a Roo.Element object
11898  * var el = Roo.get("foo");
11899  * var mgr = el.getUpdateManager();
11900  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11901  * ...
11902  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11903  * <br>
11904  * // or directly (returns the same UpdateManager instance)
11905  * var mgr = new Roo.UpdateManager("myElementId");
11906  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11907  * mgr.on("update", myFcnNeedsToKnow);
11908  * <br>
11909    // short handed call directly from the element object
11910    Roo.get("foo").load({
11911         url: "bar.php",
11912         scripts:true,
11913         params: "for=bar",
11914         text: "Loading Foo..."
11915    });
11916  * </code></pre>
11917  * @constructor
11918  * Create new UpdateManager directly.
11919  * @param {String/HTMLElement/Roo.Element} el The element to update
11920  * @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).
11921  */
11922 Roo.UpdateManager = function(el, forceNew){
11923     el = Roo.get(el);
11924     if(!forceNew && el.updateManager){
11925         return el.updateManager;
11926     }
11927     /**
11928      * The Element object
11929      * @type Roo.Element
11930      */
11931     this.el = el;
11932     /**
11933      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11934      * @type String
11935      */
11936     this.defaultUrl = null;
11937
11938     this.addEvents({
11939         /**
11940          * @event beforeupdate
11941          * Fired before an update is made, return false from your handler and the update is cancelled.
11942          * @param {Roo.Element} el
11943          * @param {String/Object/Function} url
11944          * @param {String/Object} params
11945          */
11946         "beforeupdate": true,
11947         /**
11948          * @event update
11949          * Fired after successful update is made.
11950          * @param {Roo.Element} el
11951          * @param {Object} oResponseObject The response Object
11952          */
11953         "update": true,
11954         /**
11955          * @event failure
11956          * Fired on update failure.
11957          * @param {Roo.Element} el
11958          * @param {Object} oResponseObject The response Object
11959          */
11960         "failure": true
11961     });
11962     var d = Roo.UpdateManager.defaults;
11963     /**
11964      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11965      * @type String
11966      */
11967     this.sslBlankUrl = d.sslBlankUrl;
11968     /**
11969      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11970      * @type Boolean
11971      */
11972     this.disableCaching = d.disableCaching;
11973     /**
11974      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11975      * @type String
11976      */
11977     this.indicatorText = d.indicatorText;
11978     /**
11979      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11980      * @type String
11981      */
11982     this.showLoadIndicator = d.showLoadIndicator;
11983     /**
11984      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11985      * @type Number
11986      */
11987     this.timeout = d.timeout;
11988
11989     /**
11990      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11991      * @type Boolean
11992      */
11993     this.loadScripts = d.loadScripts;
11994
11995     /**
11996      * Transaction object of current executing transaction
11997      */
11998     this.transaction = null;
11999
12000     /**
12001      * @private
12002      */
12003     this.autoRefreshProcId = null;
12004     /**
12005      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12006      * @type Function
12007      */
12008     this.refreshDelegate = this.refresh.createDelegate(this);
12009     /**
12010      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12011      * @type Function
12012      */
12013     this.updateDelegate = this.update.createDelegate(this);
12014     /**
12015      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12016      * @type Function
12017      */
12018     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12019     /**
12020      * @private
12021      */
12022     this.successDelegate = this.processSuccess.createDelegate(this);
12023     /**
12024      * @private
12025      */
12026     this.failureDelegate = this.processFailure.createDelegate(this);
12027
12028     if(!this.renderer){
12029      /**
12030       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12031       */
12032     this.renderer = new Roo.UpdateManager.BasicRenderer();
12033     }
12034     
12035     Roo.UpdateManager.superclass.constructor.call(this);
12036 };
12037
12038 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12039     /**
12040      * Get the Element this UpdateManager is bound to
12041      * @return {Roo.Element} The element
12042      */
12043     getEl : function(){
12044         return this.el;
12045     },
12046     /**
12047      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12048      * @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:
12049 <pre><code>
12050 um.update({<br/>
12051     url: "your-url.php",<br/>
12052     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12053     callback: yourFunction,<br/>
12054     scope: yourObject, //(optional scope)  <br/>
12055     discardUrl: false, <br/>
12056     nocache: false,<br/>
12057     text: "Loading...",<br/>
12058     timeout: 30,<br/>
12059     scripts: false<br/>
12060 });
12061 </code></pre>
12062      * The only required property is url. The optional properties nocache, text and scripts
12063      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12064      * @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}
12065      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12066      * @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.
12067      */
12068     update : function(url, params, callback, discardUrl){
12069         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12070             var method = this.method,
12071                 cfg;
12072             if(typeof url == "object"){ // must be config object
12073                 cfg = url;
12074                 url = cfg.url;
12075                 params = params || cfg.params;
12076                 callback = callback || cfg.callback;
12077                 discardUrl = discardUrl || cfg.discardUrl;
12078                 if(callback && cfg.scope){
12079                     callback = callback.createDelegate(cfg.scope);
12080                 }
12081                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12082                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12083                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12084                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12085                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12086             }
12087             this.showLoading();
12088             if(!discardUrl){
12089                 this.defaultUrl = url;
12090             }
12091             if(typeof url == "function"){
12092                 url = url.call(this);
12093             }
12094
12095             method = method || (params ? "POST" : "GET");
12096             if(method == "GET"){
12097                 url = this.prepareUrl(url);
12098             }
12099
12100             var o = Roo.apply(cfg ||{}, {
12101                 url : url,
12102                 params: params,
12103                 success: this.successDelegate,
12104                 failure: this.failureDelegate,
12105                 callback: undefined,
12106                 timeout: (this.timeout*1000),
12107                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12108             });
12109             Roo.log("updated manager called with timeout of " + o.timeout);
12110             this.transaction = Roo.Ajax.request(o);
12111         }
12112     },
12113
12114     /**
12115      * 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.
12116      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12117      * @param {String/HTMLElement} form The form Id or form element
12118      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12119      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12120      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12121      */
12122     formUpdate : function(form, url, reset, callback){
12123         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12124             if(typeof url == "function"){
12125                 url = url.call(this);
12126             }
12127             form = Roo.getDom(form);
12128             this.transaction = Roo.Ajax.request({
12129                 form: form,
12130                 url:url,
12131                 success: this.successDelegate,
12132                 failure: this.failureDelegate,
12133                 timeout: (this.timeout*1000),
12134                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12135             });
12136             this.showLoading.defer(1, this);
12137         }
12138     },
12139
12140     /**
12141      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12142      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12143      */
12144     refresh : function(callback){
12145         if(this.defaultUrl == null){
12146             return;
12147         }
12148         this.update(this.defaultUrl, null, callback, true);
12149     },
12150
12151     /**
12152      * Set this element to auto refresh.
12153      * @param {Number} interval How often to update (in seconds).
12154      * @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)
12155      * @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}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12157      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12158      */
12159     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12160         if(refreshNow){
12161             this.update(url || this.defaultUrl, params, callback, true);
12162         }
12163         if(this.autoRefreshProcId){
12164             clearInterval(this.autoRefreshProcId);
12165         }
12166         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12167     },
12168
12169     /**
12170      * Stop auto refresh on this element.
12171      */
12172      stopAutoRefresh : function(){
12173         if(this.autoRefreshProcId){
12174             clearInterval(this.autoRefreshProcId);
12175             delete this.autoRefreshProcId;
12176         }
12177     },
12178
12179     isAutoRefreshing : function(){
12180        return this.autoRefreshProcId ? true : false;
12181     },
12182     /**
12183      * Called to update the element to "Loading" state. Override to perform custom action.
12184      */
12185     showLoading : function(){
12186         if(this.showLoadIndicator){
12187             this.el.update(this.indicatorText);
12188         }
12189     },
12190
12191     /**
12192      * Adds unique parameter to query string if disableCaching = true
12193      * @private
12194      */
12195     prepareUrl : function(url){
12196         if(this.disableCaching){
12197             var append = "_dc=" + (new Date().getTime());
12198             if(url.indexOf("?") !== -1){
12199                 url += "&" + append;
12200             }else{
12201                 url += "?" + append;
12202             }
12203         }
12204         return url;
12205     },
12206
12207     /**
12208      * @private
12209      */
12210     processSuccess : function(response){
12211         this.transaction = null;
12212         if(response.argument.form && response.argument.reset){
12213             try{ // put in try/catch since some older FF releases had problems with this
12214                 response.argument.form.reset();
12215             }catch(e){}
12216         }
12217         if(this.loadScripts){
12218             this.renderer.render(this.el, response, this,
12219                 this.updateComplete.createDelegate(this, [response]));
12220         }else{
12221             this.renderer.render(this.el, response, this);
12222             this.updateComplete(response);
12223         }
12224     },
12225
12226     updateComplete : function(response){
12227         this.fireEvent("update", this.el, response);
12228         if(typeof response.argument.callback == "function"){
12229             response.argument.callback(this.el, true, response);
12230         }
12231     },
12232
12233     /**
12234      * @private
12235      */
12236     processFailure : function(response){
12237         this.transaction = null;
12238         this.fireEvent("failure", this.el, response);
12239         if(typeof response.argument.callback == "function"){
12240             response.argument.callback(this.el, false, response);
12241         }
12242     },
12243
12244     /**
12245      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12246      * @param {Object} renderer The object implementing the render() method
12247      */
12248     setRenderer : function(renderer){
12249         this.renderer = renderer;
12250     },
12251
12252     getRenderer : function(){
12253        return this.renderer;
12254     },
12255
12256     /**
12257      * Set the defaultUrl used for updates
12258      * @param {String/Function} defaultUrl The url or a function to call to get the url
12259      */
12260     setDefaultUrl : function(defaultUrl){
12261         this.defaultUrl = defaultUrl;
12262     },
12263
12264     /**
12265      * Aborts the executing transaction
12266      */
12267     abort : function(){
12268         if(this.transaction){
12269             Roo.Ajax.abort(this.transaction);
12270         }
12271     },
12272
12273     /**
12274      * Returns true if an update is in progress
12275      * @return {Boolean}
12276      */
12277     isUpdating : function(){
12278         if(this.transaction){
12279             return Roo.Ajax.isLoading(this.transaction);
12280         }
12281         return false;
12282     }
12283 });
12284
12285 /**
12286  * @class Roo.UpdateManager.defaults
12287  * @static (not really - but it helps the doc tool)
12288  * The defaults collection enables customizing the default properties of UpdateManager
12289  */
12290    Roo.UpdateManager.defaults = {
12291        /**
12292          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12293          * @type Number
12294          */
12295          timeout : 30,
12296
12297          /**
12298          * True to process scripts by default (Defaults to false).
12299          * @type Boolean
12300          */
12301         loadScripts : false,
12302
12303         /**
12304         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12305         * @type String
12306         */
12307         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12308         /**
12309          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12310          * @type Boolean
12311          */
12312         disableCaching : false,
12313         /**
12314          * Whether to show indicatorText when loading (Defaults to true).
12315          * @type Boolean
12316          */
12317         showLoadIndicator : true,
12318         /**
12319          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12320          * @type String
12321          */
12322         indicatorText : '<div class="loading-indicator">Loading...</div>'
12323    };
12324
12325 /**
12326  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12327  *Usage:
12328  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12329  * @param {String/HTMLElement/Roo.Element} el The element to update
12330  * @param {String} url The url
12331  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12332  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12333  * @static
12334  * @deprecated
12335  * @member Roo.UpdateManager
12336  */
12337 Roo.UpdateManager.updateElement = function(el, url, params, options){
12338     var um = Roo.get(el, true).getUpdateManager();
12339     Roo.apply(um, options);
12340     um.update(url, params, options ? options.callback : null);
12341 };
12342 // alias for backwards compat
12343 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12344 /**
12345  * @class Roo.UpdateManager.BasicRenderer
12346  * Default Content renderer. Updates the elements innerHTML with the responseText.
12347  */
12348 Roo.UpdateManager.BasicRenderer = function(){};
12349
12350 Roo.UpdateManager.BasicRenderer.prototype = {
12351     /**
12352      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12353      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12354      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12355      * @param {Roo.Element} el The element being rendered
12356      * @param {Object} response The YUI Connect response object
12357      * @param {UpdateManager} updateManager The calling update manager
12358      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12359      */
12360      render : function(el, response, updateManager, callback){
12361         el.update(response.responseText, updateManager.loadScripts, callback);
12362     }
12363 };
12364 /*
12365  * Based on:
12366  * Roo JS
12367  * (c)) Alan Knowles
12368  * Licence : LGPL
12369  */
12370
12371
12372 /**
12373  * @class Roo.DomTemplate
12374  * @extends Roo.Template
12375  * An effort at a dom based template engine..
12376  *
12377  * Similar to XTemplate, except it uses dom parsing to create the template..
12378  *
12379  * Supported features:
12380  *
12381  *  Tags:
12382
12383 <pre><code>
12384       {a_variable} - output encoded.
12385       {a_variable.format:("Y-m-d")} - call a method on the variable
12386       {a_variable:raw} - unencoded output
12387       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12388       {a_variable:this.method_on_template(...)} - call a method on the template object.
12389  
12390 </code></pre>
12391  *  The tpl tag:
12392 <pre><code>
12393         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12394         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12395         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12396         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12397   
12398 </code></pre>
12399  *      
12400  */
12401 Roo.DomTemplate = function()
12402 {
12403      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12404      if (this.html) {
12405         this.compile();
12406      }
12407 };
12408
12409
12410 Roo.extend(Roo.DomTemplate, Roo.Template, {
12411     /**
12412      * id counter for sub templates.
12413      */
12414     id : 0,
12415     /**
12416      * flag to indicate if dom parser is inside a pre,
12417      * it will strip whitespace if not.
12418      */
12419     inPre : false,
12420     
12421     /**
12422      * The various sub templates
12423      */
12424     tpls : false,
12425     
12426     
12427     
12428     /**
12429      *
12430      * basic tag replacing syntax
12431      * WORD:WORD()
12432      *
12433      * // you can fake an object call by doing this
12434      *  x.t:(test,tesT) 
12435      * 
12436      */
12437     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12438     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12439     
12440     iterChild : function (node, method) {
12441         
12442         var oldPre = this.inPre;
12443         if (node.tagName == 'PRE') {
12444             this.inPre = true;
12445         }
12446         for( var i = 0; i < node.childNodes.length; i++) {
12447             method.call(this, node.childNodes[i]);
12448         }
12449         this.inPre = oldPre;
12450     },
12451     
12452     
12453     
12454     /**
12455      * compile the template
12456      *
12457      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12458      *
12459      */
12460     compile: function()
12461     {
12462         var s = this.html;
12463         
12464         // covert the html into DOM...
12465         var doc = false;
12466         var div =false;
12467         try {
12468             doc = document.implementation.createHTMLDocument("");
12469             doc.documentElement.innerHTML =   this.html  ;
12470             div = doc.documentElement;
12471         } catch (e) {
12472             // old IE... - nasty -- it causes all sorts of issues.. with
12473             // images getting pulled from server..
12474             div = document.createElement('div');
12475             div.innerHTML = this.html;
12476         }
12477         //doc.documentElement.innerHTML = htmlBody
12478          
12479         
12480         
12481         this.tpls = [];
12482         var _t = this;
12483         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12484         
12485         var tpls = this.tpls;
12486         
12487         // create a top level template from the snippet..
12488         
12489         //Roo.log(div.innerHTML);
12490         
12491         var tpl = {
12492             uid : 'master',
12493             id : this.id++,
12494             attr : false,
12495             value : false,
12496             body : div.innerHTML,
12497             
12498             forCall : false,
12499             execCall : false,
12500             dom : div,
12501             isTop : true
12502             
12503         };
12504         tpls.unshift(tpl);
12505         
12506         
12507         // compile them...
12508         this.tpls = [];
12509         Roo.each(tpls, function(tp){
12510             this.compileTpl(tp);
12511             this.tpls[tp.id] = tp;
12512         }, this);
12513         
12514         this.master = tpls[0];
12515         return this;
12516         
12517         
12518     },
12519     
12520     compileNode : function(node, istop) {
12521         // test for
12522         //Roo.log(node);
12523         
12524         
12525         // skip anything not a tag..
12526         if (node.nodeType != 1) {
12527             if (node.nodeType == 3 && !this.inPre) {
12528                 // reduce white space..
12529                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12530                 
12531             }
12532             return;
12533         }
12534         
12535         var tpl = {
12536             uid : false,
12537             id : false,
12538             attr : false,
12539             value : false,
12540             body : '',
12541             
12542             forCall : false,
12543             execCall : false,
12544             dom : false,
12545             isTop : istop
12546             
12547             
12548         };
12549         
12550         
12551         switch(true) {
12552             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12553             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12554             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12555             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12556             // no default..
12557         }
12558         
12559         
12560         if (!tpl.attr) {
12561             // just itterate children..
12562             this.iterChild(node,this.compileNode);
12563             return;
12564         }
12565         tpl.uid = this.id++;
12566         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12567         node.removeAttribute('roo-'+ tpl.attr);
12568         if (tpl.attr != 'name') {
12569             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12570             node.parentNode.replaceChild(placeholder,  node);
12571         } else {
12572             
12573             var placeholder =  document.createElement('span');
12574             placeholder.className = 'roo-tpl-' + tpl.value;
12575             node.parentNode.replaceChild(placeholder,  node);
12576         }
12577         
12578         // parent now sees '{domtplXXXX}
12579         this.iterChild(node,this.compileNode);
12580         
12581         // we should now have node body...
12582         var div = document.createElement('div');
12583         div.appendChild(node);
12584         tpl.dom = node;
12585         // this has the unfortunate side effect of converting tagged attributes
12586         // eg. href="{...}" into %7C...%7D
12587         // this has been fixed by searching for those combo's although it's a bit hacky..
12588         
12589         
12590         tpl.body = div.innerHTML;
12591         
12592         
12593          
12594         tpl.id = tpl.uid;
12595         switch(tpl.attr) {
12596             case 'for' :
12597                 switch (tpl.value) {
12598                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12599                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12600                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12601                 }
12602                 break;
12603             
12604             case 'exec':
12605                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12606                 break;
12607             
12608             case 'if':     
12609                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12610                 break;
12611             
12612             case 'name':
12613                 tpl.id  = tpl.value; // replace non characters???
12614                 break;
12615             
12616         }
12617         
12618         
12619         this.tpls.push(tpl);
12620         
12621         
12622         
12623     },
12624     
12625     
12626     
12627     
12628     /**
12629      * Compile a segment of the template into a 'sub-template'
12630      *
12631      * 
12632      * 
12633      *
12634      */
12635     compileTpl : function(tpl)
12636     {
12637         var fm = Roo.util.Format;
12638         var useF = this.disableFormats !== true;
12639         
12640         var sep = Roo.isGecko ? "+\n" : ",\n";
12641         
12642         var undef = function(str) {
12643             Roo.debug && Roo.log("Property not found :"  + str);
12644             return '';
12645         };
12646           
12647         //Roo.log(tpl.body);
12648         
12649         
12650         
12651         var fn = function(m, lbrace, name, format, args)
12652         {
12653             //Roo.log("ARGS");
12654             //Roo.log(arguments);
12655             args = args ? args.replace(/\\'/g,"'") : args;
12656             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12657             if (typeof(format) == 'undefined') {
12658                 format =  'htmlEncode'; 
12659             }
12660             if (format == 'raw' ) {
12661                 format = false;
12662             }
12663             
12664             if(name.substr(0, 6) == 'domtpl'){
12665                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12666             }
12667             
12668             // build an array of options to determine if value is undefined..
12669             
12670             // basically get 'xxxx.yyyy' then do
12671             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12672             //    (function () { Roo.log("Property not found"); return ''; })() :
12673             //    ......
12674             
12675             var udef_ar = [];
12676             var lookfor = '';
12677             Roo.each(name.split('.'), function(st) {
12678                 lookfor += (lookfor.length ? '.': '') + st;
12679                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12680             });
12681             
12682             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12683             
12684             
12685             if(format && useF){
12686                 
12687                 args = args ? ',' + args : "";
12688                  
12689                 if(format.substr(0, 5) != "this."){
12690                     format = "fm." + format + '(';
12691                 }else{
12692                     format = 'this.call("'+ format.substr(5) + '", ';
12693                     args = ", values";
12694                 }
12695                 
12696                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12697             }
12698              
12699             if (args && args.length) {
12700                 // called with xxyx.yuu:(test,test)
12701                 // change to ()
12702                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12703             }
12704             // raw.. - :raw modifier..
12705             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12706             
12707         };
12708         var body;
12709         // branched to use + in gecko and [].join() in others
12710         if(Roo.isGecko){
12711             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12712                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12713                     "';};};";
12714         }else{
12715             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12716             body.push(tpl.body.replace(/(\r\n|\n)/g,
12717                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12718             body.push("'].join('');};};");
12719             body = body.join('');
12720         }
12721         
12722         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12723        
12724         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12725         eval(body);
12726         
12727         return this;
12728     },
12729      
12730     /**
12731      * same as applyTemplate, except it's done to one of the subTemplates
12732      * when using named templates, you can do:
12733      *
12734      * var str = pl.applySubTemplate('your-name', values);
12735      *
12736      * 
12737      * @param {Number} id of the template
12738      * @param {Object} values to apply to template
12739      * @param {Object} parent (normaly the instance of this object)
12740      */
12741     applySubTemplate : function(id, values, parent)
12742     {
12743         
12744         
12745         var t = this.tpls[id];
12746         
12747         
12748         try { 
12749             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12750                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12751                 return '';
12752             }
12753         } catch(e) {
12754             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12755             Roo.log(values);
12756           
12757             return '';
12758         }
12759         try { 
12760             
12761             if(t.execCall && t.execCall.call(this, values, parent)){
12762                 return '';
12763             }
12764         } catch(e) {
12765             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12766             Roo.log(values);
12767             return '';
12768         }
12769         
12770         try {
12771             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12772             parent = t.target ? values : parent;
12773             if(t.forCall && vs instanceof Array){
12774                 var buf = [];
12775                 for(var i = 0, len = vs.length; i < len; i++){
12776                     try {
12777                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12778                     } catch (e) {
12779                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12780                         Roo.log(e.body);
12781                         //Roo.log(t.compiled);
12782                         Roo.log(vs[i]);
12783                     }   
12784                 }
12785                 return buf.join('');
12786             }
12787         } catch (e) {
12788             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12789             Roo.log(values);
12790             return '';
12791         }
12792         try {
12793             return t.compiled.call(this, vs, parent);
12794         } catch (e) {
12795             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12796             Roo.log(e.body);
12797             //Roo.log(t.compiled);
12798             Roo.log(values);
12799             return '';
12800         }
12801     },
12802
12803    
12804
12805     applyTemplate : function(values){
12806         return this.master.compiled.call(this, values, {});
12807         //var s = this.subs;
12808     },
12809
12810     apply : function(){
12811         return this.applyTemplate.apply(this, arguments);
12812     }
12813
12814  });
12815
12816 Roo.DomTemplate.from = function(el){
12817     el = Roo.getDom(el);
12818     return new Roo.Domtemplate(el.value || el.innerHTML);
12819 };/*
12820  * Based on:
12821  * Ext JS Library 1.1.1
12822  * Copyright(c) 2006-2007, Ext JS, LLC.
12823  *
12824  * Originally Released Under LGPL - original licence link has changed is not relivant.
12825  *
12826  * Fork - LGPL
12827  * <script type="text/javascript">
12828  */
12829
12830 /**
12831  * @class Roo.util.DelayedTask
12832  * Provides a convenient method of performing setTimeout where a new
12833  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12834  * You can use this class to buffer
12835  * the keypress events for a certain number of milliseconds, and perform only if they stop
12836  * for that amount of time.
12837  * @constructor The parameters to this constructor serve as defaults and are not required.
12838  * @param {Function} fn (optional) The default function to timeout
12839  * @param {Object} scope (optional) The default scope of that timeout
12840  * @param {Array} args (optional) The default Array of arguments
12841  */
12842 Roo.util.DelayedTask = function(fn, scope, args){
12843     var id = null, d, t;
12844
12845     var call = function(){
12846         var now = new Date().getTime();
12847         if(now - t >= d){
12848             clearInterval(id);
12849             id = null;
12850             fn.apply(scope, args || []);
12851         }
12852     };
12853     /**
12854      * Cancels any pending timeout and queues a new one
12855      * @param {Number} delay The milliseconds to delay
12856      * @param {Function} newFn (optional) Overrides function passed to constructor
12857      * @param {Object} newScope (optional) Overrides scope passed to constructor
12858      * @param {Array} newArgs (optional) Overrides args passed to constructor
12859      */
12860     this.delay = function(delay, newFn, newScope, newArgs){
12861         if(id && delay != d){
12862             this.cancel();
12863         }
12864         d = delay;
12865         t = new Date().getTime();
12866         fn = newFn || fn;
12867         scope = newScope || scope;
12868         args = newArgs || args;
12869         if(!id){
12870             id = setInterval(call, d);
12871         }
12872     };
12873
12874     /**
12875      * Cancel the last queued timeout
12876      */
12877     this.cancel = function(){
12878         if(id){
12879             clearInterval(id);
12880             id = null;
12881         }
12882     };
12883 };/*
12884  * Based on:
12885  * Ext JS Library 1.1.1
12886  * Copyright(c) 2006-2007, Ext JS, LLC.
12887  *
12888  * Originally Released Under LGPL - original licence link has changed is not relivant.
12889  *
12890  * Fork - LGPL
12891  * <script type="text/javascript">
12892  */
12893  
12894  
12895 Roo.util.TaskRunner = function(interval){
12896     interval = interval || 10;
12897     var tasks = [], removeQueue = [];
12898     var id = 0;
12899     var running = false;
12900
12901     var stopThread = function(){
12902         running = false;
12903         clearInterval(id);
12904         id = 0;
12905     };
12906
12907     var startThread = function(){
12908         if(!running){
12909             running = true;
12910             id = setInterval(runTasks, interval);
12911         }
12912     };
12913
12914     var removeTask = function(task){
12915         removeQueue.push(task);
12916         if(task.onStop){
12917             task.onStop();
12918         }
12919     };
12920
12921     var runTasks = function(){
12922         if(removeQueue.length > 0){
12923             for(var i = 0, len = removeQueue.length; i < len; i++){
12924                 tasks.remove(removeQueue[i]);
12925             }
12926             removeQueue = [];
12927             if(tasks.length < 1){
12928                 stopThread();
12929                 return;
12930             }
12931         }
12932         var now = new Date().getTime();
12933         for(var i = 0, len = tasks.length; i < len; ++i){
12934             var t = tasks[i];
12935             var itime = now - t.taskRunTime;
12936             if(t.interval <= itime){
12937                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12938                 t.taskRunTime = now;
12939                 if(rt === false || t.taskRunCount === t.repeat){
12940                     removeTask(t);
12941                     return;
12942                 }
12943             }
12944             if(t.duration && t.duration <= (now - t.taskStartTime)){
12945                 removeTask(t);
12946             }
12947         }
12948     };
12949
12950     /**
12951      * Queues a new task.
12952      * @param {Object} task
12953      */
12954     this.start = function(task){
12955         tasks.push(task);
12956         task.taskStartTime = new Date().getTime();
12957         task.taskRunTime = 0;
12958         task.taskRunCount = 0;
12959         startThread();
12960         return task;
12961     };
12962
12963     this.stop = function(task){
12964         removeTask(task);
12965         return task;
12966     };
12967
12968     this.stopAll = function(){
12969         stopThread();
12970         for(var i = 0, len = tasks.length; i < len; i++){
12971             if(tasks[i].onStop){
12972                 tasks[i].onStop();
12973             }
12974         }
12975         tasks = [];
12976         removeQueue = [];
12977     };
12978 };
12979
12980 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990
12991  
12992 /**
12993  * @class Roo.util.MixedCollection
12994  * @extends Roo.util.Observable
12995  * A Collection class that maintains both numeric indexes and keys and exposes events.
12996  * @constructor
12997  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12998  * collection (defaults to false)
12999  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13000  * and return the key value for that item.  This is used when available to look up the key on items that
13001  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13002  * equivalent to providing an implementation for the {@link #getKey} method.
13003  */
13004 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13005     this.items = [];
13006     this.map = {};
13007     this.keys = [];
13008     this.length = 0;
13009     this.addEvents({
13010         /**
13011          * @event clear
13012          * Fires when the collection is cleared.
13013          */
13014         "clear" : true,
13015         /**
13016          * @event add
13017          * Fires when an item is added to the collection.
13018          * @param {Number} index The index at which the item was added.
13019          * @param {Object} o The item added.
13020          * @param {String} key The key associated with the added item.
13021          */
13022         "add" : true,
13023         /**
13024          * @event replace
13025          * Fires when an item is replaced in the collection.
13026          * @param {String} key he key associated with the new added.
13027          * @param {Object} old The item being replaced.
13028          * @param {Object} new The new item.
13029          */
13030         "replace" : true,
13031         /**
13032          * @event remove
13033          * Fires when an item is removed from the collection.
13034          * @param {Object} o The item being removed.
13035          * @param {String} key (optional) The key associated with the removed item.
13036          */
13037         "remove" : true,
13038         "sort" : true
13039     });
13040     this.allowFunctions = allowFunctions === true;
13041     if(keyFn){
13042         this.getKey = keyFn;
13043     }
13044     Roo.util.MixedCollection.superclass.constructor.call(this);
13045 };
13046
13047 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13048     allowFunctions : false,
13049     
13050 /**
13051  * Adds an item to the collection.
13052  * @param {String} key The key to associate with the item
13053  * @param {Object} o The item to add.
13054  * @return {Object} The item added.
13055  */
13056     add : function(key, o){
13057         if(arguments.length == 1){
13058             o = arguments[0];
13059             key = this.getKey(o);
13060         }
13061         if(typeof key == "undefined" || key === null){
13062             this.length++;
13063             this.items.push(o);
13064             this.keys.push(null);
13065         }else{
13066             var old = this.map[key];
13067             if(old){
13068                 return this.replace(key, o);
13069             }
13070             this.length++;
13071             this.items.push(o);
13072             this.map[key] = o;
13073             this.keys.push(key);
13074         }
13075         this.fireEvent("add", this.length-1, o, key);
13076         return o;
13077     },
13078        
13079 /**
13080   * MixedCollection has a generic way to fetch keys if you implement getKey.
13081 <pre><code>
13082 // normal way
13083 var mc = new Roo.util.MixedCollection();
13084 mc.add(someEl.dom.id, someEl);
13085 mc.add(otherEl.dom.id, otherEl);
13086 //and so on
13087
13088 // using getKey
13089 var mc = new Roo.util.MixedCollection();
13090 mc.getKey = function(el){
13091    return el.dom.id;
13092 };
13093 mc.add(someEl);
13094 mc.add(otherEl);
13095
13096 // or via the constructor
13097 var mc = new Roo.util.MixedCollection(false, function(el){
13098    return el.dom.id;
13099 });
13100 mc.add(someEl);
13101 mc.add(otherEl);
13102 </code></pre>
13103  * @param o {Object} The item for which to find the key.
13104  * @return {Object} The key for the passed item.
13105  */
13106     getKey : function(o){
13107          return o.id; 
13108     },
13109    
13110 /**
13111  * Replaces an item in the collection.
13112  * @param {String} key The key associated with the item to replace, or the item to replace.
13113  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13114  * @return {Object}  The new item.
13115  */
13116     replace : function(key, o){
13117         if(arguments.length == 1){
13118             o = arguments[0];
13119             key = this.getKey(o);
13120         }
13121         var old = this.item(key);
13122         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13123              return this.add(key, o);
13124         }
13125         var index = this.indexOfKey(key);
13126         this.items[index] = o;
13127         this.map[key] = o;
13128         this.fireEvent("replace", key, old, o);
13129         return o;
13130     },
13131    
13132 /**
13133  * Adds all elements of an Array or an Object to the collection.
13134  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13135  * an Array of values, each of which are added to the collection.
13136  */
13137     addAll : function(objs){
13138         if(arguments.length > 1 || objs instanceof Array){
13139             var args = arguments.length > 1 ? arguments : objs;
13140             for(var i = 0, len = args.length; i < len; i++){
13141                 this.add(args[i]);
13142             }
13143         }else{
13144             for(var key in objs){
13145                 if(this.allowFunctions || typeof objs[key] != "function"){
13146                     this.add(key, objs[key]);
13147                 }
13148             }
13149         }
13150     },
13151    
13152 /**
13153  * Executes the specified function once for every item in the collection, passing each
13154  * item as the first and only parameter. returning false from the function will stop the iteration.
13155  * @param {Function} fn The function to execute for each item.
13156  * @param {Object} scope (optional) The scope in which to execute the function.
13157  */
13158     each : function(fn, scope){
13159         var items = [].concat(this.items); // each safe for removal
13160         for(var i = 0, len = items.length; i < len; i++){
13161             if(fn.call(scope || items[i], items[i], i, len) === false){
13162                 break;
13163             }
13164         }
13165     },
13166    
13167 /**
13168  * Executes the specified function once for every key in the collection, passing each
13169  * key, and its associated item as the first two parameters.
13170  * @param {Function} fn The function to execute for each item.
13171  * @param {Object} scope (optional) The scope in which to execute the function.
13172  */
13173     eachKey : function(fn, scope){
13174         for(var i = 0, len = this.keys.length; i < len; i++){
13175             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the first item in the collection which elicits a true return value from the
13181  * passed selection function.
13182  * @param {Function} fn The selection function to execute for each item.
13183  * @param {Object} scope (optional) The scope in which to execute the function.
13184  * @return {Object} The first item in the collection which returned true from the selection function.
13185  */
13186     find : function(fn, scope){
13187         for(var i = 0, len = this.items.length; i < len; i++){
13188             if(fn.call(scope || window, this.items[i], this.keys[i])){
13189                 return this.items[i];
13190             }
13191         }
13192         return null;
13193     },
13194    
13195 /**
13196  * Inserts an item at the specified index in the collection.
13197  * @param {Number} index The index to insert the item at.
13198  * @param {String} key The key to associate with the new item, or the item itself.
13199  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13200  * @return {Object} The item inserted.
13201  */
13202     insert : function(index, key, o){
13203         if(arguments.length == 2){
13204             o = arguments[1];
13205             key = this.getKey(o);
13206         }
13207         if(index >= this.length){
13208             return this.add(key, o);
13209         }
13210         this.length++;
13211         this.items.splice(index, 0, o);
13212         if(typeof key != "undefined" && key != null){
13213             this.map[key] = o;
13214         }
13215         this.keys.splice(index, 0, key);
13216         this.fireEvent("add", index, o, key);
13217         return o;
13218     },
13219    
13220 /**
13221  * Removed an item from the collection.
13222  * @param {Object} o The item to remove.
13223  * @return {Object} The item removed.
13224  */
13225     remove : function(o){
13226         return this.removeAt(this.indexOf(o));
13227     },
13228    
13229 /**
13230  * Remove an item from a specified index in the collection.
13231  * @param {Number} index The index within the collection of the item to remove.
13232  */
13233     removeAt : function(index){
13234         if(index < this.length && index >= 0){
13235             this.length--;
13236             var o = this.items[index];
13237             this.items.splice(index, 1);
13238             var key = this.keys[index];
13239             if(typeof key != "undefined"){
13240                 delete this.map[key];
13241             }
13242             this.keys.splice(index, 1);
13243             this.fireEvent("remove", o, key);
13244         }
13245     },
13246    
13247 /**
13248  * Removed an item associated with the passed key fom the collection.
13249  * @param {String} key The key of the item to remove.
13250  */
13251     removeKey : function(key){
13252         return this.removeAt(this.indexOfKey(key));
13253     },
13254    
13255 /**
13256  * Returns the number of items in the collection.
13257  * @return {Number} the number of items in the collection.
13258  */
13259     getCount : function(){
13260         return this.length; 
13261     },
13262    
13263 /**
13264  * Returns index within the collection of the passed Object.
13265  * @param {Object} o The item to find the index of.
13266  * @return {Number} index of the item.
13267  */
13268     indexOf : function(o){
13269         if(!this.items.indexOf){
13270             for(var i = 0, len = this.items.length; i < len; i++){
13271                 if(this.items[i] == o) {
13272                     return i;
13273                 }
13274             }
13275             return -1;
13276         }else{
13277             return this.items.indexOf(o);
13278         }
13279     },
13280    
13281 /**
13282  * Returns index within the collection of the passed key.
13283  * @param {String} key The key to find the index of.
13284  * @return {Number} index of the key.
13285  */
13286     indexOfKey : function(key){
13287         if(!this.keys.indexOf){
13288             for(var i = 0, len = this.keys.length; i < len; i++){
13289                 if(this.keys[i] == key) {
13290                     return i;
13291                 }
13292             }
13293             return -1;
13294         }else{
13295             return this.keys.indexOf(key);
13296         }
13297     },
13298    
13299 /**
13300  * Returns the item associated with the passed key OR index. Key has priority over index.
13301  * @param {String/Number} key The key or index of the item.
13302  * @return {Object} The item associated with the passed key.
13303  */
13304     item : function(key){
13305         if (key === 'length') {
13306             return null;
13307         }
13308         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13309         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13310     },
13311     
13312 /**
13313  * Returns the item at the specified index.
13314  * @param {Number} index The index of the item.
13315  * @return {Object}
13316  */
13317     itemAt : function(index){
13318         return this.items[index];
13319     },
13320     
13321 /**
13322  * Returns the item associated with the passed key.
13323  * @param {String/Number} key The key of the item.
13324  * @return {Object} The item associated with the passed key.
13325  */
13326     key : function(key){
13327         return this.map[key];
13328     },
13329    
13330 /**
13331  * Returns true if the collection contains the passed Object as an item.
13332  * @param {Object} o  The Object to look for in the collection.
13333  * @return {Boolean} True if the collection contains the Object as an item.
13334  */
13335     contains : function(o){
13336         return this.indexOf(o) != -1;
13337     },
13338    
13339 /**
13340  * Returns true if the collection contains the passed Object as a key.
13341  * @param {String} key The key to look for in the collection.
13342  * @return {Boolean} True if the collection contains the Object as a key.
13343  */
13344     containsKey : function(key){
13345         return typeof this.map[key] != "undefined";
13346     },
13347    
13348 /**
13349  * Removes all items from the collection.
13350  */
13351     clear : function(){
13352         this.length = 0;
13353         this.items = [];
13354         this.keys = [];
13355         this.map = {};
13356         this.fireEvent("clear");
13357     },
13358    
13359 /**
13360  * Returns the first item in the collection.
13361  * @return {Object} the first item in the collection..
13362  */
13363     first : function(){
13364         return this.items[0]; 
13365     },
13366    
13367 /**
13368  * Returns the last item in the collection.
13369  * @return {Object} the last item in the collection..
13370  */
13371     last : function(){
13372         return this.items[this.length-1];   
13373     },
13374     
13375     _sort : function(property, dir, fn){
13376         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13377         fn = fn || function(a, b){
13378             return a-b;
13379         };
13380         var c = [], k = this.keys, items = this.items;
13381         for(var i = 0, len = items.length; i < len; i++){
13382             c[c.length] = {key: k[i], value: items[i], index: i};
13383         }
13384         c.sort(function(a, b){
13385             var v = fn(a[property], b[property]) * dsc;
13386             if(v == 0){
13387                 v = (a.index < b.index ? -1 : 1);
13388             }
13389             return v;
13390         });
13391         for(var i = 0, len = c.length; i < len; i++){
13392             items[i] = c[i].value;
13393             k[i] = c[i].key;
13394         }
13395         this.fireEvent("sort", this);
13396     },
13397     
13398     /**
13399      * Sorts this collection with the passed comparison function
13400      * @param {String} direction (optional) "ASC" or "DESC"
13401      * @param {Function} fn (optional) comparison function
13402      */
13403     sort : function(dir, fn){
13404         this._sort("value", dir, fn);
13405     },
13406     
13407     /**
13408      * Sorts this collection by keys
13409      * @param {String} direction (optional) "ASC" or "DESC"
13410      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13411      */
13412     keySort : function(dir, fn){
13413         this._sort("key", dir, fn || function(a, b){
13414             return String(a).toUpperCase()-String(b).toUpperCase();
13415         });
13416     },
13417     
13418     /**
13419      * Returns a range of items in this collection
13420      * @param {Number} startIndex (optional) defaults to 0
13421      * @param {Number} endIndex (optional) default to the last item
13422      * @return {Array} An array of items
13423      */
13424     getRange : function(start, end){
13425         var items = this.items;
13426         if(items.length < 1){
13427             return [];
13428         }
13429         start = start || 0;
13430         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13431         var r = [];
13432         if(start <= end){
13433             for(var i = start; i <= end; i++) {
13434                     r[r.length] = items[i];
13435             }
13436         }else{
13437             for(var i = start; i >= end; i--) {
13438                     r[r.length] = items[i];
13439             }
13440         }
13441         return r;
13442     },
13443         
13444     /**
13445      * Filter the <i>objects</i> in this collection by a specific property. 
13446      * Returns a new collection that has been filtered.
13447      * @param {String} property A property on your objects
13448      * @param {String/RegExp} value Either string that the property values 
13449      * should start with or a RegExp to test against the property
13450      * @return {MixedCollection} The new filtered collection
13451      */
13452     filter : function(property, value){
13453         if(!value.exec){ // not a regex
13454             value = String(value);
13455             if(value.length == 0){
13456                 return this.clone();
13457             }
13458             value = new RegExp("^" + Roo.escapeRe(value), "i");
13459         }
13460         return this.filterBy(function(o){
13461             return o && value.test(o[property]);
13462         });
13463         },
13464     
13465     /**
13466      * Filter by a function. * Returns a new collection that has been filtered.
13467      * The passed function will be called with each 
13468      * object in the collection. If the function returns true, the value is included 
13469      * otherwise it is filtered.
13470      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13471      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13472      * @return {MixedCollection} The new filtered collection
13473      */
13474     filterBy : function(fn, scope){
13475         var r = new Roo.util.MixedCollection();
13476         r.getKey = this.getKey;
13477         var k = this.keys, it = this.items;
13478         for(var i = 0, len = it.length; i < len; i++){
13479             if(fn.call(scope||this, it[i], k[i])){
13480                                 r.add(k[i], it[i]);
13481                         }
13482         }
13483         return r;
13484     },
13485     
13486     /**
13487      * Creates a duplicate of this collection
13488      * @return {MixedCollection}
13489      */
13490     clone : function(){
13491         var r = new Roo.util.MixedCollection();
13492         var k = this.keys, it = this.items;
13493         for(var i = 0, len = it.length; i < len; i++){
13494             r.add(k[i], it[i]);
13495         }
13496         r.getKey = this.getKey;
13497         return r;
13498     }
13499 });
13500 /**
13501  * Returns the item associated with the passed key or index.
13502  * @method
13503  * @param {String/Number} key The key or index of the item.
13504  * @return {Object} The item associated with the passed key.
13505  */
13506 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13507  * Based on:
13508  * Ext JS Library 1.1.1
13509  * Copyright(c) 2006-2007, Ext JS, LLC.
13510  *
13511  * Originally Released Under LGPL - original licence link has changed is not relivant.
13512  *
13513  * Fork - LGPL
13514  * <script type="text/javascript">
13515  */
13516 /**
13517  * @class Roo.util.JSON
13518  * Modified version of Douglas Crockford"s json.js that doesn"t
13519  * mess with the Object prototype 
13520  * http://www.json.org/js.html
13521  * @singleton
13522  */
13523 Roo.util.JSON = new (function(){
13524     var useHasOwn = {}.hasOwnProperty ? true : false;
13525     
13526     // crashes Safari in some instances
13527     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13528     
13529     var pad = function(n) {
13530         return n < 10 ? "0" + n : n;
13531     };
13532     
13533     var m = {
13534         "\b": '\\b',
13535         "\t": '\\t',
13536         "\n": '\\n',
13537         "\f": '\\f',
13538         "\r": '\\r',
13539         '"' : '\\"',
13540         "\\": '\\\\'
13541     };
13542
13543     var encodeString = function(s){
13544         if (/["\\\x00-\x1f]/.test(s)) {
13545             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13546                 var c = m[b];
13547                 if(c){
13548                     return c;
13549                 }
13550                 c = b.charCodeAt();
13551                 return "\\u00" +
13552                     Math.floor(c / 16).toString(16) +
13553                     (c % 16).toString(16);
13554             }) + '"';
13555         }
13556         return '"' + s + '"';
13557     };
13558     
13559     var encodeArray = function(o){
13560         var a = ["["], b, i, l = o.length, v;
13561             for (i = 0; i < l; i += 1) {
13562                 v = o[i];
13563                 switch (typeof v) {
13564                     case "undefined":
13565                     case "function":
13566                     case "unknown":
13567                         break;
13568                     default:
13569                         if (b) {
13570                             a.push(',');
13571                         }
13572                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13573                         b = true;
13574                 }
13575             }
13576             a.push("]");
13577             return a.join("");
13578     };
13579     
13580     var encodeDate = function(o){
13581         return '"' + o.getFullYear() + "-" +
13582                 pad(o.getMonth() + 1) + "-" +
13583                 pad(o.getDate()) + "T" +
13584                 pad(o.getHours()) + ":" +
13585                 pad(o.getMinutes()) + ":" +
13586                 pad(o.getSeconds()) + '"';
13587     };
13588     
13589     /**
13590      * Encodes an Object, Array or other value
13591      * @param {Mixed} o The variable to encode
13592      * @return {String} The JSON string
13593      */
13594     this.encode = function(o)
13595     {
13596         // should this be extended to fully wrap stringify..
13597         
13598         if(typeof o == "undefined" || o === null){
13599             return "null";
13600         }else if(o instanceof Array){
13601             return encodeArray(o);
13602         }else if(o instanceof Date){
13603             return encodeDate(o);
13604         }else if(typeof o == "string"){
13605             return encodeString(o);
13606         }else if(typeof o == "number"){
13607             return isFinite(o) ? String(o) : "null";
13608         }else if(typeof o == "boolean"){
13609             return String(o);
13610         }else {
13611             var a = ["{"], b, i, v;
13612             for (i in o) {
13613                 if(!useHasOwn || o.hasOwnProperty(i)) {
13614                     v = o[i];
13615                     switch (typeof v) {
13616                     case "undefined":
13617                     case "function":
13618                     case "unknown":
13619                         break;
13620                     default:
13621                         if(b){
13622                             a.push(',');
13623                         }
13624                         a.push(this.encode(i), ":",
13625                                 v === null ? "null" : this.encode(v));
13626                         b = true;
13627                     }
13628                 }
13629             }
13630             a.push("}");
13631             return a.join("");
13632         }
13633     };
13634     
13635     /**
13636      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13637      * @param {String} json The JSON string
13638      * @return {Object} The resulting object
13639      */
13640     this.decode = function(json){
13641         
13642         return  /** eval:var:json */ eval("(" + json + ')');
13643     };
13644 })();
13645 /** 
13646  * Shorthand for {@link Roo.util.JSON#encode}
13647  * @member Roo encode 
13648  * @method */
13649 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13650 /** 
13651  * Shorthand for {@link Roo.util.JSON#decode}
13652  * @member Roo decode 
13653  * @method */
13654 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13655 /*
13656  * Based on:
13657  * Ext JS Library 1.1.1
13658  * Copyright(c) 2006-2007, Ext JS, LLC.
13659  *
13660  * Originally Released Under LGPL - original licence link has changed is not relivant.
13661  *
13662  * Fork - LGPL
13663  * <script type="text/javascript">
13664  */
13665  
13666 /**
13667  * @class Roo.util.Format
13668  * Reusable data formatting functions
13669  * @singleton
13670  */
13671 Roo.util.Format = function(){
13672     var trimRe = /^\s+|\s+$/g;
13673     return {
13674         /**
13675          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13676          * @param {String} value The string to truncate
13677          * @param {Number} length The maximum length to allow before truncating
13678          * @return {String} The converted text
13679          */
13680         ellipsis : function(value, len){
13681             if(value && value.length > len){
13682                 return value.substr(0, len-3)+"...";
13683             }
13684             return value;
13685         },
13686
13687         /**
13688          * Checks a reference and converts it to empty string if it is undefined
13689          * @param {Mixed} value Reference to check
13690          * @return {Mixed} Empty string if converted, otherwise the original value
13691          */
13692         undef : function(value){
13693             return typeof value != "undefined" ? value : "";
13694         },
13695
13696         /**
13697          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13698          * @param {String} value The string to encode
13699          * @return {String} The encoded text
13700          */
13701         htmlEncode : function(value){
13702             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13703         },
13704
13705         /**
13706          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13707          * @param {String} value The string to decode
13708          * @return {String} The decoded text
13709          */
13710         htmlDecode : function(value){
13711             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13712         },
13713
13714         /**
13715          * Trims any whitespace from either side of a string
13716          * @param {String} value The text to trim
13717          * @return {String} The trimmed text
13718          */
13719         trim : function(value){
13720             return String(value).replace(trimRe, "");
13721         },
13722
13723         /**
13724          * Returns a substring from within an original string
13725          * @param {String} value The original text
13726          * @param {Number} start The start index of the substring
13727          * @param {Number} length The length of the substring
13728          * @return {String} The substring
13729          */
13730         substr : function(value, start, length){
13731             return String(value).substr(start, length);
13732         },
13733
13734         /**
13735          * Converts a string to all lower case letters
13736          * @param {String} value The text to convert
13737          * @return {String} The converted text
13738          */
13739         lowercase : function(value){
13740             return String(value).toLowerCase();
13741         },
13742
13743         /**
13744          * Converts a string to all upper case letters
13745          * @param {String} value The text to convert
13746          * @return {String} The converted text
13747          */
13748         uppercase : function(value){
13749             return String(value).toUpperCase();
13750         },
13751
13752         /**
13753          * Converts the first character only of a string to upper case
13754          * @param {String} value The text to convert
13755          * @return {String} The converted text
13756          */
13757         capitalize : function(value){
13758             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13759         },
13760
13761         // private
13762         call : function(value, fn){
13763             if(arguments.length > 2){
13764                 var args = Array.prototype.slice.call(arguments, 2);
13765                 args.unshift(value);
13766                  
13767                 return /** eval:var:value */  eval(fn).apply(window, args);
13768             }else{
13769                 /** eval:var:value */
13770                 return /** eval:var:value */ eval(fn).call(window, value);
13771             }
13772         },
13773
13774        
13775         /**
13776          * safer version of Math.toFixed..??/
13777          * @param {Number/String} value The numeric value to format
13778          * @param {Number/String} value Decimal places 
13779          * @return {String} The formatted currency string
13780          */
13781         toFixed : function(v, n)
13782         {
13783             // why not use to fixed - precision is buggered???
13784             if (!n) {
13785                 return Math.round(v-0);
13786             }
13787             var fact = Math.pow(10,n+1);
13788             v = (Math.round((v-0)*fact))/fact;
13789             var z = (''+fact).substring(2);
13790             if (v == Math.floor(v)) {
13791                 return Math.floor(v) + '.' + z;
13792             }
13793             
13794             // now just padd decimals..
13795             var ps = String(v).split('.');
13796             var fd = (ps[1] + z);
13797             var r = fd.substring(0,n); 
13798             var rm = fd.substring(n); 
13799             if (rm < 5) {
13800                 return ps[0] + '.' + r;
13801             }
13802             r*=1; // turn it into a number;
13803             r++;
13804             if (String(r).length != n) {
13805                 ps[0]*=1;
13806                 ps[0]++;
13807                 r = String(r).substring(1); // chop the end off.
13808             }
13809             
13810             return ps[0] + '.' + r;
13811              
13812         },
13813         
13814         /**
13815          * Format a number as US currency
13816          * @param {Number/String} value The numeric value to format
13817          * @return {String} The formatted currency string
13818          */
13819         usMoney : function(v){
13820             return '$' + Roo.util.Format.number(v);
13821         },
13822         
13823         /**
13824          * Format a number
13825          * eventually this should probably emulate php's number_format
13826          * @param {Number/String} value The numeric value to format
13827          * @param {Number} decimals number of decimal places
13828          * @param {String} delimiter for thousands (default comma)
13829          * @return {String} The formatted currency string
13830          */
13831         number : function(v, decimals, thousandsDelimiter)
13832         {
13833             // multiply and round.
13834             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13835             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13836             
13837             var mul = Math.pow(10, decimals);
13838             var zero = String(mul).substring(1);
13839             v = (Math.round((v-0)*mul))/mul;
13840             
13841             // if it's '0' number.. then
13842             
13843             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13844             v = String(v);
13845             var ps = v.split('.');
13846             var whole = ps[0];
13847             
13848             var r = /(\d+)(\d{3})/;
13849             // add comma's
13850             
13851             if(thousandsDelimiter.length != 0) {
13852                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13853             } 
13854             
13855             var sub = ps[1] ?
13856                     // has decimals..
13857                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13858                     // does not have decimals
13859                     (decimals ? ('.' + zero) : '');
13860             
13861             
13862             return whole + sub ;
13863         },
13864         
13865         /**
13866          * Parse a value into a formatted date using the specified format pattern.
13867          * @param {Mixed} value The value to format
13868          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13869          * @return {String} The formatted date string
13870          */
13871         date : function(v, format){
13872             if(!v){
13873                 return "";
13874             }
13875             if(!(v instanceof Date)){
13876                 v = new Date(Date.parse(v));
13877             }
13878             return v.dateFormat(format || Roo.util.Format.defaults.date);
13879         },
13880
13881         /**
13882          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13883          * @param {String} format Any valid date format string
13884          * @return {Function} The date formatting function
13885          */
13886         dateRenderer : function(format){
13887             return function(v){
13888                 return Roo.util.Format.date(v, format);  
13889             };
13890         },
13891
13892         // private
13893         stripTagsRE : /<\/?[^>]+>/gi,
13894         
13895         /**
13896          * Strips all HTML tags
13897          * @param {Mixed} value The text from which to strip tags
13898          * @return {String} The stripped text
13899          */
13900         stripTags : function(v){
13901             return !v ? v : String(v).replace(this.stripTagsRE, "");
13902         }
13903     };
13904 }();
13905 Roo.util.Format.defaults = {
13906     date : 'd/M/Y'
13907 };/*
13908  * Based on:
13909  * Ext JS Library 1.1.1
13910  * Copyright(c) 2006-2007, Ext JS, LLC.
13911  *
13912  * Originally Released Under LGPL - original licence link has changed is not relivant.
13913  *
13914  * Fork - LGPL
13915  * <script type="text/javascript">
13916  */
13917
13918
13919  
13920
13921 /**
13922  * @class Roo.MasterTemplate
13923  * @extends Roo.Template
13924  * Provides a template that can have child templates. The syntax is:
13925 <pre><code>
13926 var t = new Roo.MasterTemplate(
13927         '&lt;select name="{name}"&gt;',
13928                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13929         '&lt;/select&gt;'
13930 );
13931 t.add('options', {value: 'foo', text: 'bar'});
13932 // or you can add multiple child elements in one shot
13933 t.addAll('options', [
13934     {value: 'foo', text: 'bar'},
13935     {value: 'foo2', text: 'bar2'},
13936     {value: 'foo3', text: 'bar3'}
13937 ]);
13938 // then append, applying the master template values
13939 t.append('my-form', {name: 'my-select'});
13940 </code></pre>
13941 * A name attribute for the child template is not required if you have only one child
13942 * template or you want to refer to them by index.
13943  */
13944 Roo.MasterTemplate = function(){
13945     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13946     this.originalHtml = this.html;
13947     var st = {};
13948     var m, re = this.subTemplateRe;
13949     re.lastIndex = 0;
13950     var subIndex = 0;
13951     while(m = re.exec(this.html)){
13952         var name = m[1], content = m[2];
13953         st[subIndex] = {
13954             name: name,
13955             index: subIndex,
13956             buffer: [],
13957             tpl : new Roo.Template(content)
13958         };
13959         if(name){
13960             st[name] = st[subIndex];
13961         }
13962         st[subIndex].tpl.compile();
13963         st[subIndex].tpl.call = this.call.createDelegate(this);
13964         subIndex++;
13965     }
13966     this.subCount = subIndex;
13967     this.subs = st;
13968 };
13969 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13970     /**
13971     * The regular expression used to match sub templates
13972     * @type RegExp
13973     * @property
13974     */
13975     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13976
13977     /**
13978      * Applies the passed values to a child template.
13979      * @param {String/Number} name (optional) The name or index of the child template
13980      * @param {Array/Object} values The values to be applied to the template
13981      * @return {MasterTemplate} this
13982      */
13983      add : function(name, values){
13984         if(arguments.length == 1){
13985             values = arguments[0];
13986             name = 0;
13987         }
13988         var s = this.subs[name];
13989         s.buffer[s.buffer.length] = s.tpl.apply(values);
13990         return this;
13991     },
13992
13993     /**
13994      * Applies all the passed values to a child template.
13995      * @param {String/Number} name (optional) The name or index of the child template
13996      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13997      * @param {Boolean} reset (optional) True to reset the template first
13998      * @return {MasterTemplate} this
13999      */
14000     fill : function(name, values, reset){
14001         var a = arguments;
14002         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14003             values = a[0];
14004             name = 0;
14005             reset = a[1];
14006         }
14007         if(reset){
14008             this.reset();
14009         }
14010         for(var i = 0, len = values.length; i < len; i++){
14011             this.add(name, values[i]);
14012         }
14013         return this;
14014     },
14015
14016     /**
14017      * Resets the template for reuse
14018      * @return {MasterTemplate} this
14019      */
14020      reset : function(){
14021         var s = this.subs;
14022         for(var i = 0; i < this.subCount; i++){
14023             s[i].buffer = [];
14024         }
14025         return this;
14026     },
14027
14028     applyTemplate : function(values){
14029         var s = this.subs;
14030         var replaceIndex = -1;
14031         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14032             return s[++replaceIndex].buffer.join("");
14033         });
14034         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14035     },
14036
14037     apply : function(){
14038         return this.applyTemplate.apply(this, arguments);
14039     },
14040
14041     compile : function(){return this;}
14042 });
14043
14044 /**
14045  * Alias for fill().
14046  * @method
14047  */
14048 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14049  /**
14050  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14051  * var tpl = Roo.MasterTemplate.from('element-id');
14052  * @param {String/HTMLElement} el
14053  * @param {Object} config
14054  * @static
14055  */
14056 Roo.MasterTemplate.from = function(el, config){
14057     el = Roo.getDom(el);
14058     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
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  * @class Roo.util.CSS
14073  * Utility class for manipulating CSS rules
14074  * @singleton
14075  */
14076 Roo.util.CSS = function(){
14077         var rules = null;
14078         var doc = document;
14079
14080     var camelRe = /(-[a-z])/gi;
14081     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14082
14083    return {
14084    /**
14085     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14086     * tag and appended to the HEAD of the document.
14087     * @param {String|Object} cssText The text containing the css rules
14088     * @param {String} id An id to add to the stylesheet for later removal
14089     * @return {StyleSheet}
14090     */
14091     createStyleSheet : function(cssText, id){
14092         var ss;
14093         var head = doc.getElementsByTagName("head")[0];
14094         var nrules = doc.createElement("style");
14095         nrules.setAttribute("type", "text/css");
14096         if(id){
14097             nrules.setAttribute("id", id);
14098         }
14099         if (typeof(cssText) != 'string') {
14100             // support object maps..
14101             // not sure if this a good idea.. 
14102             // perhaps it should be merged with the general css handling
14103             // and handle js style props.
14104             var cssTextNew = [];
14105             for(var n in cssText) {
14106                 var citems = [];
14107                 for(var k in cssText[n]) {
14108                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14109                 }
14110                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14111                 
14112             }
14113             cssText = cssTextNew.join("\n");
14114             
14115         }
14116        
14117        
14118        if(Roo.isIE){
14119            head.appendChild(nrules);
14120            ss = nrules.styleSheet;
14121            ss.cssText = cssText;
14122        }else{
14123            try{
14124                 nrules.appendChild(doc.createTextNode(cssText));
14125            }catch(e){
14126                nrules.cssText = cssText; 
14127            }
14128            head.appendChild(nrules);
14129            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14130        }
14131        this.cacheStyleSheet(ss);
14132        return ss;
14133    },
14134
14135    /**
14136     * Removes a style or link tag by id
14137     * @param {String} id The id of the tag
14138     */
14139    removeStyleSheet : function(id){
14140        var existing = doc.getElementById(id);
14141        if(existing){
14142            existing.parentNode.removeChild(existing);
14143        }
14144    },
14145
14146    /**
14147     * Dynamically swaps an existing stylesheet reference for a new one
14148     * @param {String} id The id of an existing link tag to remove
14149     * @param {String} url The href of the new stylesheet to include
14150     */
14151    swapStyleSheet : function(id, url){
14152        this.removeStyleSheet(id);
14153        var ss = doc.createElement("link");
14154        ss.setAttribute("rel", "stylesheet");
14155        ss.setAttribute("type", "text/css");
14156        ss.setAttribute("id", id);
14157        ss.setAttribute("href", url);
14158        doc.getElementsByTagName("head")[0].appendChild(ss);
14159    },
14160    
14161    /**
14162     * Refresh the rule cache if you have dynamically added stylesheets
14163     * @return {Object} An object (hash) of rules indexed by selector
14164     */
14165    refreshCache : function(){
14166        return this.getRules(true);
14167    },
14168
14169    // private
14170    cacheStyleSheet : function(stylesheet){
14171        if(!rules){
14172            rules = {};
14173        }
14174        try{// try catch for cross domain access issue
14175            var ssRules = stylesheet.cssRules || stylesheet.rules;
14176            for(var j = ssRules.length-1; j >= 0; --j){
14177                rules[ssRules[j].selectorText] = ssRules[j];
14178            }
14179        }catch(e){}
14180    },
14181    
14182    /**
14183     * Gets all css rules for the document
14184     * @param {Boolean} refreshCache true to refresh the internal cache
14185     * @return {Object} An object (hash) of rules indexed by selector
14186     */
14187    getRules : function(refreshCache){
14188                 if(rules == null || refreshCache){
14189                         rules = {};
14190                         var ds = doc.styleSheets;
14191                         for(var i =0, len = ds.length; i < len; i++){
14192                             try{
14193                         this.cacheStyleSheet(ds[i]);
14194                     }catch(e){} 
14195                 }
14196                 }
14197                 return rules;
14198         },
14199         
14200         /**
14201     * Gets an an individual CSS rule by selector(s)
14202     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14203     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14204     * @return {CSSRule} The CSS rule or null if one is not found
14205     */
14206    getRule : function(selector, refreshCache){
14207                 var rs = this.getRules(refreshCache);
14208                 if(!(selector instanceof Array)){
14209                     return rs[selector];
14210                 }
14211                 for(var i = 0; i < selector.length; i++){
14212                         if(rs[selector[i]]){
14213                                 return rs[selector[i]];
14214                         }
14215                 }
14216                 return null;
14217         },
14218         
14219         
14220         /**
14221     * Updates a rule property
14222     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14223     * @param {String} property The css property
14224     * @param {String} value The new value for the property
14225     * @return {Boolean} true If a rule was found and updated
14226     */
14227    updateRule : function(selector, property, value){
14228                 if(!(selector instanceof Array)){
14229                         var rule = this.getRule(selector);
14230                         if(rule){
14231                                 rule.style[property.replace(camelRe, camelFn)] = value;
14232                                 return true;
14233                         }
14234                 }else{
14235                         for(var i = 0; i < selector.length; i++){
14236                                 if(this.updateRule(selector[i], property, value)){
14237                                         return true;
14238                                 }
14239                         }
14240                 }
14241                 return false;
14242         }
14243    };   
14244 }();/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255  
14256
14257 /**
14258  * @class Roo.util.ClickRepeater
14259  * @extends Roo.util.Observable
14260  * 
14261  * A wrapper class which can be applied to any element. Fires a "click" event while the
14262  * mouse is pressed. The interval between firings may be specified in the config but
14263  * defaults to 10 milliseconds.
14264  * 
14265  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14266  * 
14267  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14268  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14269  * Similar to an autorepeat key delay.
14270  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14271  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14272  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14273  *           "interval" and "delay" are ignored. "immediate" is honored.
14274  * @cfg {Boolean} preventDefault True to prevent the default click event
14275  * @cfg {Boolean} stopDefault True to stop the default click event
14276  * 
14277  * @history
14278  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14279  *     2007-02-02 jvs Renamed to ClickRepeater
14280  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14281  *
14282  *  @constructor
14283  * @param {String/HTMLElement/Element} el The element to listen on
14284  * @param {Object} config
14285  **/
14286 Roo.util.ClickRepeater = function(el, config)
14287 {
14288     this.el = Roo.get(el);
14289     this.el.unselectable();
14290
14291     Roo.apply(this, config);
14292
14293     this.addEvents({
14294     /**
14295      * @event mousedown
14296      * Fires when the mouse button is depressed.
14297      * @param {Roo.util.ClickRepeater} this
14298      */
14299         "mousedown" : true,
14300     /**
14301      * @event click
14302      * Fires on a specified interval during the time the element is pressed.
14303      * @param {Roo.util.ClickRepeater} this
14304      */
14305         "click" : true,
14306     /**
14307      * @event mouseup
14308      * Fires when the mouse key is released.
14309      * @param {Roo.util.ClickRepeater} this
14310      */
14311         "mouseup" : true
14312     });
14313
14314     this.el.on("mousedown", this.handleMouseDown, this);
14315     if(this.preventDefault || this.stopDefault){
14316         this.el.on("click", function(e){
14317             if(this.preventDefault){
14318                 e.preventDefault();
14319             }
14320             if(this.stopDefault){
14321                 e.stopEvent();
14322             }
14323         }, this);
14324     }
14325
14326     // allow inline handler
14327     if(this.handler){
14328         this.on("click", this.handler,  this.scope || this);
14329     }
14330
14331     Roo.util.ClickRepeater.superclass.constructor.call(this);
14332 };
14333
14334 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14335     interval : 20,
14336     delay: 250,
14337     preventDefault : true,
14338     stopDefault : false,
14339     timer : 0,
14340
14341     // private
14342     handleMouseDown : function(){
14343         clearTimeout(this.timer);
14344         this.el.blur();
14345         if(this.pressClass){
14346             this.el.addClass(this.pressClass);
14347         }
14348         this.mousedownTime = new Date();
14349
14350         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14351         this.el.on("mouseout", this.handleMouseOut, this);
14352
14353         this.fireEvent("mousedown", this);
14354         this.fireEvent("click", this);
14355         
14356         this.timer = this.click.defer(this.delay || this.interval, this);
14357     },
14358
14359     // private
14360     click : function(){
14361         this.fireEvent("click", this);
14362         this.timer = this.click.defer(this.getInterval(), this);
14363     },
14364
14365     // private
14366     getInterval: function(){
14367         if(!this.accelerate){
14368             return this.interval;
14369         }
14370         var pressTime = this.mousedownTime.getElapsed();
14371         if(pressTime < 500){
14372             return 400;
14373         }else if(pressTime < 1700){
14374             return 320;
14375         }else if(pressTime < 2600){
14376             return 250;
14377         }else if(pressTime < 3500){
14378             return 180;
14379         }else if(pressTime < 4400){
14380             return 140;
14381         }else if(pressTime < 5300){
14382             return 80;
14383         }else if(pressTime < 6200){
14384             return 50;
14385         }else{
14386             return 10;
14387         }
14388     },
14389
14390     // private
14391     handleMouseOut : function(){
14392         clearTimeout(this.timer);
14393         if(this.pressClass){
14394             this.el.removeClass(this.pressClass);
14395         }
14396         this.el.on("mouseover", this.handleMouseReturn, this);
14397     },
14398
14399     // private
14400     handleMouseReturn : function(){
14401         this.el.un("mouseover", this.handleMouseReturn);
14402         if(this.pressClass){
14403             this.el.addClass(this.pressClass);
14404         }
14405         this.click();
14406     },
14407
14408     // private
14409     handleMouseUp : function(){
14410         clearTimeout(this.timer);
14411         this.el.un("mouseover", this.handleMouseReturn);
14412         this.el.un("mouseout", this.handleMouseOut);
14413         Roo.get(document).un("mouseup", this.handleMouseUp);
14414         this.el.removeClass(this.pressClass);
14415         this.fireEvent("mouseup", this);
14416     }
14417 });/*
14418  * Based on:
14419  * Ext JS Library 1.1.1
14420  * Copyright(c) 2006-2007, Ext JS, LLC.
14421  *
14422  * Originally Released Under LGPL - original licence link has changed is not relivant.
14423  *
14424  * Fork - LGPL
14425  * <script type="text/javascript">
14426  */
14427
14428  
14429 /**
14430  * @class Roo.KeyNav
14431  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14432  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14433  * way to implement custom navigation schemes for any UI component.</p>
14434  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14435  * pageUp, pageDown, del, home, end.  Usage:</p>
14436  <pre><code>
14437 var nav = new Roo.KeyNav("my-element", {
14438     "left" : function(e){
14439         this.moveLeft(e.ctrlKey);
14440     },
14441     "right" : function(e){
14442         this.moveRight(e.ctrlKey);
14443     },
14444     "enter" : function(e){
14445         this.save();
14446     },
14447     scope : this
14448 });
14449 </code></pre>
14450  * @constructor
14451  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14452  * @param {Object} config The config
14453  */
14454 Roo.KeyNav = function(el, config){
14455     this.el = Roo.get(el);
14456     Roo.apply(this, config);
14457     if(!this.disabled){
14458         this.disabled = true;
14459         this.enable();
14460     }
14461 };
14462
14463 Roo.KeyNav.prototype = {
14464     /**
14465      * @cfg {Boolean} disabled
14466      * True to disable this KeyNav instance (defaults to false)
14467      */
14468     disabled : false,
14469     /**
14470      * @cfg {String} defaultEventAction
14471      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14472      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14473      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14474      */
14475     defaultEventAction: "stopEvent",
14476     /**
14477      * @cfg {Boolean} forceKeyDown
14478      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14479      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14480      * handle keydown instead of keypress.
14481      */
14482     forceKeyDown : false,
14483
14484     // private
14485     prepareEvent : function(e){
14486         var k = e.getKey();
14487         var h = this.keyToHandler[k];
14488         //if(h && this[h]){
14489         //    e.stopPropagation();
14490         //}
14491         if(Roo.isSafari && h && k >= 37 && k <= 40){
14492             e.stopEvent();
14493         }
14494     },
14495
14496     // private
14497     relay : function(e){
14498         var k = e.getKey();
14499         var h = this.keyToHandler[k];
14500         if(h && this[h]){
14501             if(this.doRelay(e, this[h], h) !== true){
14502                 e[this.defaultEventAction]();
14503             }
14504         }
14505     },
14506
14507     // private
14508     doRelay : function(e, h, hname){
14509         return h.call(this.scope || this, e);
14510     },
14511
14512     // possible handlers
14513     enter : false,
14514     left : false,
14515     right : false,
14516     up : false,
14517     down : false,
14518     tab : false,
14519     esc : false,
14520     pageUp : false,
14521     pageDown : false,
14522     del : false,
14523     home : false,
14524     end : false,
14525
14526     // quick lookup hash
14527     keyToHandler : {
14528         37 : "left",
14529         39 : "right",
14530         38 : "up",
14531         40 : "down",
14532         33 : "pageUp",
14533         34 : "pageDown",
14534         46 : "del",
14535         36 : "home",
14536         35 : "end",
14537         13 : "enter",
14538         27 : "esc",
14539         9  : "tab"
14540     },
14541
14542         /**
14543          * Enable this KeyNav
14544          */
14545         enable: function(){
14546                 if(this.disabled){
14547             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14548             // the EventObject will normalize Safari automatically
14549             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14550                 this.el.on("keydown", this.relay,  this);
14551             }else{
14552                 this.el.on("keydown", this.prepareEvent,  this);
14553                 this.el.on("keypress", this.relay,  this);
14554             }
14555                     this.disabled = false;
14556                 }
14557         },
14558
14559         /**
14560          * Disable this KeyNav
14561          */
14562         disable: function(){
14563                 if(!this.disabled){
14564                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14565                 this.el.un("keydown", this.relay);
14566             }else{
14567                 this.el.un("keydown", this.prepareEvent);
14568                 this.el.un("keypress", this.relay);
14569             }
14570                     this.disabled = true;
14571                 }
14572         }
14573 };/*
14574  * Based on:
14575  * Ext JS Library 1.1.1
14576  * Copyright(c) 2006-2007, Ext JS, LLC.
14577  *
14578  * Originally Released Under LGPL - original licence link has changed is not relivant.
14579  *
14580  * Fork - LGPL
14581  * <script type="text/javascript">
14582  */
14583
14584  
14585 /**
14586  * @class Roo.KeyMap
14587  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14588  * The constructor accepts the same config object as defined by {@link #addBinding}.
14589  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14590  * combination it will call the function with this signature (if the match is a multi-key
14591  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14592  * A KeyMap can also handle a string representation of keys.<br />
14593  * Usage:
14594  <pre><code>
14595 // map one key by key code
14596 var map = new Roo.KeyMap("my-element", {
14597     key: 13, // or Roo.EventObject.ENTER
14598     fn: myHandler,
14599     scope: myObject
14600 });
14601
14602 // map multiple keys to one action by string
14603 var map = new Roo.KeyMap("my-element", {
14604     key: "a\r\n\t",
14605     fn: myHandler,
14606     scope: myObject
14607 });
14608
14609 // map multiple keys to multiple actions by strings and array of codes
14610 var map = new Roo.KeyMap("my-element", [
14611     {
14612         key: [10,13],
14613         fn: function(){ alert("Return was pressed"); }
14614     }, {
14615         key: "abc",
14616         fn: function(){ alert('a, b or c was pressed'); }
14617     }, {
14618         key: "\t",
14619         ctrl:true,
14620         shift:true,
14621         fn: function(){ alert('Control + shift + tab was pressed.'); }
14622     }
14623 ]);
14624 </code></pre>
14625  * <b>Note: A KeyMap starts enabled</b>
14626  * @constructor
14627  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14628  * @param {Object} config The config (see {@link #addBinding})
14629  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14630  */
14631 Roo.KeyMap = function(el, config, eventName){
14632     this.el  = Roo.get(el);
14633     this.eventName = eventName || "keydown";
14634     this.bindings = [];
14635     if(config){
14636         this.addBinding(config);
14637     }
14638     this.enable();
14639 };
14640
14641 Roo.KeyMap.prototype = {
14642     /**
14643      * True to stop the event from bubbling and prevent the default browser action if the
14644      * key was handled by the KeyMap (defaults to false)
14645      * @type Boolean
14646      */
14647     stopEvent : false,
14648
14649     /**
14650      * Add a new binding to this KeyMap. The following config object properties are supported:
14651      * <pre>
14652 Property    Type             Description
14653 ----------  ---------------  ----------------------------------------------------------------------
14654 key         String/Array     A single keycode or an array of keycodes to handle
14655 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14656 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14657 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14658 fn          Function         The function to call when KeyMap finds the expected key combination
14659 scope       Object           The scope of the callback function
14660 </pre>
14661      *
14662      * Usage:
14663      * <pre><code>
14664 // Create a KeyMap
14665 var map = new Roo.KeyMap(document, {
14666     key: Roo.EventObject.ENTER,
14667     fn: handleKey,
14668     scope: this
14669 });
14670
14671 //Add a new binding to the existing KeyMap later
14672 map.addBinding({
14673     key: 'abc',
14674     shift: true,
14675     fn: handleKey,
14676     scope: this
14677 });
14678 </code></pre>
14679      * @param {Object/Array} config A single KeyMap config or an array of configs
14680      */
14681         addBinding : function(config){
14682         if(config instanceof Array){
14683             for(var i = 0, len = config.length; i < len; i++){
14684                 this.addBinding(config[i]);
14685             }
14686             return;
14687         }
14688         var keyCode = config.key,
14689             shift = config.shift, 
14690             ctrl = config.ctrl, 
14691             alt = config.alt,
14692             fn = config.fn,
14693             scope = config.scope;
14694         if(typeof keyCode == "string"){
14695             var ks = [];
14696             var keyString = keyCode.toUpperCase();
14697             for(var j = 0, len = keyString.length; j < len; j++){
14698                 ks.push(keyString.charCodeAt(j));
14699             }
14700             keyCode = ks;
14701         }
14702         var keyArray = keyCode instanceof Array;
14703         var handler = function(e){
14704             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14705                 var k = e.getKey();
14706                 if(keyArray){
14707                     for(var i = 0, len = keyCode.length; i < len; i++){
14708                         if(keyCode[i] == k){
14709                           if(this.stopEvent){
14710                               e.stopEvent();
14711                           }
14712                           fn.call(scope || window, k, e);
14713                           return;
14714                         }
14715                     }
14716                 }else{
14717                     if(k == keyCode){
14718                         if(this.stopEvent){
14719                            e.stopEvent();
14720                         }
14721                         fn.call(scope || window, k, e);
14722                     }
14723                 }
14724             }
14725         };
14726         this.bindings.push(handler);  
14727         },
14728
14729     /**
14730      * Shorthand for adding a single key listener
14731      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14732      * following options:
14733      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14734      * @param {Function} fn The function to call
14735      * @param {Object} scope (optional) The scope of the function
14736      */
14737     on : function(key, fn, scope){
14738         var keyCode, shift, ctrl, alt;
14739         if(typeof key == "object" && !(key instanceof Array)){
14740             keyCode = key.key;
14741             shift = key.shift;
14742             ctrl = key.ctrl;
14743             alt = key.alt;
14744         }else{
14745             keyCode = key;
14746         }
14747         this.addBinding({
14748             key: keyCode,
14749             shift: shift,
14750             ctrl: ctrl,
14751             alt: alt,
14752             fn: fn,
14753             scope: scope
14754         })
14755     },
14756
14757     // private
14758     handleKeyDown : function(e){
14759             if(this.enabled){ //just in case
14760             var b = this.bindings;
14761             for(var i = 0, len = b.length; i < len; i++){
14762                 b[i].call(this, e);
14763             }
14764             }
14765         },
14766         
14767         /**
14768          * Returns true if this KeyMap is enabled
14769          * @return {Boolean} 
14770          */
14771         isEnabled : function(){
14772             return this.enabled;  
14773         },
14774         
14775         /**
14776          * Enables this KeyMap
14777          */
14778         enable: function(){
14779                 if(!this.enabled){
14780                     this.el.on(this.eventName, this.handleKeyDown, this);
14781                     this.enabled = true;
14782                 }
14783         },
14784
14785         /**
14786          * Disable this KeyMap
14787          */
14788         disable: function(){
14789                 if(this.enabled){
14790                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14791                     this.enabled = false;
14792                 }
14793         }
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805  
14806 /**
14807  * @class Roo.util.TextMetrics
14808  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14809  * wide, in pixels, a given block of text will be.
14810  * @singleton
14811  */
14812 Roo.util.TextMetrics = function(){
14813     var shared;
14814     return {
14815         /**
14816          * Measures the size of the specified text
14817          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14818          * that can affect the size of the rendered text
14819          * @param {String} text The text to measure
14820          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14821          * in order to accurately measure the text height
14822          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14823          */
14824         measure : function(el, text, fixedWidth){
14825             if(!shared){
14826                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14827             }
14828             shared.bind(el);
14829             shared.setFixedWidth(fixedWidth || 'auto');
14830             return shared.getSize(text);
14831         },
14832
14833         /**
14834          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14835          * the overhead of multiple calls to initialize the style properties on each measurement.
14836          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14837          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14838          * in order to accurately measure the text height
14839          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14840          */
14841         createInstance : function(el, fixedWidth){
14842             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14843         }
14844     };
14845 }();
14846
14847  
14848
14849 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14850     var ml = new Roo.Element(document.createElement('div'));
14851     document.body.appendChild(ml.dom);
14852     ml.position('absolute');
14853     ml.setLeftTop(-1000, -1000);
14854     ml.hide();
14855
14856     if(fixedWidth){
14857         ml.setWidth(fixedWidth);
14858     }
14859      
14860     var instance = {
14861         /**
14862          * Returns the size of the specified text based on the internal element's style and width properties
14863          * @memberOf Roo.util.TextMetrics.Instance#
14864          * @param {String} text The text to measure
14865          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14866          */
14867         getSize : function(text){
14868             ml.update(text);
14869             var s = ml.getSize();
14870             ml.update('');
14871             return s;
14872         },
14873
14874         /**
14875          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14876          * that can affect the size of the rendered text
14877          * @memberOf Roo.util.TextMetrics.Instance#
14878          * @param {String/HTMLElement} el The element, dom node or id
14879          */
14880         bind : function(el){
14881             ml.setStyle(
14882                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14883             );
14884         },
14885
14886         /**
14887          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14888          * to set a fixed width in order to accurately measure the text height.
14889          * @memberOf Roo.util.TextMetrics.Instance#
14890          * @param {Number} width The width to set on the element
14891          */
14892         setFixedWidth : function(width){
14893             ml.setWidth(width);
14894         },
14895
14896         /**
14897          * Returns the measured width of the specified text
14898          * @memberOf Roo.util.TextMetrics.Instance#
14899          * @param {String} text The text to measure
14900          * @return {Number} width The width in pixels
14901          */
14902         getWidth : function(text){
14903             ml.dom.style.width = 'auto';
14904             return this.getSize(text).width;
14905         },
14906
14907         /**
14908          * Returns the measured height of the specified text.  For multiline text, be sure to call
14909          * {@link #setFixedWidth} if necessary.
14910          * @memberOf Roo.util.TextMetrics.Instance#
14911          * @param {String} text The text to measure
14912          * @return {Number} height The height in pixels
14913          */
14914         getHeight : function(text){
14915             return this.getSize(text).height;
14916         }
14917     };
14918
14919     instance.bind(bindTo);
14920
14921     return instance;
14922 };
14923
14924 // backwards compat
14925 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14926  * Based on:
14927  * Ext JS Library 1.1.1
14928  * Copyright(c) 2006-2007, Ext JS, LLC.
14929  *
14930  * Originally Released Under LGPL - original licence link has changed is not relivant.
14931  *
14932  * Fork - LGPL
14933  * <script type="text/javascript">
14934  */
14935
14936 /**
14937  * @class Roo.state.Provider
14938  * Abstract base class for state provider implementations. This class provides methods
14939  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14940  * Provider interface.
14941  */
14942 Roo.state.Provider = function(){
14943     /**
14944      * @event statechange
14945      * Fires when a state change occurs.
14946      * @param {Provider} this This state provider
14947      * @param {String} key The state key which was changed
14948      * @param {String} value The encoded value for the state
14949      */
14950     this.addEvents({
14951         "statechange": true
14952     });
14953     this.state = {};
14954     Roo.state.Provider.superclass.constructor.call(this);
14955 };
14956 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14957     /**
14958      * Returns the current value for a key
14959      * @param {String} name The key name
14960      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14961      * @return {Mixed} The state data
14962      */
14963     get : function(name, defaultValue){
14964         return typeof this.state[name] == "undefined" ?
14965             defaultValue : this.state[name];
14966     },
14967     
14968     /**
14969      * Clears a value from the state
14970      * @param {String} name The key name
14971      */
14972     clear : function(name){
14973         delete this.state[name];
14974         this.fireEvent("statechange", this, name, null);
14975     },
14976     
14977     /**
14978      * Sets the value for a key
14979      * @param {String} name The key name
14980      * @param {Mixed} value The value to set
14981      */
14982     set : function(name, value){
14983         this.state[name] = value;
14984         this.fireEvent("statechange", this, name, value);
14985     },
14986     
14987     /**
14988      * Decodes a string previously encoded with {@link #encodeValue}.
14989      * @param {String} value The value to decode
14990      * @return {Mixed} The decoded value
14991      */
14992     decodeValue : function(cookie){
14993         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14994         var matches = re.exec(unescape(cookie));
14995         if(!matches || !matches[1]) {
14996             return; // non state cookie
14997         }
14998         var type = matches[1];
14999         var v = matches[2];
15000         switch(type){
15001             case "n":
15002                 return parseFloat(v);
15003             case "d":
15004                 return new Date(Date.parse(v));
15005             case "b":
15006                 return (v == "1");
15007             case "a":
15008                 var all = [];
15009                 var values = v.split("^");
15010                 for(var i = 0, len = values.length; i < len; i++){
15011                     all.push(this.decodeValue(values[i]));
15012                 }
15013                 return all;
15014            case "o":
15015                 var all = {};
15016                 var values = v.split("^");
15017                 for(var i = 0, len = values.length; i < len; i++){
15018                     var kv = values[i].split("=");
15019                     all[kv[0]] = this.decodeValue(kv[1]);
15020                 }
15021                 return all;
15022            default:
15023                 return v;
15024         }
15025     },
15026     
15027     /**
15028      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15029      * @param {Mixed} value The value to encode
15030      * @return {String} The encoded value
15031      */
15032     encodeValue : function(v){
15033         var enc;
15034         if(typeof v == "number"){
15035             enc = "n:" + v;
15036         }else if(typeof v == "boolean"){
15037             enc = "b:" + (v ? "1" : "0");
15038         }else if(v instanceof Date){
15039             enc = "d:" + v.toGMTString();
15040         }else if(v instanceof Array){
15041             var flat = "";
15042             for(var i = 0, len = v.length; i < len; i++){
15043                 flat += this.encodeValue(v[i]);
15044                 if(i != len-1) {
15045                     flat += "^";
15046                 }
15047             }
15048             enc = "a:" + flat;
15049         }else if(typeof v == "object"){
15050             var flat = "";
15051             for(var key in v){
15052                 if(typeof v[key] != "function"){
15053                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15054                 }
15055             }
15056             enc = "o:" + flat.substring(0, flat.length-1);
15057         }else{
15058             enc = "s:" + v;
15059         }
15060         return escape(enc);        
15061     }
15062 });
15063
15064 /*
15065  * Based on:
15066  * Ext JS Library 1.1.1
15067  * Copyright(c) 2006-2007, Ext JS, LLC.
15068  *
15069  * Originally Released Under LGPL - original licence link has changed is not relivant.
15070  *
15071  * Fork - LGPL
15072  * <script type="text/javascript">
15073  */
15074 /**
15075  * @class Roo.state.Manager
15076  * This is the global state manager. By default all components that are "state aware" check this class
15077  * for state information if you don't pass them a custom state provider. In order for this class
15078  * to be useful, it must be initialized with a provider when your application initializes.
15079  <pre><code>
15080 // in your initialization function
15081 init : function(){
15082    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15083    ...
15084    // supposed you have a {@link Roo.BorderLayout}
15085    var layout = new Roo.BorderLayout(...);
15086    layout.restoreState();
15087    // or a {Roo.BasicDialog}
15088    var dialog = new Roo.BasicDialog(...);
15089    dialog.restoreState();
15090  </code></pre>
15091  * @singleton
15092  */
15093 Roo.state.Manager = function(){
15094     var provider = new Roo.state.Provider();
15095     
15096     return {
15097         /**
15098          * Configures the default state provider for your application
15099          * @param {Provider} stateProvider The state provider to set
15100          */
15101         setProvider : function(stateProvider){
15102             provider = stateProvider;
15103         },
15104         
15105         /**
15106          * Returns the current value for a key
15107          * @param {String} name The key name
15108          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15109          * @return {Mixed} The state data
15110          */
15111         get : function(key, defaultValue){
15112             return provider.get(key, defaultValue);
15113         },
15114         
15115         /**
15116          * Sets the value for a key
15117          * @param {String} name The key name
15118          * @param {Mixed} value The state data
15119          */
15120          set : function(key, value){
15121             provider.set(key, value);
15122         },
15123         
15124         /**
15125          * Clears a value from the state
15126          * @param {String} name The key name
15127          */
15128         clear : function(key){
15129             provider.clear(key);
15130         },
15131         
15132         /**
15133          * Gets the currently configured state provider
15134          * @return {Provider} The state provider
15135          */
15136         getProvider : function(){
15137             return provider;
15138         }
15139     };
15140 }();
15141 /*
15142  * Based on:
15143  * Ext JS Library 1.1.1
15144  * Copyright(c) 2006-2007, Ext JS, LLC.
15145  *
15146  * Originally Released Under LGPL - original licence link has changed is not relivant.
15147  *
15148  * Fork - LGPL
15149  * <script type="text/javascript">
15150  */
15151 /**
15152  * @class Roo.state.CookieProvider
15153  * @extends Roo.state.Provider
15154  * The default Provider implementation which saves state via cookies.
15155  * <br />Usage:
15156  <pre><code>
15157    var cp = new Roo.state.CookieProvider({
15158        path: "/cgi-bin/",
15159        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15160        domain: "roojs.com"
15161    })
15162    Roo.state.Manager.setProvider(cp);
15163  </code></pre>
15164  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15165  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15166  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15167  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15168  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15169  * domain the page is running on including the 'www' like 'www.roojs.com')
15170  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15171  * @constructor
15172  * Create a new CookieProvider
15173  * @param {Object} config The configuration object
15174  */
15175 Roo.state.CookieProvider = function(config){
15176     Roo.state.CookieProvider.superclass.constructor.call(this);
15177     this.path = "/";
15178     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15179     this.domain = null;
15180     this.secure = false;
15181     Roo.apply(this, config);
15182     this.state = this.readCookies();
15183 };
15184
15185 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15186     // private
15187     set : function(name, value){
15188         if(typeof value == "undefined" || value === null){
15189             this.clear(name);
15190             return;
15191         }
15192         this.setCookie(name, value);
15193         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15194     },
15195
15196     // private
15197     clear : function(name){
15198         this.clearCookie(name);
15199         Roo.state.CookieProvider.superclass.clear.call(this, name);
15200     },
15201
15202     // private
15203     readCookies : function(){
15204         var cookies = {};
15205         var c = document.cookie + ";";
15206         var re = /\s?(.*?)=(.*?);/g;
15207         var matches;
15208         while((matches = re.exec(c)) != null){
15209             var name = matches[1];
15210             var value = matches[2];
15211             if(name && name.substring(0,3) == "ys-"){
15212                 cookies[name.substr(3)] = this.decodeValue(value);
15213             }
15214         }
15215         return cookies;
15216     },
15217
15218     // private
15219     setCookie : function(name, value){
15220         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15221            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15222            ((this.path == null) ? "" : ("; path=" + this.path)) +
15223            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15224            ((this.secure == true) ? "; secure" : "");
15225     },
15226
15227     // private
15228     clearCookie : function(name){
15229         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15230            ((this.path == null) ? "" : ("; path=" + this.path)) +
15231            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15232            ((this.secure == true) ? "; secure" : "");
15233     }
15234 });/*
15235  * Based on:
15236  * Ext JS Library 1.1.1
15237  * Copyright(c) 2006-2007, Ext JS, LLC.
15238  *
15239  * Originally Released Under LGPL - original licence link has changed is not relivant.
15240  *
15241  * Fork - LGPL
15242  * <script type="text/javascript">
15243  */
15244  
15245
15246 /**
15247  * @class Roo.ComponentMgr
15248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15249  * @singleton
15250  */
15251 Roo.ComponentMgr = function(){
15252     var all = new Roo.util.MixedCollection();
15253
15254     return {
15255         /**
15256          * Registers a component.
15257          * @param {Roo.Component} c The component
15258          */
15259         register : function(c){
15260             all.add(c);
15261         },
15262
15263         /**
15264          * Unregisters a component.
15265          * @param {Roo.Component} c The component
15266          */
15267         unregister : function(c){
15268             all.remove(c);
15269         },
15270
15271         /**
15272          * Returns a component by id
15273          * @param {String} id The component id
15274          */
15275         get : function(id){
15276             return all.get(id);
15277         },
15278
15279         /**
15280          * Registers a function that will be called when a specified component is added to ComponentMgr
15281          * @param {String} id The component id
15282          * @param {Funtction} fn The callback function
15283          * @param {Object} scope The scope of the callback
15284          */
15285         onAvailable : function(id, fn, scope){
15286             all.on("add", function(index, o){
15287                 if(o.id == id){
15288                     fn.call(scope || o, o);
15289                     all.un("add", fn, scope);
15290                 }
15291             });
15292         }
15293     };
15294 }();/*
15295  * Based on:
15296  * Ext JS Library 1.1.1
15297  * Copyright(c) 2006-2007, Ext JS, LLC.
15298  *
15299  * Originally Released Under LGPL - original licence link has changed is not relivant.
15300  *
15301  * Fork - LGPL
15302  * <script type="text/javascript">
15303  */
15304  
15305 /**
15306  * @class Roo.Component
15307  * @extends Roo.util.Observable
15308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15312  * All visual components (widgets) that require rendering into a layout should subclass Component.
15313  * @constructor
15314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15315  * 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
15316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15317  */
15318 Roo.Component = function(config){
15319     config = config || {};
15320     if(config.tagName || config.dom || typeof config == "string"){ // element object
15321         config = {el: config, id: config.id || config};
15322     }
15323     this.initialConfig = config;
15324
15325     Roo.apply(this, config);
15326     this.addEvents({
15327         /**
15328          * @event disable
15329          * Fires after the component is disabled.
15330              * @param {Roo.Component} this
15331              */
15332         disable : true,
15333         /**
15334          * @event enable
15335          * Fires after the component is enabled.
15336              * @param {Roo.Component} this
15337              */
15338         enable : true,
15339         /**
15340          * @event beforeshow
15341          * Fires before the component is shown.  Return false to stop the show.
15342              * @param {Roo.Component} this
15343              */
15344         beforeshow : true,
15345         /**
15346          * @event show
15347          * Fires after the component is shown.
15348              * @param {Roo.Component} this
15349              */
15350         show : true,
15351         /**
15352          * @event beforehide
15353          * Fires before the component is hidden. Return false to stop the hide.
15354              * @param {Roo.Component} this
15355              */
15356         beforehide : true,
15357         /**
15358          * @event hide
15359          * Fires after the component is hidden.
15360              * @param {Roo.Component} this
15361              */
15362         hide : true,
15363         /**
15364          * @event beforerender
15365          * Fires before the component is rendered. Return false to stop the render.
15366              * @param {Roo.Component} this
15367              */
15368         beforerender : true,
15369         /**
15370          * @event render
15371          * Fires after the component is rendered.
15372              * @param {Roo.Component} this
15373              */
15374         render : true,
15375         /**
15376          * @event beforedestroy
15377          * Fires before the component is destroyed. Return false to stop the destroy.
15378              * @param {Roo.Component} this
15379              */
15380         beforedestroy : true,
15381         /**
15382          * @event destroy
15383          * Fires after the component is destroyed.
15384              * @param {Roo.Component} this
15385              */
15386         destroy : true
15387     });
15388     if(!this.id){
15389         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15390     }
15391     Roo.ComponentMgr.register(this);
15392     Roo.Component.superclass.constructor.call(this);
15393     this.initComponent();
15394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15395         this.render(this.renderTo);
15396         delete this.renderTo;
15397     }
15398 };
15399
15400 /** @private */
15401 Roo.Component.AUTO_ID = 1000;
15402
15403 Roo.extend(Roo.Component, Roo.util.Observable, {
15404     /**
15405      * @scope Roo.Component.prototype
15406      * @type {Boolean}
15407      * true if this component is hidden. Read-only.
15408      */
15409     hidden : false,
15410     /**
15411      * @type {Boolean}
15412      * true if this component is disabled. Read-only.
15413      */
15414     disabled : false,
15415     /**
15416      * @type {Boolean}
15417      * true if this component has been rendered. Read-only.
15418      */
15419     rendered : false,
15420     
15421     /** @cfg {String} disableClass
15422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15423      */
15424     disabledClass : "x-item-disabled",
15425         /** @cfg {Boolean} allowDomMove
15426          * Whether the component can move the Dom node when rendering (defaults to true).
15427          */
15428     allowDomMove : true,
15429     /** @cfg {String} hideMode (display|visibility)
15430      * How this component should hidden. Supported values are
15431      * "visibility" (css visibility), "offsets" (negative offset position) and
15432      * "display" (css display) - defaults to "display".
15433      */
15434     hideMode: 'display',
15435
15436     /** @private */
15437     ctype : "Roo.Component",
15438
15439     /**
15440      * @cfg {String} actionMode 
15441      * which property holds the element that used for  hide() / show() / disable() / enable()
15442      * default is 'el' 
15443      */
15444     actionMode : "el",
15445
15446     /** @private */
15447     getActionEl : function(){
15448         return this[this.actionMode];
15449     },
15450
15451     initComponent : Roo.emptyFn,
15452     /**
15453      * If this is a lazy rendering component, render it to its container element.
15454      * @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.
15455      */
15456     render : function(container, position){
15457         
15458         if(this.rendered){
15459             return this;
15460         }
15461         
15462         if(this.fireEvent("beforerender", this) === false){
15463             return false;
15464         }
15465         
15466         if(!container && this.el){
15467             this.el = Roo.get(this.el);
15468             container = this.el.dom.parentNode;
15469             this.allowDomMove = false;
15470         }
15471         this.container = Roo.get(container);
15472         this.rendered = true;
15473         if(position !== undefined){
15474             if(typeof position == 'number'){
15475                 position = this.container.dom.childNodes[position];
15476             }else{
15477                 position = Roo.getDom(position);
15478             }
15479         }
15480         this.onRender(this.container, position || null);
15481         if(this.cls){
15482             this.el.addClass(this.cls);
15483             delete this.cls;
15484         }
15485         if(this.style){
15486             this.el.applyStyles(this.style);
15487             delete this.style;
15488         }
15489         this.fireEvent("render", this);
15490         this.afterRender(this.container);
15491         if(this.hidden){
15492             this.hide();
15493         }
15494         if(this.disabled){
15495             this.disable();
15496         }
15497
15498         return this;
15499         
15500     },
15501
15502     /** @private */
15503     // default function is not really useful
15504     onRender : function(ct, position){
15505         if(this.el){
15506             this.el = Roo.get(this.el);
15507             if(this.allowDomMove !== false){
15508                 ct.dom.insertBefore(this.el.dom, position);
15509             }
15510         }
15511     },
15512
15513     /** @private */
15514     getAutoCreate : function(){
15515         var cfg = typeof this.autoCreate == "object" ?
15516                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15517         if(this.id && !cfg.id){
15518             cfg.id = this.id;
15519         }
15520         return cfg;
15521     },
15522
15523     /** @private */
15524     afterRender : Roo.emptyFn,
15525
15526     /**
15527      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15528      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15529      */
15530     destroy : function(){
15531         if(this.fireEvent("beforedestroy", this) !== false){
15532             this.purgeListeners();
15533             this.beforeDestroy();
15534             if(this.rendered){
15535                 this.el.removeAllListeners();
15536                 this.el.remove();
15537                 if(this.actionMode == "container"){
15538                     this.container.remove();
15539                 }
15540             }
15541             this.onDestroy();
15542             Roo.ComponentMgr.unregister(this);
15543             this.fireEvent("destroy", this);
15544         }
15545     },
15546
15547         /** @private */
15548     beforeDestroy : function(){
15549
15550     },
15551
15552         /** @private */
15553         onDestroy : function(){
15554
15555     },
15556
15557     /**
15558      * Returns the underlying {@link Roo.Element}.
15559      * @return {Roo.Element} The element
15560      */
15561     getEl : function(){
15562         return this.el;
15563     },
15564
15565     /**
15566      * Returns the id of this component.
15567      * @return {String}
15568      */
15569     getId : function(){
15570         return this.id;
15571     },
15572
15573     /**
15574      * Try to focus this component.
15575      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15576      * @return {Roo.Component} this
15577      */
15578     focus : function(selectText){
15579         if(this.rendered){
15580             this.el.focus();
15581             if(selectText === true){
15582                 this.el.dom.select();
15583             }
15584         }
15585         return this;
15586     },
15587
15588     /** @private */
15589     blur : function(){
15590         if(this.rendered){
15591             this.el.blur();
15592         }
15593         return this;
15594     },
15595
15596     /**
15597      * Disable this component.
15598      * @return {Roo.Component} this
15599      */
15600     disable : function(){
15601         if(this.rendered){
15602             this.onDisable();
15603         }
15604         this.disabled = true;
15605         this.fireEvent("disable", this);
15606         return this;
15607     },
15608
15609         // private
15610     onDisable : function(){
15611         this.getActionEl().addClass(this.disabledClass);
15612         this.el.dom.disabled = true;
15613     },
15614
15615     /**
15616      * Enable this component.
15617      * @return {Roo.Component} this
15618      */
15619     enable : function(){
15620         if(this.rendered){
15621             this.onEnable();
15622         }
15623         this.disabled = false;
15624         this.fireEvent("enable", this);
15625         return this;
15626     },
15627
15628         // private
15629     onEnable : function(){
15630         this.getActionEl().removeClass(this.disabledClass);
15631         this.el.dom.disabled = false;
15632     },
15633
15634     /**
15635      * Convenience function for setting disabled/enabled by boolean.
15636      * @param {Boolean} disabled
15637      */
15638     setDisabled : function(disabled){
15639         this[disabled ? "disable" : "enable"]();
15640     },
15641
15642     /**
15643      * Show this component.
15644      * @return {Roo.Component} this
15645      */
15646     show: function(){
15647         if(this.fireEvent("beforeshow", this) !== false){
15648             this.hidden = false;
15649             if(this.rendered){
15650                 this.onShow();
15651             }
15652             this.fireEvent("show", this);
15653         }
15654         return this;
15655     },
15656
15657     // private
15658     onShow : function(){
15659         var ae = this.getActionEl();
15660         if(this.hideMode == 'visibility'){
15661             ae.dom.style.visibility = "visible";
15662         }else if(this.hideMode == 'offsets'){
15663             ae.removeClass('x-hidden');
15664         }else{
15665             ae.dom.style.display = "";
15666         }
15667     },
15668
15669     /**
15670      * Hide this component.
15671      * @return {Roo.Component} this
15672      */
15673     hide: function(){
15674         if(this.fireEvent("beforehide", this) !== false){
15675             this.hidden = true;
15676             if(this.rendered){
15677                 this.onHide();
15678             }
15679             this.fireEvent("hide", this);
15680         }
15681         return this;
15682     },
15683
15684     // private
15685     onHide : function(){
15686         var ae = this.getActionEl();
15687         if(this.hideMode == 'visibility'){
15688             ae.dom.style.visibility = "hidden";
15689         }else if(this.hideMode == 'offsets'){
15690             ae.addClass('x-hidden');
15691         }else{
15692             ae.dom.style.display = "none";
15693         }
15694     },
15695
15696     /**
15697      * Convenience function to hide or show this component by boolean.
15698      * @param {Boolean} visible True to show, false to hide
15699      * @return {Roo.Component} this
15700      */
15701     setVisible: function(visible){
15702         if(visible) {
15703             this.show();
15704         }else{
15705             this.hide();
15706         }
15707         return this;
15708     },
15709
15710     /**
15711      * Returns true if this component is visible.
15712      */
15713     isVisible : function(){
15714         return this.getActionEl().isVisible();
15715     },
15716
15717     cloneConfig : function(overrides){
15718         overrides = overrides || {};
15719         var id = overrides.id || Roo.id();
15720         var cfg = Roo.applyIf(overrides, this.initialConfig);
15721         cfg.id = id; // prevent dup id
15722         return new this.constructor(cfg);
15723     }
15724 });/*
15725  * Based on:
15726  * Ext JS Library 1.1.1
15727  * Copyright(c) 2006-2007, Ext JS, LLC.
15728  *
15729  * Originally Released Under LGPL - original licence link has changed is not relivant.
15730  *
15731  * Fork - LGPL
15732  * <script type="text/javascript">
15733  */
15734
15735 /**
15736  * @class Roo.BoxComponent
15737  * @extends Roo.Component
15738  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15739  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15740  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15741  * layout containers.
15742  * @constructor
15743  * @param {Roo.Element/String/Object} config The configuration options.
15744  */
15745 Roo.BoxComponent = function(config){
15746     Roo.Component.call(this, config);
15747     this.addEvents({
15748         /**
15749          * @event resize
15750          * Fires after the component is resized.
15751              * @param {Roo.Component} this
15752              * @param {Number} adjWidth The box-adjusted width that was set
15753              * @param {Number} adjHeight The box-adjusted height that was set
15754              * @param {Number} rawWidth The width that was originally specified
15755              * @param {Number} rawHeight The height that was originally specified
15756              */
15757         resize : true,
15758         /**
15759          * @event move
15760          * Fires after the component is moved.
15761              * @param {Roo.Component} this
15762              * @param {Number} x The new x position
15763              * @param {Number} y The new y position
15764              */
15765         move : true
15766     });
15767 };
15768
15769 Roo.extend(Roo.BoxComponent, Roo.Component, {
15770     // private, set in afterRender to signify that the component has been rendered
15771     boxReady : false,
15772     // private, used to defer height settings to subclasses
15773     deferHeight: false,
15774     /** @cfg {Number} width
15775      * width (optional) size of component
15776      */
15777      /** @cfg {Number} height
15778      * height (optional) size of component
15779      */
15780      
15781     /**
15782      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15783      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15784      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15785      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15786      * @return {Roo.BoxComponent} this
15787      */
15788     setSize : function(w, h){
15789         // support for standard size objects
15790         if(typeof w == 'object'){
15791             h = w.height;
15792             w = w.width;
15793         }
15794         // not rendered
15795         if(!this.boxReady){
15796             this.width = w;
15797             this.height = h;
15798             return this;
15799         }
15800
15801         // prevent recalcs when not needed
15802         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15803             return this;
15804         }
15805         this.lastSize = {width: w, height: h};
15806
15807         var adj = this.adjustSize(w, h);
15808         var aw = adj.width, ah = adj.height;
15809         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15810             var rz = this.getResizeEl();
15811             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15812                 rz.setSize(aw, ah);
15813             }else if(!this.deferHeight && ah !== undefined){
15814                 rz.setHeight(ah);
15815             }else if(aw !== undefined){
15816                 rz.setWidth(aw);
15817             }
15818             this.onResize(aw, ah, w, h);
15819             this.fireEvent('resize', this, aw, ah, w, h);
15820         }
15821         return this;
15822     },
15823
15824     /**
15825      * Gets the current size of the component's underlying element.
15826      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15827      */
15828     getSize : function(){
15829         return this.el.getSize();
15830     },
15831
15832     /**
15833      * Gets the current XY position of the component's underlying element.
15834      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15835      * @return {Array} The XY position of the element (e.g., [100, 200])
15836      */
15837     getPosition : function(local){
15838         if(local === true){
15839             return [this.el.getLeft(true), this.el.getTop(true)];
15840         }
15841         return this.xy || this.el.getXY();
15842     },
15843
15844     /**
15845      * Gets the current box measurements of the component's underlying element.
15846      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15847      * @returns {Object} box An object in the format {x, y, width, height}
15848      */
15849     getBox : function(local){
15850         var s = this.el.getSize();
15851         if(local){
15852             s.x = this.el.getLeft(true);
15853             s.y = this.el.getTop(true);
15854         }else{
15855             var xy = this.xy || this.el.getXY();
15856             s.x = xy[0];
15857             s.y = xy[1];
15858         }
15859         return s;
15860     },
15861
15862     /**
15863      * Sets the current box measurements of the component's underlying element.
15864      * @param {Object} box An object in the format {x, y, width, height}
15865      * @returns {Roo.BoxComponent} this
15866      */
15867     updateBox : function(box){
15868         this.setSize(box.width, box.height);
15869         this.setPagePosition(box.x, box.y);
15870         return this;
15871     },
15872
15873     // protected
15874     getResizeEl : function(){
15875         return this.resizeEl || this.el;
15876     },
15877
15878     // protected
15879     getPositionEl : function(){
15880         return this.positionEl || this.el;
15881     },
15882
15883     /**
15884      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15885      * This method fires the move event.
15886      * @param {Number} left The new left
15887      * @param {Number} top The new top
15888      * @returns {Roo.BoxComponent} this
15889      */
15890     setPosition : function(x, y){
15891         this.x = x;
15892         this.y = y;
15893         if(!this.boxReady){
15894             return this;
15895         }
15896         var adj = this.adjustPosition(x, y);
15897         var ax = adj.x, ay = adj.y;
15898
15899         var el = this.getPositionEl();
15900         if(ax !== undefined || ay !== undefined){
15901             if(ax !== undefined && ay !== undefined){
15902                 el.setLeftTop(ax, ay);
15903             }else if(ax !== undefined){
15904                 el.setLeft(ax);
15905             }else if(ay !== undefined){
15906                 el.setTop(ay);
15907             }
15908             this.onPosition(ax, ay);
15909             this.fireEvent('move', this, ax, ay);
15910         }
15911         return this;
15912     },
15913
15914     /**
15915      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15916      * This method fires the move event.
15917      * @param {Number} x The new x position
15918      * @param {Number} y The new y position
15919      * @returns {Roo.BoxComponent} this
15920      */
15921     setPagePosition : function(x, y){
15922         this.pageX = x;
15923         this.pageY = y;
15924         if(!this.boxReady){
15925             return;
15926         }
15927         if(x === undefined || y === undefined){ // cannot translate undefined points
15928             return;
15929         }
15930         var p = this.el.translatePoints(x, y);
15931         this.setPosition(p.left, p.top);
15932         return this;
15933     },
15934
15935     // private
15936     onRender : function(ct, position){
15937         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15938         if(this.resizeEl){
15939             this.resizeEl = Roo.get(this.resizeEl);
15940         }
15941         if(this.positionEl){
15942             this.positionEl = Roo.get(this.positionEl);
15943         }
15944     },
15945
15946     // private
15947     afterRender : function(){
15948         Roo.BoxComponent.superclass.afterRender.call(this);
15949         this.boxReady = true;
15950         this.setSize(this.width, this.height);
15951         if(this.x || this.y){
15952             this.setPosition(this.x, this.y);
15953         }
15954         if(this.pageX || this.pageY){
15955             this.setPagePosition(this.pageX, this.pageY);
15956         }
15957     },
15958
15959     /**
15960      * Force the component's size to recalculate based on the underlying element's current height and width.
15961      * @returns {Roo.BoxComponent} this
15962      */
15963     syncSize : function(){
15964         delete this.lastSize;
15965         this.setSize(this.el.getWidth(), this.el.getHeight());
15966         return this;
15967     },
15968
15969     /**
15970      * Called after the component is resized, this method is empty by default but can be implemented by any
15971      * subclass that needs to perform custom logic after a resize occurs.
15972      * @param {Number} adjWidth The box-adjusted width that was set
15973      * @param {Number} adjHeight The box-adjusted height that was set
15974      * @param {Number} rawWidth The width that was originally specified
15975      * @param {Number} rawHeight The height that was originally specified
15976      */
15977     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15978
15979     },
15980
15981     /**
15982      * Called after the component is moved, this method is empty by default but can be implemented by any
15983      * subclass that needs to perform custom logic after a move occurs.
15984      * @param {Number} x The new x position
15985      * @param {Number} y The new y position
15986      */
15987     onPosition : function(x, y){
15988
15989     },
15990
15991     // private
15992     adjustSize : function(w, h){
15993         if(this.autoWidth){
15994             w = 'auto';
15995         }
15996         if(this.autoHeight){
15997             h = 'auto';
15998         }
15999         return {width : w, height: h};
16000     },
16001
16002     // private
16003     adjustPosition : function(x, y){
16004         return {x : x, y: y};
16005     }
16006 });/*
16007  * Original code for Roojs - LGPL
16008  * <script type="text/javascript">
16009  */
16010  
16011 /**
16012  * @class Roo.XComponent
16013  * A delayed Element creator...
16014  * Or a way to group chunks of interface together.
16015  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16016  *  used in conjunction with XComponent.build() it will create an instance of each element,
16017  *  then call addxtype() to build the User interface.
16018  * 
16019  * Mypart.xyx = new Roo.XComponent({
16020
16021     parent : 'Mypart.xyz', // empty == document.element.!!
16022     order : '001',
16023     name : 'xxxx'
16024     region : 'xxxx'
16025     disabled : function() {} 
16026      
16027     tree : function() { // return an tree of xtype declared components
16028         var MODULE = this;
16029         return 
16030         {
16031             xtype : 'NestedLayoutPanel',
16032             // technicall
16033         }
16034      ]
16035  *})
16036  *
16037  *
16038  * It can be used to build a big heiracy, with parent etc.
16039  * or you can just use this to render a single compoent to a dom element
16040  * MYPART.render(Roo.Element | String(id) | dom_element )
16041  *
16042  *
16043  * Usage patterns.
16044  *
16045  * Classic Roo
16046  *
16047  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16048  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16049  *
16050  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16051  *
16052  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16053  * - if mulitple topModules exist, the last one is defined as the top module.
16054  *
16055  * Embeded Roo
16056  * 
16057  * When the top level or multiple modules are to embedded into a existing HTML page,
16058  * the parent element can container '#id' of the element where the module will be drawn.
16059  *
16060  * Bootstrap Roo
16061  *
16062  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16063  * it relies more on a include mechanism, where sub modules are included into an outer page.
16064  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16065  * 
16066  * Bootstrap Roo Included elements
16067  *
16068  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16069  * hence confusing the component builder as it thinks there are multiple top level elements. 
16070  *
16071  * String Over-ride & Translations
16072  *
16073  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16074  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16075  * are needed. @see Roo.XComponent.overlayString  
16076  * 
16077  * 
16078  * 
16079  * @extends Roo.util.Observable
16080  * @constructor
16081  * @param cfg {Object} configuration of component
16082  * 
16083  */
16084 Roo.XComponent = function(cfg) {
16085     Roo.apply(this, cfg);
16086     this.addEvents({ 
16087         /**
16088              * @event built
16089              * Fires when this the componnt is built
16090              * @param {Roo.XComponent} c the component
16091              */
16092         'built' : true
16093         
16094     });
16095     this.region = this.region || 'center'; // default..
16096     Roo.XComponent.register(this);
16097     this.modules = false;
16098     this.el = false; // where the layout goes..
16099     
16100     
16101 }
16102 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16103     /**
16104      * @property el
16105      * The created element (with Roo.factory())
16106      * @type {Roo.Layout}
16107      */
16108     el  : false,
16109     
16110     /**
16111      * @property el
16112      * for BC  - use el in new code
16113      * @type {Roo.Layout}
16114      */
16115     panel : false,
16116     
16117     /**
16118      * @property layout
16119      * for BC  - use el in new code
16120      * @type {Roo.Layout}
16121      */
16122     layout : false,
16123     
16124      /**
16125      * @cfg {Function|boolean} disabled
16126      * If this module is disabled by some rule, return true from the funtion
16127      */
16128     disabled : false,
16129     
16130     /**
16131      * @cfg {String} parent 
16132      * Name of parent element which it get xtype added to..
16133      */
16134     parent: false,
16135     
16136     /**
16137      * @cfg {String} order
16138      * Used to set the order in which elements are created (usefull for multiple tabs)
16139      */
16140     
16141     order : false,
16142     /**
16143      * @cfg {String} name
16144      * String to display while loading.
16145      */
16146     name : false,
16147     /**
16148      * @cfg {String} region
16149      * Region to render component to (defaults to center)
16150      */
16151     region : 'center',
16152     
16153     /**
16154      * @cfg {Array} items
16155      * A single item array - the first element is the root of the tree..
16156      * It's done this way to stay compatible with the Xtype system...
16157      */
16158     items : false,
16159     
16160     /**
16161      * @property _tree
16162      * The method that retuns the tree of parts that make up this compoennt 
16163      * @type {function}
16164      */
16165     _tree  : false,
16166     
16167      /**
16168      * render
16169      * render element to dom or tree
16170      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16171      */
16172     
16173     render : function(el)
16174     {
16175         
16176         el = el || false;
16177         var hp = this.parent ? 1 : 0;
16178         Roo.debug &&  Roo.log(this);
16179         
16180         var tree = this._tree ? this._tree() : this.tree();
16181
16182         
16183         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16184             // if parent is a '#.....' string, then let's use that..
16185             var ename = this.parent.substr(1);
16186             this.parent = false;
16187             Roo.debug && Roo.log(ename);
16188             switch (ename) {
16189                 case 'bootstrap-body':
16190                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16191                         // this is the BorderLayout standard?
16192                        this.parent = { el : true };
16193                        break;
16194                     }
16195                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16196                         // need to insert stuff...
16197                         this.parent =  {
16198                              el : new Roo.bootstrap.layout.Border({
16199                                  el : document.body, 
16200                      
16201                                  center: {
16202                                     titlebar: false,
16203                                     autoScroll:false,
16204                                     closeOnTab: true,
16205                                     tabPosition: 'top',
16206                                       //resizeTabs: true,
16207                                     alwaysShowTabs: true,
16208                                     hideTabs: false
16209                                      //minTabWidth: 140
16210                                  }
16211                              })
16212                         
16213                          };
16214                          break;
16215                     }
16216                          
16217                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16218                         this.parent = { el :  new  Roo.bootstrap.Body() };
16219                         Roo.debug && Roo.log("setting el to doc body");
16220                          
16221                     } else {
16222                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16223                     }
16224                     break;
16225                 case 'bootstrap':
16226                     this.parent = { el : true};
16227                     // fall through
16228                 default:
16229                     el = Roo.get(ename);
16230                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16231                         this.parent = { el : true};
16232                     }
16233                     
16234                     break;
16235             }
16236                 
16237             
16238             if (!el && !this.parent) {
16239                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16240                 return;
16241             }
16242         }
16243         
16244         Roo.debug && Roo.log("EL:");
16245         Roo.debug && Roo.log(el);
16246         Roo.debug && Roo.log("this.parent.el:");
16247         Roo.debug && Roo.log(this.parent.el);
16248         
16249
16250         // altertive root elements ??? - we need a better way to indicate these.
16251         var is_alt = Roo.XComponent.is_alt ||
16252                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16253                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16254                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16255         
16256         
16257         
16258         if (!this.parent && is_alt) {
16259             //el = Roo.get(document.body);
16260             this.parent = { el : true };
16261         }
16262             
16263             
16264         
16265         if (!this.parent) {
16266             
16267             Roo.debug && Roo.log("no parent - creating one");
16268             
16269             el = el ? Roo.get(el) : false;      
16270             
16271             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16272                 
16273                 this.parent =  {
16274                     el : new Roo.bootstrap.layout.Border({
16275                         el: el || document.body,
16276                     
16277                         center: {
16278                             titlebar: false,
16279                             autoScroll:false,
16280                             closeOnTab: true,
16281                             tabPosition: 'top',
16282                              //resizeTabs: true,
16283                             alwaysShowTabs: false,
16284                             hideTabs: true,
16285                             minTabWidth: 140,
16286                             overflow: 'visible'
16287                          }
16288                      })
16289                 };
16290             } else {
16291             
16292                 // it's a top level one..
16293                 this.parent =  {
16294                     el : new Roo.BorderLayout(el || document.body, {
16295                         center: {
16296                             titlebar: false,
16297                             autoScroll:false,
16298                             closeOnTab: true,
16299                             tabPosition: 'top',
16300                              //resizeTabs: true,
16301                             alwaysShowTabs: el && hp? false :  true,
16302                             hideTabs: el || !hp ? true :  false,
16303                             minTabWidth: 140
16304                          }
16305                     })
16306                 };
16307             }
16308         }
16309         
16310         if (!this.parent.el) {
16311                 // probably an old style ctor, which has been disabled.
16312                 return;
16313
16314         }
16315                 // The 'tree' method is  '_tree now' 
16316             
16317         tree.region = tree.region || this.region;
16318         var is_body = false;
16319         if (this.parent.el === true) {
16320             // bootstrap... - body..
16321             if (el) {
16322                 tree.el = el;
16323             }
16324             this.parent.el = Roo.factory(tree);
16325             is_body = true;
16326         }
16327         
16328         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16329         this.fireEvent('built', this);
16330         
16331         this.panel = this.el;
16332         this.layout = this.panel.layout;
16333         this.parentLayout = this.parent.layout  || false;  
16334          
16335     }
16336     
16337 });
16338
16339 Roo.apply(Roo.XComponent, {
16340     /**
16341      * @property  hideProgress
16342      * true to disable the building progress bar.. usefull on single page renders.
16343      * @type Boolean
16344      */
16345     hideProgress : false,
16346     /**
16347      * @property  buildCompleted
16348      * True when the builder has completed building the interface.
16349      * @type Boolean
16350      */
16351     buildCompleted : false,
16352      
16353     /**
16354      * @property  topModule
16355      * the upper most module - uses document.element as it's constructor.
16356      * @type Object
16357      */
16358      
16359     topModule  : false,
16360       
16361     /**
16362      * @property  modules
16363      * array of modules to be created by registration system.
16364      * @type {Array} of Roo.XComponent
16365      */
16366     
16367     modules : [],
16368     /**
16369      * @property  elmodules
16370      * array of modules to be created by which use #ID 
16371      * @type {Array} of Roo.XComponent
16372      */
16373      
16374     elmodules : [],
16375
16376      /**
16377      * @property  is_alt
16378      * Is an alternative Root - normally used by bootstrap or other systems,
16379      *    where the top element in the tree can wrap 'body' 
16380      * @type {boolean}  (default false)
16381      */
16382      
16383     is_alt : false,
16384     /**
16385      * @property  build_from_html
16386      * Build elements from html - used by bootstrap HTML stuff 
16387      *    - this is cleared after build is completed
16388      * @type {boolean}    (default false)
16389      */
16390      
16391     build_from_html : false,
16392     /**
16393      * Register components to be built later.
16394      *
16395      * This solves the following issues
16396      * - Building is not done on page load, but after an authentication process has occured.
16397      * - Interface elements are registered on page load
16398      * - Parent Interface elements may not be loaded before child, so this handles that..
16399      * 
16400      *
16401      * example:
16402      * 
16403      * MyApp.register({
16404           order : '000001',
16405           module : 'Pman.Tab.projectMgr',
16406           region : 'center',
16407           parent : 'Pman.layout',
16408           disabled : false,  // or use a function..
16409         })
16410      
16411      * * @param {Object} details about module
16412      */
16413     register : function(obj) {
16414                 
16415         Roo.XComponent.event.fireEvent('register', obj);
16416         switch(typeof(obj.disabled) ) {
16417                 
16418             case 'undefined':
16419                 break;
16420             
16421             case 'function':
16422                 if ( obj.disabled() ) {
16423                         return;
16424                 }
16425                 break;
16426             
16427             default:
16428                 if (obj.disabled || obj.region == '#disabled') {
16429                         return;
16430                 }
16431                 break;
16432         }
16433                 
16434         this.modules.push(obj);
16435          
16436     },
16437     /**
16438      * convert a string to an object..
16439      * eg. 'AAA.BBB' -> finds AAA.BBB
16440
16441      */
16442     
16443     toObject : function(str)
16444     {
16445         if (!str || typeof(str) == 'object') {
16446             return str;
16447         }
16448         if (str.substring(0,1) == '#') {
16449             return str;
16450         }
16451
16452         var ar = str.split('.');
16453         var rt, o;
16454         rt = ar.shift();
16455             /** eval:var:o */
16456         try {
16457             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16458         } catch (e) {
16459             throw "Module not found : " + str;
16460         }
16461         
16462         if (o === false) {
16463             throw "Module not found : " + str;
16464         }
16465         Roo.each(ar, function(e) {
16466             if (typeof(o[e]) == 'undefined') {
16467                 throw "Module not found : " + str;
16468             }
16469             o = o[e];
16470         });
16471         
16472         return o;
16473         
16474     },
16475     
16476     
16477     /**
16478      * move modules into their correct place in the tree..
16479      * 
16480      */
16481     preBuild : function ()
16482     {
16483         var _t = this;
16484         Roo.each(this.modules , function (obj)
16485         {
16486             Roo.XComponent.event.fireEvent('beforebuild', obj);
16487             
16488             var opar = obj.parent;
16489             try { 
16490                 obj.parent = this.toObject(opar);
16491             } catch(e) {
16492                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16493                 return;
16494             }
16495             
16496             if (!obj.parent) {
16497                 Roo.debug && Roo.log("GOT top level module");
16498                 Roo.debug && Roo.log(obj);
16499                 obj.modules = new Roo.util.MixedCollection(false, 
16500                     function(o) { return o.order + '' }
16501                 );
16502                 this.topModule = obj;
16503                 return;
16504             }
16505                         // parent is a string (usually a dom element name..)
16506             if (typeof(obj.parent) == 'string') {
16507                 this.elmodules.push(obj);
16508                 return;
16509             }
16510             if (obj.parent.constructor != Roo.XComponent) {
16511                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16512             }
16513             if (!obj.parent.modules) {
16514                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16515                     function(o) { return o.order + '' }
16516                 );
16517             }
16518             if (obj.parent.disabled) {
16519                 obj.disabled = true;
16520             }
16521             obj.parent.modules.add(obj);
16522         }, this);
16523     },
16524     
16525      /**
16526      * make a list of modules to build.
16527      * @return {Array} list of modules. 
16528      */ 
16529     
16530     buildOrder : function()
16531     {
16532         var _this = this;
16533         var cmp = function(a,b) {   
16534             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16535         };
16536         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16537             throw "No top level modules to build";
16538         }
16539         
16540         // make a flat list in order of modules to build.
16541         var mods = this.topModule ? [ this.topModule ] : [];
16542                 
16543         
16544         // elmodules (is a list of DOM based modules )
16545         Roo.each(this.elmodules, function(e) {
16546             mods.push(e);
16547             if (!this.topModule &&
16548                 typeof(e.parent) == 'string' &&
16549                 e.parent.substring(0,1) == '#' &&
16550                 Roo.get(e.parent.substr(1))
16551                ) {
16552                 
16553                 _this.topModule = e;
16554             }
16555             
16556         });
16557
16558         
16559         // add modules to their parents..
16560         var addMod = function(m) {
16561             Roo.debug && Roo.log("build Order: add: " + m.name);
16562                 
16563             mods.push(m);
16564             if (m.modules && !m.disabled) {
16565                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16566                 m.modules.keySort('ASC',  cmp );
16567                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16568     
16569                 m.modules.each(addMod);
16570             } else {
16571                 Roo.debug && Roo.log("build Order: no child modules");
16572             }
16573             // not sure if this is used any more..
16574             if (m.finalize) {
16575                 m.finalize.name = m.name + " (clean up) ";
16576                 mods.push(m.finalize);
16577             }
16578             
16579         }
16580         if (this.topModule && this.topModule.modules) { 
16581             this.topModule.modules.keySort('ASC',  cmp );
16582             this.topModule.modules.each(addMod);
16583         } 
16584         return mods;
16585     },
16586     
16587      /**
16588      * Build the registered modules.
16589      * @param {Object} parent element.
16590      * @param {Function} optional method to call after module has been added.
16591      * 
16592      */ 
16593    
16594     build : function(opts) 
16595     {
16596         
16597         if (typeof(opts) != 'undefined') {
16598             Roo.apply(this,opts);
16599         }
16600         
16601         this.preBuild();
16602         var mods = this.buildOrder();
16603       
16604         //this.allmods = mods;
16605         //Roo.debug && Roo.log(mods);
16606         //return;
16607         if (!mods.length) { // should not happen
16608             throw "NO modules!!!";
16609         }
16610         
16611         
16612         var msg = "Building Interface...";
16613         // flash it up as modal - so we store the mask!?
16614         if (!this.hideProgress && Roo.MessageBox) {
16615             Roo.MessageBox.show({ title: 'loading' });
16616             Roo.MessageBox.show({
16617                title: "Please wait...",
16618                msg: msg,
16619                width:450,
16620                progress:true,
16621                buttons : false,
16622                closable:false,
16623                modal: false
16624               
16625             });
16626         }
16627         var total = mods.length;
16628         
16629         var _this = this;
16630         var progressRun = function() {
16631             if (!mods.length) {
16632                 Roo.debug && Roo.log('hide?');
16633                 if (!this.hideProgress && Roo.MessageBox) {
16634                     Roo.MessageBox.hide();
16635                 }
16636                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16637                 
16638                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16639                 
16640                 // THE END...
16641                 return false;   
16642             }
16643             
16644             var m = mods.shift();
16645             
16646             
16647             Roo.debug && Roo.log(m);
16648             // not sure if this is supported any more.. - modules that are are just function
16649             if (typeof(m) == 'function') { 
16650                 m.call(this);
16651                 return progressRun.defer(10, _this);
16652             } 
16653             
16654             
16655             msg = "Building Interface " + (total  - mods.length) + 
16656                     " of " + total + 
16657                     (m.name ? (' - ' + m.name) : '');
16658                         Roo.debug && Roo.log(msg);
16659             if (!_this.hideProgress &&  Roo.MessageBox) { 
16660                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16661             }
16662             
16663          
16664             // is the module disabled?
16665             var disabled = (typeof(m.disabled) == 'function') ?
16666                 m.disabled.call(m.module.disabled) : m.disabled;    
16667             
16668             
16669             if (disabled) {
16670                 return progressRun(); // we do not update the display!
16671             }
16672             
16673             // now build 
16674             
16675                         
16676                         
16677             m.render();
16678             // it's 10 on top level, and 1 on others??? why...
16679             return progressRun.defer(10, _this);
16680              
16681         }
16682         progressRun.defer(1, _this);
16683      
16684         
16685         
16686     },
16687     /**
16688      * Overlay a set of modified strings onto a component
16689      * This is dependant on our builder exporting the strings and 'named strings' elements.
16690      * 
16691      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16692      * @param {Object} associative array of 'named' string and it's new value.
16693      * 
16694      */
16695         overlayStrings : function( component, strings )
16696     {
16697         if (typeof(component['_named_strings']) == 'undefined') {
16698             throw "ERROR: component does not have _named_strings";
16699         }
16700         for ( var k in strings ) {
16701             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16702             if (md !== false) {
16703                 component['_strings'][md] = strings[k];
16704             } else {
16705                 Roo.log('could not find named string: ' + k + ' in');
16706                 Roo.log(component);
16707             }
16708             
16709         }
16710         
16711     },
16712     
16713         
16714         /**
16715          * Event Object.
16716          *
16717          *
16718          */
16719         event: false, 
16720     /**
16721          * wrapper for event.on - aliased later..  
16722          * Typically use to register a event handler for register:
16723          *
16724          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16725          *
16726          */
16727     on : false
16728    
16729     
16730     
16731 });
16732
16733 Roo.XComponent.event = new Roo.util.Observable({
16734                 events : { 
16735                         /**
16736                          * @event register
16737                          * Fires when an Component is registered,
16738                          * set the disable property on the Component to stop registration.
16739                          * @param {Roo.XComponent} c the component being registerd.
16740                          * 
16741                          */
16742                         'register' : true,
16743             /**
16744                          * @event beforebuild
16745                          * Fires before each Component is built
16746                          * can be used to apply permissions.
16747                          * @param {Roo.XComponent} c the component being registerd.
16748                          * 
16749                          */
16750                         'beforebuild' : true,
16751                         /**
16752                          * @event buildcomplete
16753                          * Fires on the top level element when all elements have been built
16754                          * @param {Roo.XComponent} the top level component.
16755                          */
16756                         'buildcomplete' : true
16757                         
16758                 }
16759 });
16760
16761 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16762  //
16763  /**
16764  * marked - a markdown parser
16765  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16766  * https://github.com/chjj/marked
16767  */
16768
16769
16770 /**
16771  *
16772  * Roo.Markdown - is a very crude wrapper around marked..
16773  *
16774  * usage:
16775  * 
16776  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16777  * 
16778  * Note: move the sample code to the bottom of this
16779  * file before uncommenting it.
16780  *
16781  */
16782
16783 Roo.Markdown = {};
16784 Roo.Markdown.toHtml = function(text) {
16785     
16786     var c = new Roo.Markdown.marked.setOptions({
16787             renderer: new Roo.Markdown.marked.Renderer(),
16788             gfm: true,
16789             tables: true,
16790             breaks: false,
16791             pedantic: false,
16792             sanitize: false,
16793             smartLists: true,
16794             smartypants: false
16795           });
16796     // A FEW HACKS!!?
16797     
16798     text = text.replace(/\\\n/g,' ');
16799     return Roo.Markdown.marked(text);
16800 };
16801 //
16802 // converter
16803 //
16804 // Wraps all "globals" so that the only thing
16805 // exposed is makeHtml().
16806 //
16807 (function() {
16808     
16809      /**
16810          * eval:var:escape
16811          * eval:var:unescape
16812          * eval:var:replace
16813          */
16814       
16815     /**
16816      * Helpers
16817      */
16818     
16819     var escape = function (html, encode) {
16820       return html
16821         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
16822         .replace(/</g, '&lt;')
16823         .replace(/>/g, '&gt;')
16824         .replace(/"/g, '&quot;')
16825         .replace(/'/g, '&#39;');
16826     }
16827     
16828     var unescape = function (html) {
16829         // explicitly match decimal, hex, and named HTML entities 
16830       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
16831         n = n.toLowerCase();
16832         if (n === 'colon') { return ':'; }
16833         if (n.charAt(0) === '#') {
16834           return n.charAt(1) === 'x'
16835             ? String.fromCharCode(parseInt(n.substring(2), 16))
16836             : String.fromCharCode(+n.substring(1));
16837         }
16838         return '';
16839       });
16840     }
16841     
16842     var replace = function (regex, opt) {
16843       regex = regex.source;
16844       opt = opt || '';
16845       return function self(name, val) {
16846         if (!name) { return new RegExp(regex, opt); }
16847         val = val.source || val;
16848         val = val.replace(/(^|[^\[])\^/g, '$1');
16849         regex = regex.replace(name, val);
16850         return self;
16851       };
16852     }
16853
16854
16855          /**
16856          * eval:var:noop
16857     */
16858     var noop = function () {}
16859     noop.exec = noop;
16860     
16861          /**
16862          * eval:var:merge
16863     */
16864     var merge = function (obj) {
16865       var i = 1
16866         , target
16867         , key;
16868     
16869       for (; i < arguments.length; i++) {
16870         target = arguments[i];
16871         for (key in target) {
16872           if (Object.prototype.hasOwnProperty.call(target, key)) {
16873             obj[key] = target[key];
16874           }
16875         }
16876       }
16877     
16878       return obj;
16879     }
16880     
16881     
16882     /**
16883      * Block-Level Grammar
16884      */
16885     
16886     
16887     
16888     
16889     var block = {
16890       newline: /^\n+/,
16891       code: /^( {4}[^\n]+\n*)+/,
16892       fences: noop,
16893       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16894       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16895       nptable: noop,
16896       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16897       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16898       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16899       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16900       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16901       table: noop,
16902       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16903       text: /^[^\n]+/
16904     };
16905     
16906     block.bullet = /(?:[*+-]|\d+\.)/;
16907     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16908     block.item = replace(block.item, 'gm')
16909       (/bull/g, block.bullet)
16910       ();
16911     
16912     block.list = replace(block.list)
16913       (/bull/g, block.bullet)
16914       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16915       ('def', '\\n+(?=' + block.def.source + ')')
16916       ();
16917     
16918     block.blockquote = replace(block.blockquote)
16919       ('def', block.def)
16920       ();
16921     
16922     block._tag = '(?!(?:'
16923       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16924       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16925       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16926     
16927     block.html = replace(block.html)
16928       ('comment', /<!--[\s\S]*?-->/)
16929       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16930       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16931       (/tag/g, block._tag)
16932       ();
16933     
16934     block.paragraph = replace(block.paragraph)
16935       ('hr', block.hr)
16936       ('heading', block.heading)
16937       ('lheading', block.lheading)
16938       ('blockquote', block.blockquote)
16939       ('tag', '<' + block._tag)
16940       ('def', block.def)
16941       ();
16942     
16943     /**
16944      * Normal Block Grammar
16945      */
16946     
16947     block.normal = merge({}, block);
16948     
16949     /**
16950      * GFM Block Grammar
16951      */
16952     
16953     block.gfm = merge({}, block.normal, {
16954       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16955       paragraph: /^/,
16956       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16957     });
16958     
16959     block.gfm.paragraph = replace(block.paragraph)
16960       ('(?!', '(?!'
16961         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16962         + block.list.source.replace('\\1', '\\3') + '|')
16963       ();
16964     
16965     /**
16966      * GFM + Tables Block Grammar
16967      */
16968     
16969     block.tables = merge({}, block.gfm, {
16970       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16971       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16972     });
16973     
16974     /**
16975      * Block Lexer
16976      */
16977     
16978     var Lexer = function (options) {
16979       this.tokens = [];
16980       this.tokens.links = {};
16981       this.options = options || marked.defaults;
16982       this.rules = block.normal;
16983     
16984       if (this.options.gfm) {
16985         if (this.options.tables) {
16986           this.rules = block.tables;
16987         } else {
16988           this.rules = block.gfm;
16989         }
16990       }
16991     }
16992     
16993     /**
16994      * Expose Block Rules
16995      */
16996     
16997     Lexer.rules = block;
16998     
16999     /**
17000      * Static Lex Method
17001      */
17002     
17003     Lexer.lex = function(src, options) {
17004       var lexer = new Lexer(options);
17005       return lexer.lex(src);
17006     };
17007     
17008     /**
17009      * Preprocessing
17010      */
17011     
17012     Lexer.prototype.lex = function(src) {
17013       src = src
17014         .replace(/\r\n|\r/g, '\n')
17015         .replace(/\t/g, '    ')
17016         .replace(/\u00a0/g, ' ')
17017         .replace(/\u2424/g, '\n');
17018     
17019       return this.token(src, true);
17020     };
17021     
17022     /**
17023      * Lexing
17024      */
17025     
17026     Lexer.prototype.token = function(src, top, bq) {
17027       var src = src.replace(/^ +$/gm, '')
17028         , next
17029         , loose
17030         , cap
17031         , bull
17032         , b
17033         , item
17034         , space
17035         , i
17036         , l;
17037     
17038       while (src) {
17039         // newline
17040         if (cap = this.rules.newline.exec(src)) {
17041           src = src.substring(cap[0].length);
17042           if (cap[0].length > 1) {
17043             this.tokens.push({
17044               type: 'space'
17045             });
17046           }
17047         }
17048     
17049         // code
17050         if (cap = this.rules.code.exec(src)) {
17051           src = src.substring(cap[0].length);
17052           cap = cap[0].replace(/^ {4}/gm, '');
17053           this.tokens.push({
17054             type: 'code',
17055             text: !this.options.pedantic
17056               ? cap.replace(/\n+$/, '')
17057               : cap
17058           });
17059           continue;
17060         }
17061     
17062         // fences (gfm)
17063         if (cap = this.rules.fences.exec(src)) {
17064           src = src.substring(cap[0].length);
17065           this.tokens.push({
17066             type: 'code',
17067             lang: cap[2],
17068             text: cap[3] || ''
17069           });
17070           continue;
17071         }
17072     
17073         // heading
17074         if (cap = this.rules.heading.exec(src)) {
17075           src = src.substring(cap[0].length);
17076           this.tokens.push({
17077             type: 'heading',
17078             depth: cap[1].length,
17079             text: cap[2]
17080           });
17081           continue;
17082         }
17083     
17084         // table no leading pipe (gfm)
17085         if (top && (cap = this.rules.nptable.exec(src))) {
17086           src = src.substring(cap[0].length);
17087     
17088           item = {
17089             type: 'table',
17090             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17091             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17092             cells: cap[3].replace(/\n$/, '').split('\n')
17093           };
17094     
17095           for (i = 0; i < item.align.length; i++) {
17096             if (/^ *-+: *$/.test(item.align[i])) {
17097               item.align[i] = 'right';
17098             } else if (/^ *:-+: *$/.test(item.align[i])) {
17099               item.align[i] = 'center';
17100             } else if (/^ *:-+ *$/.test(item.align[i])) {
17101               item.align[i] = 'left';
17102             } else {
17103               item.align[i] = null;
17104             }
17105           }
17106     
17107           for (i = 0; i < item.cells.length; i++) {
17108             item.cells[i] = item.cells[i].split(/ *\| */);
17109           }
17110     
17111           this.tokens.push(item);
17112     
17113           continue;
17114         }
17115     
17116         // lheading
17117         if (cap = this.rules.lheading.exec(src)) {
17118           src = src.substring(cap[0].length);
17119           this.tokens.push({
17120             type: 'heading',
17121             depth: cap[2] === '=' ? 1 : 2,
17122             text: cap[1]
17123           });
17124           continue;
17125         }
17126     
17127         // hr
17128         if (cap = this.rules.hr.exec(src)) {
17129           src = src.substring(cap[0].length);
17130           this.tokens.push({
17131             type: 'hr'
17132           });
17133           continue;
17134         }
17135     
17136         // blockquote
17137         if (cap = this.rules.blockquote.exec(src)) {
17138           src = src.substring(cap[0].length);
17139     
17140           this.tokens.push({
17141             type: 'blockquote_start'
17142           });
17143     
17144           cap = cap[0].replace(/^ *> ?/gm, '');
17145     
17146           // Pass `top` to keep the current
17147           // "toplevel" state. This is exactly
17148           // how markdown.pl works.
17149           this.token(cap, top, true);
17150     
17151           this.tokens.push({
17152             type: 'blockquote_end'
17153           });
17154     
17155           continue;
17156         }
17157     
17158         // list
17159         if (cap = this.rules.list.exec(src)) {
17160           src = src.substring(cap[0].length);
17161           bull = cap[2];
17162     
17163           this.tokens.push({
17164             type: 'list_start',
17165             ordered: bull.length > 1
17166           });
17167     
17168           // Get each top-level item.
17169           cap = cap[0].match(this.rules.item);
17170     
17171           next = false;
17172           l = cap.length;
17173           i = 0;
17174     
17175           for (; i < l; i++) {
17176             item = cap[i];
17177     
17178             // Remove the list item's bullet
17179             // so it is seen as the next token.
17180             space = item.length;
17181             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17182     
17183             // Outdent whatever the
17184             // list item contains. Hacky.
17185             if (~item.indexOf('\n ')) {
17186               space -= item.length;
17187               item = !this.options.pedantic
17188                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17189                 : item.replace(/^ {1,4}/gm, '');
17190             }
17191     
17192             // Determine whether the next list item belongs here.
17193             // Backpedal if it does not belong in this list.
17194             if (this.options.smartLists && i !== l - 1) {
17195               b = block.bullet.exec(cap[i + 1])[0];
17196               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17197                 src = cap.slice(i + 1).join('\n') + src;
17198                 i = l - 1;
17199               }
17200             }
17201     
17202             // Determine whether item is loose or not.
17203             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17204             // for discount behavior.
17205             loose = next || /\n\n(?!\s*$)/.test(item);
17206             if (i !== l - 1) {
17207               next = item.charAt(item.length - 1) === '\n';
17208               if (!loose) { loose = next; }
17209             }
17210     
17211             this.tokens.push({
17212               type: loose
17213                 ? 'loose_item_start'
17214                 : 'list_item_start'
17215             });
17216     
17217             // Recurse.
17218             this.token(item, false, bq);
17219     
17220             this.tokens.push({
17221               type: 'list_item_end'
17222             });
17223           }
17224     
17225           this.tokens.push({
17226             type: 'list_end'
17227           });
17228     
17229           continue;
17230         }
17231     
17232         // html
17233         if (cap = this.rules.html.exec(src)) {
17234           src = src.substring(cap[0].length);
17235           this.tokens.push({
17236             type: this.options.sanitize
17237               ? 'paragraph'
17238               : 'html',
17239             pre: !this.options.sanitizer
17240               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17241             text: cap[0]
17242           });
17243           continue;
17244         }
17245     
17246         // def
17247         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17248           src = src.substring(cap[0].length);
17249           this.tokens.links[cap[1].toLowerCase()] = {
17250             href: cap[2],
17251             title: cap[3]
17252           };
17253           continue;
17254         }
17255     
17256         // table (gfm)
17257         if (top && (cap = this.rules.table.exec(src))) {
17258           src = src.substring(cap[0].length);
17259     
17260           item = {
17261             type: 'table',
17262             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17263             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17264             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17265           };
17266     
17267           for (i = 0; i < item.align.length; i++) {
17268             if (/^ *-+: *$/.test(item.align[i])) {
17269               item.align[i] = 'right';
17270             } else if (/^ *:-+: *$/.test(item.align[i])) {
17271               item.align[i] = 'center';
17272             } else if (/^ *:-+ *$/.test(item.align[i])) {
17273               item.align[i] = 'left';
17274             } else {
17275               item.align[i] = null;
17276             }
17277           }
17278     
17279           for (i = 0; i < item.cells.length; i++) {
17280             item.cells[i] = item.cells[i]
17281               .replace(/^ *\| *| *\| *$/g, '')
17282               .split(/ *\| */);
17283           }
17284     
17285           this.tokens.push(item);
17286     
17287           continue;
17288         }
17289     
17290         // top-level paragraph
17291         if (top && (cap = this.rules.paragraph.exec(src))) {
17292           src = src.substring(cap[0].length);
17293           this.tokens.push({
17294             type: 'paragraph',
17295             text: cap[1].charAt(cap[1].length - 1) === '\n'
17296               ? cap[1].slice(0, -1)
17297               : cap[1]
17298           });
17299           continue;
17300         }
17301     
17302         // text
17303         if (cap = this.rules.text.exec(src)) {
17304           // Top-level should never reach here.
17305           src = src.substring(cap[0].length);
17306           this.tokens.push({
17307             type: 'text',
17308             text: cap[0]
17309           });
17310           continue;
17311         }
17312     
17313         if (src) {
17314           throw new
17315             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17316         }
17317       }
17318     
17319       return this.tokens;
17320     };
17321     
17322     /**
17323      * Inline-Level Grammar
17324      */
17325     
17326     var inline = {
17327       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17328       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17329       url: noop,
17330       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17331       link: /^!?\[(inside)\]\(href\)/,
17332       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17333       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17334       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17335       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17336       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17337       br: /^ {2,}\n(?!\s*$)/,
17338       del: noop,
17339       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17340     };
17341     
17342     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17343     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17344     
17345     inline.link = replace(inline.link)
17346       ('inside', inline._inside)
17347       ('href', inline._href)
17348       ();
17349     
17350     inline.reflink = replace(inline.reflink)
17351       ('inside', inline._inside)
17352       ();
17353     
17354     /**
17355      * Normal Inline Grammar
17356      */
17357     
17358     inline.normal = merge({}, inline);
17359     
17360     /**
17361      * Pedantic Inline Grammar
17362      */
17363     
17364     inline.pedantic = merge({}, inline.normal, {
17365       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17366       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17367     });
17368     
17369     /**
17370      * GFM Inline Grammar
17371      */
17372     
17373     inline.gfm = merge({}, inline.normal, {
17374       escape: replace(inline.escape)('])', '~|])')(),
17375       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17376       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17377       text: replace(inline.text)
17378         (']|', '~]|')
17379         ('|', '|https?://|')
17380         ()
17381     });
17382     
17383     /**
17384      * GFM + Line Breaks Inline Grammar
17385      */
17386     
17387     inline.breaks = merge({}, inline.gfm, {
17388       br: replace(inline.br)('{2,}', '*')(),
17389       text: replace(inline.gfm.text)('{2,}', '*')()
17390     });
17391     
17392     /**
17393      * Inline Lexer & Compiler
17394      */
17395     
17396     var InlineLexer  = function (links, options) {
17397       this.options = options || marked.defaults;
17398       this.links = links;
17399       this.rules = inline.normal;
17400       this.renderer = this.options.renderer || new Renderer;
17401       this.renderer.options = this.options;
17402     
17403       if (!this.links) {
17404         throw new
17405           Error('Tokens array requires a `links` property.');
17406       }
17407     
17408       if (this.options.gfm) {
17409         if (this.options.breaks) {
17410           this.rules = inline.breaks;
17411         } else {
17412           this.rules = inline.gfm;
17413         }
17414       } else if (this.options.pedantic) {
17415         this.rules = inline.pedantic;
17416       }
17417     }
17418     
17419     /**
17420      * Expose Inline Rules
17421      */
17422     
17423     InlineLexer.rules = inline;
17424     
17425     /**
17426      * Static Lexing/Compiling Method
17427      */
17428     
17429     InlineLexer.output = function(src, links, options) {
17430       var inline = new InlineLexer(links, options);
17431       return inline.output(src);
17432     };
17433     
17434     /**
17435      * Lexing/Compiling
17436      */
17437     
17438     InlineLexer.prototype.output = function(src) {
17439       var out = ''
17440         , link
17441         , text
17442         , href
17443         , cap;
17444     
17445       while (src) {
17446         // escape
17447         if (cap = this.rules.escape.exec(src)) {
17448           src = src.substring(cap[0].length);
17449           out += cap[1];
17450           continue;
17451         }
17452     
17453         // autolink
17454         if (cap = this.rules.autolink.exec(src)) {
17455           src = src.substring(cap[0].length);
17456           if (cap[2] === '@') {
17457             text = cap[1].charAt(6) === ':'
17458               ? this.mangle(cap[1].substring(7))
17459               : this.mangle(cap[1]);
17460             href = this.mangle('mailto:') + text;
17461           } else {
17462             text = escape(cap[1]);
17463             href = text;
17464           }
17465           out += this.renderer.link(href, null, text);
17466           continue;
17467         }
17468     
17469         // url (gfm)
17470         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17471           src = src.substring(cap[0].length);
17472           text = escape(cap[1]);
17473           href = text;
17474           out += this.renderer.link(href, null, text);
17475           continue;
17476         }
17477     
17478         // tag
17479         if (cap = this.rules.tag.exec(src)) {
17480           if (!this.inLink && /^<a /i.test(cap[0])) {
17481             this.inLink = true;
17482           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17483             this.inLink = false;
17484           }
17485           src = src.substring(cap[0].length);
17486           out += this.options.sanitize
17487             ? this.options.sanitizer
17488               ? this.options.sanitizer(cap[0])
17489               : escape(cap[0])
17490             : cap[0];
17491           continue;
17492         }
17493     
17494         // link
17495         if (cap = this.rules.link.exec(src)) {
17496           src = src.substring(cap[0].length);
17497           this.inLink = true;
17498           out += this.outputLink(cap, {
17499             href: cap[2],
17500             title: cap[3]
17501           });
17502           this.inLink = false;
17503           continue;
17504         }
17505     
17506         // reflink, nolink
17507         if ((cap = this.rules.reflink.exec(src))
17508             || (cap = this.rules.nolink.exec(src))) {
17509           src = src.substring(cap[0].length);
17510           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17511           link = this.links[link.toLowerCase()];
17512           if (!link || !link.href) {
17513             out += cap[0].charAt(0);
17514             src = cap[0].substring(1) + src;
17515             continue;
17516           }
17517           this.inLink = true;
17518           out += this.outputLink(cap, link);
17519           this.inLink = false;
17520           continue;
17521         }
17522     
17523         // strong
17524         if (cap = this.rules.strong.exec(src)) {
17525           src = src.substring(cap[0].length);
17526           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17527           continue;
17528         }
17529     
17530         // em
17531         if (cap = this.rules.em.exec(src)) {
17532           src = src.substring(cap[0].length);
17533           out += this.renderer.em(this.output(cap[2] || cap[1]));
17534           continue;
17535         }
17536     
17537         // code
17538         if (cap = this.rules.code.exec(src)) {
17539           src = src.substring(cap[0].length);
17540           out += this.renderer.codespan(escape(cap[2], true));
17541           continue;
17542         }
17543     
17544         // br
17545         if (cap = this.rules.br.exec(src)) {
17546           src = src.substring(cap[0].length);
17547           out += this.renderer.br();
17548           continue;
17549         }
17550     
17551         // del (gfm)
17552         if (cap = this.rules.del.exec(src)) {
17553           src = src.substring(cap[0].length);
17554           out += this.renderer.del(this.output(cap[1]));
17555           continue;
17556         }
17557     
17558         // text
17559         if (cap = this.rules.text.exec(src)) {
17560           src = src.substring(cap[0].length);
17561           out += this.renderer.text(escape(this.smartypants(cap[0])));
17562           continue;
17563         }
17564     
17565         if (src) {
17566           throw new
17567             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17568         }
17569       }
17570     
17571       return out;
17572     };
17573     
17574     /**
17575      * Compile Link
17576      */
17577     
17578     InlineLexer.prototype.outputLink = function(cap, link) {
17579       var href = escape(link.href)
17580         , title = link.title ? escape(link.title) : null;
17581     
17582       return cap[0].charAt(0) !== '!'
17583         ? this.renderer.link(href, title, this.output(cap[1]))
17584         : this.renderer.image(href, title, escape(cap[1]));
17585     };
17586     
17587     /**
17588      * Smartypants Transformations
17589      */
17590     
17591     InlineLexer.prototype.smartypants = function(text) {
17592       if (!this.options.smartypants)  { return text; }
17593       return text
17594         // em-dashes
17595         .replace(/---/g, '\u2014')
17596         // en-dashes
17597         .replace(/--/g, '\u2013')
17598         // opening singles
17599         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17600         // closing singles & apostrophes
17601         .replace(/'/g, '\u2019')
17602         // opening doubles
17603         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17604         // closing doubles
17605         .replace(/"/g, '\u201d')
17606         // ellipses
17607         .replace(/\.{3}/g, '\u2026');
17608     };
17609     
17610     /**
17611      * Mangle Links
17612      */
17613     
17614     InlineLexer.prototype.mangle = function(text) {
17615       if (!this.options.mangle) { return text; }
17616       var out = ''
17617         , l = text.length
17618         , i = 0
17619         , ch;
17620     
17621       for (; i < l; i++) {
17622         ch = text.charCodeAt(i);
17623         if (Math.random() > 0.5) {
17624           ch = 'x' + ch.toString(16);
17625         }
17626         out += '&#' + ch + ';';
17627       }
17628     
17629       return out;
17630     };
17631     
17632     /**
17633      * Renderer
17634      */
17635     
17636      /**
17637          * eval:var:Renderer
17638     */
17639     
17640     var Renderer   = function (options) {
17641       this.options = options || {};
17642     }
17643     
17644     Renderer.prototype.code = function(code, lang, escaped) {
17645       if (this.options.highlight) {
17646         var out = this.options.highlight(code, lang);
17647         if (out != null && out !== code) {
17648           escaped = true;
17649           code = out;
17650         }
17651       } else {
17652             // hack!!! - it's already escapeD?
17653             escaped = true;
17654       }
17655     
17656       if (!lang) {
17657         return '<pre><code>'
17658           + (escaped ? code : escape(code, true))
17659           + '\n</code></pre>';
17660       }
17661     
17662       return '<pre><code class="'
17663         + this.options.langPrefix
17664         + escape(lang, true)
17665         + '">'
17666         + (escaped ? code : escape(code, true))
17667         + '\n</code></pre>\n';
17668     };
17669     
17670     Renderer.prototype.blockquote = function(quote) {
17671       return '<blockquote>\n' + quote + '</blockquote>\n';
17672     };
17673     
17674     Renderer.prototype.html = function(html) {
17675       return html;
17676     };
17677     
17678     Renderer.prototype.heading = function(text, level, raw) {
17679       return '<h'
17680         + level
17681         + ' id="'
17682         + this.options.headerPrefix
17683         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17684         + '">'
17685         + text
17686         + '</h'
17687         + level
17688         + '>\n';
17689     };
17690     
17691     Renderer.prototype.hr = function() {
17692       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17693     };
17694     
17695     Renderer.prototype.list = function(body, ordered) {
17696       var type = ordered ? 'ol' : 'ul';
17697       return '<' + type + '>\n' + body + '</' + type + '>\n';
17698     };
17699     
17700     Renderer.prototype.listitem = function(text) {
17701       return '<li>' + text + '</li>\n';
17702     };
17703     
17704     Renderer.prototype.paragraph = function(text) {
17705       return '<p>' + text + '</p>\n';
17706     };
17707     
17708     Renderer.prototype.table = function(header, body) {
17709       return '<table class="table table-striped">\n'
17710         + '<thead>\n'
17711         + header
17712         + '</thead>\n'
17713         + '<tbody>\n'
17714         + body
17715         + '</tbody>\n'
17716         + '</table>\n';
17717     };
17718     
17719     Renderer.prototype.tablerow = function(content) {
17720       return '<tr>\n' + content + '</tr>\n';
17721     };
17722     
17723     Renderer.prototype.tablecell = function(content, flags) {
17724       var type = flags.header ? 'th' : 'td';
17725       var tag = flags.align
17726         ? '<' + type + ' style="text-align:' + flags.align + '">'
17727         : '<' + type + '>';
17728       return tag + content + '</' + type + '>\n';
17729     };
17730     
17731     // span level renderer
17732     Renderer.prototype.strong = function(text) {
17733       return '<strong>' + text + '</strong>';
17734     };
17735     
17736     Renderer.prototype.em = function(text) {
17737       return '<em>' + text + '</em>';
17738     };
17739     
17740     Renderer.prototype.codespan = function(text) {
17741       return '<code>' + text + '</code>';
17742     };
17743     
17744     Renderer.prototype.br = function() {
17745       return this.options.xhtml ? '<br/>' : '<br>';
17746     };
17747     
17748     Renderer.prototype.del = function(text) {
17749       return '<del>' + text + '</del>';
17750     };
17751     
17752     Renderer.prototype.link = function(href, title, text) {
17753       if (this.options.sanitize) {
17754         try {
17755           var prot = decodeURIComponent(unescape(href))
17756             .replace(/[^\w:]/g, '')
17757             .toLowerCase();
17758         } catch (e) {
17759           return '';
17760         }
17761         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17762           return '';
17763         }
17764       }
17765       var out = '<a href="' + href + '"';
17766       if (title) {
17767         out += ' title="' + title + '"';
17768       }
17769       out += '>' + text + '</a>';
17770       return out;
17771     };
17772     
17773     Renderer.prototype.image = function(href, title, text) {
17774       var out = '<img src="' + href + '" alt="' + text + '"';
17775       if (title) {
17776         out += ' title="' + title + '"';
17777       }
17778       out += this.options.xhtml ? '/>' : '>';
17779       return out;
17780     };
17781     
17782     Renderer.prototype.text = function(text) {
17783       return text;
17784     };
17785     
17786     /**
17787      * Parsing & Compiling
17788      */
17789          /**
17790          * eval:var:Parser
17791     */
17792     
17793     var Parser= function (options) {
17794       this.tokens = [];
17795       this.token = null;
17796       this.options = options || marked.defaults;
17797       this.options.renderer = this.options.renderer || new Renderer;
17798       this.renderer = this.options.renderer;
17799       this.renderer.options = this.options;
17800     }
17801     
17802     /**
17803      * Static Parse Method
17804      */
17805     
17806     Parser.parse = function(src, options, renderer) {
17807       var parser = new Parser(options, renderer);
17808       return parser.parse(src);
17809     };
17810     
17811     /**
17812      * Parse Loop
17813      */
17814     
17815     Parser.prototype.parse = function(src) {
17816       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17817       this.tokens = src.reverse();
17818     
17819       var out = '';
17820       while (this.next()) {
17821         out += this.tok();
17822       }
17823     
17824       return out;
17825     };
17826     
17827     /**
17828      * Next Token
17829      */
17830     
17831     Parser.prototype.next = function() {
17832       return this.token = this.tokens.pop();
17833     };
17834     
17835     /**
17836      * Preview Next Token
17837      */
17838     
17839     Parser.prototype.peek = function() {
17840       return this.tokens[this.tokens.length - 1] || 0;
17841     };
17842     
17843     /**
17844      * Parse Text Tokens
17845      */
17846     
17847     Parser.prototype.parseText = function() {
17848       var body = this.token.text;
17849     
17850       while (this.peek().type === 'text') {
17851         body += '\n' + this.next().text;
17852       }
17853     
17854       return this.inline.output(body);
17855     };
17856     
17857     /**
17858      * Parse Current Token
17859      */
17860     
17861     Parser.prototype.tok = function() {
17862       switch (this.token.type) {
17863         case 'space': {
17864           return '';
17865         }
17866         case 'hr': {
17867           return this.renderer.hr();
17868         }
17869         case 'heading': {
17870           return this.renderer.heading(
17871             this.inline.output(this.token.text),
17872             this.token.depth,
17873             this.token.text);
17874         }
17875         case 'code': {
17876           return this.renderer.code(this.token.text,
17877             this.token.lang,
17878             this.token.escaped);
17879         }
17880         case 'table': {
17881           var header = ''
17882             , body = ''
17883             , i
17884             , row
17885             , cell
17886             , flags
17887             , j;
17888     
17889           // header
17890           cell = '';
17891           for (i = 0; i < this.token.header.length; i++) {
17892             flags = { header: true, align: this.token.align[i] };
17893             cell += this.renderer.tablecell(
17894               this.inline.output(this.token.header[i]),
17895               { header: true, align: this.token.align[i] }
17896             );
17897           }
17898           header += this.renderer.tablerow(cell);
17899     
17900           for (i = 0; i < this.token.cells.length; i++) {
17901             row = this.token.cells[i];
17902     
17903             cell = '';
17904             for (j = 0; j < row.length; j++) {
17905               cell += this.renderer.tablecell(
17906                 this.inline.output(row[j]),
17907                 { header: false, align: this.token.align[j] }
17908               );
17909             }
17910     
17911             body += this.renderer.tablerow(cell);
17912           }
17913           return this.renderer.table(header, body);
17914         }
17915         case 'blockquote_start': {
17916           var body = '';
17917     
17918           while (this.next().type !== 'blockquote_end') {
17919             body += this.tok();
17920           }
17921     
17922           return this.renderer.blockquote(body);
17923         }
17924         case 'list_start': {
17925           var body = ''
17926             , ordered = this.token.ordered;
17927     
17928           while (this.next().type !== 'list_end') {
17929             body += this.tok();
17930           }
17931     
17932           return this.renderer.list(body, ordered);
17933         }
17934         case 'list_item_start': {
17935           var body = '';
17936     
17937           while (this.next().type !== 'list_item_end') {
17938             body += this.token.type === 'text'
17939               ? this.parseText()
17940               : this.tok();
17941           }
17942     
17943           return this.renderer.listitem(body);
17944         }
17945         case 'loose_item_start': {
17946           var body = '';
17947     
17948           while (this.next().type !== 'list_item_end') {
17949             body += this.tok();
17950           }
17951     
17952           return this.renderer.listitem(body);
17953         }
17954         case 'html': {
17955           var html = !this.token.pre && !this.options.pedantic
17956             ? this.inline.output(this.token.text)
17957             : this.token.text;
17958           return this.renderer.html(html);
17959         }
17960         case 'paragraph': {
17961           return this.renderer.paragraph(this.inline.output(this.token.text));
17962         }
17963         case 'text': {
17964           return this.renderer.paragraph(this.parseText());
17965         }
17966       }
17967     };
17968   
17969     
17970     /**
17971      * Marked
17972      */
17973          /**
17974          * eval:var:marked
17975     */
17976     var marked = function (src, opt, callback) {
17977       if (callback || typeof opt === 'function') {
17978         if (!callback) {
17979           callback = opt;
17980           opt = null;
17981         }
17982     
17983         opt = merge({}, marked.defaults, opt || {});
17984     
17985         var highlight = opt.highlight
17986           , tokens
17987           , pending
17988           , i = 0;
17989     
17990         try {
17991           tokens = Lexer.lex(src, opt)
17992         } catch (e) {
17993           return callback(e);
17994         }
17995     
17996         pending = tokens.length;
17997          /**
17998          * eval:var:done
17999     */
18000         var done = function(err) {
18001           if (err) {
18002             opt.highlight = highlight;
18003             return callback(err);
18004           }
18005     
18006           var out;
18007     
18008           try {
18009             out = Parser.parse(tokens, opt);
18010           } catch (e) {
18011             err = e;
18012           }
18013     
18014           opt.highlight = highlight;
18015     
18016           return err
18017             ? callback(err)
18018             : callback(null, out);
18019         };
18020     
18021         if (!highlight || highlight.length < 3) {
18022           return done();
18023         }
18024     
18025         delete opt.highlight;
18026     
18027         if (!pending) { return done(); }
18028     
18029         for (; i < tokens.length; i++) {
18030           (function(token) {
18031             if (token.type !== 'code') {
18032               return --pending || done();
18033             }
18034             return highlight(token.text, token.lang, function(err, code) {
18035               if (err) { return done(err); }
18036               if (code == null || code === token.text) {
18037                 return --pending || done();
18038               }
18039               token.text = code;
18040               token.escaped = true;
18041               --pending || done();
18042             });
18043           })(tokens[i]);
18044         }
18045     
18046         return;
18047       }
18048       try {
18049         if (opt) { opt = merge({}, marked.defaults, opt); }
18050         return Parser.parse(Lexer.lex(src, opt), opt);
18051       } catch (e) {
18052         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18053         if ((opt || marked.defaults).silent) {
18054           return '<p>An error occured:</p><pre>'
18055             + escape(e.message + '', true)
18056             + '</pre>';
18057         }
18058         throw e;
18059       }
18060     }
18061     
18062     /**
18063      * Options
18064      */
18065     
18066     marked.options =
18067     marked.setOptions = function(opt) {
18068       merge(marked.defaults, opt);
18069       return marked;
18070     };
18071     
18072     marked.defaults = {
18073       gfm: true,
18074       tables: true,
18075       breaks: false,
18076       pedantic: false,
18077       sanitize: false,
18078       sanitizer: null,
18079       mangle: true,
18080       smartLists: false,
18081       silent: false,
18082       highlight: null,
18083       langPrefix: 'lang-',
18084       smartypants: false,
18085       headerPrefix: '',
18086       renderer: new Renderer,
18087       xhtml: false
18088     };
18089     
18090     /**
18091      * Expose
18092      */
18093     
18094     marked.Parser = Parser;
18095     marked.parser = Parser.parse;
18096     
18097     marked.Renderer = Renderer;
18098     
18099     marked.Lexer = Lexer;
18100     marked.lexer = Lexer.lex;
18101     
18102     marked.InlineLexer = InlineLexer;
18103     marked.inlineLexer = InlineLexer.output;
18104     
18105     marked.parse = marked;
18106     
18107     Roo.Markdown.marked = marked;
18108
18109 })();/*
18110  * Based on:
18111  * Ext JS Library 1.1.1
18112  * Copyright(c) 2006-2007, Ext JS, LLC.
18113  *
18114  * Originally Released Under LGPL - original licence link has changed is not relivant.
18115  *
18116  * Fork - LGPL
18117  * <script type="text/javascript">
18118  */
18119
18120
18121
18122 /*
18123  * These classes are derivatives of the similarly named classes in the YUI Library.
18124  * The original license:
18125  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18126  * Code licensed under the BSD License:
18127  * http://developer.yahoo.net/yui/license.txt
18128  */
18129
18130 (function() {
18131
18132 var Event=Roo.EventManager;
18133 var Dom=Roo.lib.Dom;
18134
18135 /**
18136  * @class Roo.dd.DragDrop
18137  * @extends Roo.util.Observable
18138  * Defines the interface and base operation of items that that can be
18139  * dragged or can be drop targets.  It was designed to be extended, overriding
18140  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18141  * Up to three html elements can be associated with a DragDrop instance:
18142  * <ul>
18143  * <li>linked element: the element that is passed into the constructor.
18144  * This is the element which defines the boundaries for interaction with
18145  * other DragDrop objects.</li>
18146  * <li>handle element(s): The drag operation only occurs if the element that
18147  * was clicked matches a handle element.  By default this is the linked
18148  * element, but there are times that you will want only a portion of the
18149  * linked element to initiate the drag operation, and the setHandleElId()
18150  * method provides a way to define this.</li>
18151  * <li>drag element: this represents the element that would be moved along
18152  * with the cursor during a drag operation.  By default, this is the linked
18153  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18154  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18155  * </li>
18156  * </ul>
18157  * This class should not be instantiated until the onload event to ensure that
18158  * the associated elements are available.
18159  * The following would define a DragDrop obj that would interact with any
18160  * other DragDrop obj in the "group1" group:
18161  * <pre>
18162  *  dd = new Roo.dd.DragDrop("div1", "group1");
18163  * </pre>
18164  * Since none of the event handlers have been implemented, nothing would
18165  * actually happen if you were to run the code above.  Normally you would
18166  * override this class or one of the default implementations, but you can
18167  * also override the methods you want on an instance of the class...
18168  * <pre>
18169  *  dd.onDragDrop = function(e, id) {
18170  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18171  *  }
18172  * </pre>
18173  * @constructor
18174  * @param {String} id of the element that is linked to this instance
18175  * @param {String} sGroup the group of related DragDrop objects
18176  * @param {object} config an object containing configurable attributes
18177  *                Valid properties for DragDrop:
18178  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18179  */
18180 Roo.dd.DragDrop = function(id, sGroup, config) {
18181     if (id) {
18182         this.init(id, sGroup, config);
18183     }
18184     
18185 };
18186
18187 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18188
18189     /**
18190      * The id of the element associated with this object.  This is what we
18191      * refer to as the "linked element" because the size and position of
18192      * this element is used to determine when the drag and drop objects have
18193      * interacted.
18194      * @property id
18195      * @type String
18196      */
18197     id: null,
18198
18199     /**
18200      * Configuration attributes passed into the constructor
18201      * @property config
18202      * @type object
18203      */
18204     config: null,
18205
18206     /**
18207      * The id of the element that will be dragged.  By default this is same
18208      * as the linked element , but could be changed to another element. Ex:
18209      * Roo.dd.DDProxy
18210      * @property dragElId
18211      * @type String
18212      * @private
18213      */
18214     dragElId: null,
18215
18216     /**
18217      * the id of the element that initiates the drag operation.  By default
18218      * this is the linked element, but could be changed to be a child of this
18219      * element.  This lets us do things like only starting the drag when the
18220      * header element within the linked html element is clicked.
18221      * @property handleElId
18222      * @type String
18223      * @private
18224      */
18225     handleElId: null,
18226
18227     /**
18228      * An associative array of HTML tags that will be ignored if clicked.
18229      * @property invalidHandleTypes
18230      * @type {string: string}
18231      */
18232     invalidHandleTypes: null,
18233
18234     /**
18235      * An associative array of ids for elements that will be ignored if clicked
18236      * @property invalidHandleIds
18237      * @type {string: string}
18238      */
18239     invalidHandleIds: null,
18240
18241     /**
18242      * An indexted array of css class names for elements that will be ignored
18243      * if clicked.
18244      * @property invalidHandleClasses
18245      * @type string[]
18246      */
18247     invalidHandleClasses: null,
18248
18249     /**
18250      * The linked element's absolute X position at the time the drag was
18251      * started
18252      * @property startPageX
18253      * @type int
18254      * @private
18255      */
18256     startPageX: 0,
18257
18258     /**
18259      * The linked element's absolute X position at the time the drag was
18260      * started
18261      * @property startPageY
18262      * @type int
18263      * @private
18264      */
18265     startPageY: 0,
18266
18267     /**
18268      * The group defines a logical collection of DragDrop objects that are
18269      * related.  Instances only get events when interacting with other
18270      * DragDrop object in the same group.  This lets us define multiple
18271      * groups using a single DragDrop subclass if we want.
18272      * @property groups
18273      * @type {string: string}
18274      */
18275     groups: null,
18276
18277     /**
18278      * Individual drag/drop instances can be locked.  This will prevent
18279      * onmousedown start drag.
18280      * @property locked
18281      * @type boolean
18282      * @private
18283      */
18284     locked: false,
18285
18286     /**
18287      * Lock this instance
18288      * @method lock
18289      */
18290     lock: function() { this.locked = true; },
18291
18292     /**
18293      * Unlock this instace
18294      * @method unlock
18295      */
18296     unlock: function() { this.locked = false; },
18297
18298     /**
18299      * By default, all insances can be a drop target.  This can be disabled by
18300      * setting isTarget to false.
18301      * @method isTarget
18302      * @type boolean
18303      */
18304     isTarget: true,
18305
18306     /**
18307      * The padding configured for this drag and drop object for calculating
18308      * the drop zone intersection with this object.
18309      * @method padding
18310      * @type int[]
18311      */
18312     padding: null,
18313
18314     /**
18315      * Cached reference to the linked element
18316      * @property _domRef
18317      * @private
18318      */
18319     _domRef: null,
18320
18321     /**
18322      * Internal typeof flag
18323      * @property __ygDragDrop
18324      * @private
18325      */
18326     __ygDragDrop: true,
18327
18328     /**
18329      * Set to true when horizontal contraints are applied
18330      * @property constrainX
18331      * @type boolean
18332      * @private
18333      */
18334     constrainX: false,
18335
18336     /**
18337      * Set to true when vertical contraints are applied
18338      * @property constrainY
18339      * @type boolean
18340      * @private
18341      */
18342     constrainY: false,
18343
18344     /**
18345      * The left constraint
18346      * @property minX
18347      * @type int
18348      * @private
18349      */
18350     minX: 0,
18351
18352     /**
18353      * The right constraint
18354      * @property maxX
18355      * @type int
18356      * @private
18357      */
18358     maxX: 0,
18359
18360     /**
18361      * The up constraint
18362      * @property minY
18363      * @type int
18364      * @type int
18365      * @private
18366      */
18367     minY: 0,
18368
18369     /**
18370      * The down constraint
18371      * @property maxY
18372      * @type int
18373      * @private
18374      */
18375     maxY: 0,
18376
18377     /**
18378      * Maintain offsets when we resetconstraints.  Set to true when you want
18379      * the position of the element relative to its parent to stay the same
18380      * when the page changes
18381      *
18382      * @property maintainOffset
18383      * @type boolean
18384      */
18385     maintainOffset: false,
18386
18387     /**
18388      * Array of pixel locations the element will snap to if we specified a
18389      * horizontal graduation/interval.  This array is generated automatically
18390      * when you define a tick interval.
18391      * @property xTicks
18392      * @type int[]
18393      */
18394     xTicks: null,
18395
18396     /**
18397      * Array of pixel locations the element will snap to if we specified a
18398      * vertical graduation/interval.  This array is generated automatically
18399      * when you define a tick interval.
18400      * @property yTicks
18401      * @type int[]
18402      */
18403     yTicks: null,
18404
18405     /**
18406      * By default the drag and drop instance will only respond to the primary
18407      * button click (left button for a right-handed mouse).  Set to true to
18408      * allow drag and drop to start with any mouse click that is propogated
18409      * by the browser
18410      * @property primaryButtonOnly
18411      * @type boolean
18412      */
18413     primaryButtonOnly: true,
18414
18415     /**
18416      * The availabe property is false until the linked dom element is accessible.
18417      * @property available
18418      * @type boolean
18419      */
18420     available: false,
18421
18422     /**
18423      * By default, drags can only be initiated if the mousedown occurs in the
18424      * region the linked element is.  This is done in part to work around a
18425      * bug in some browsers that mis-report the mousedown if the previous
18426      * mouseup happened outside of the window.  This property is set to true
18427      * if outer handles are defined.
18428      *
18429      * @property hasOuterHandles
18430      * @type boolean
18431      * @default false
18432      */
18433     hasOuterHandles: false,
18434
18435     /**
18436      * Code that executes immediately before the startDrag event
18437      * @method b4StartDrag
18438      * @private
18439      */
18440     b4StartDrag: function(x, y) { },
18441
18442     /**
18443      * Abstract method called after a drag/drop object is clicked
18444      * and the drag or mousedown time thresholds have beeen met.
18445      * @method startDrag
18446      * @param {int} X click location
18447      * @param {int} Y click location
18448      */
18449     startDrag: function(x, y) { /* override this */ },
18450
18451     /**
18452      * Code that executes immediately before the onDrag event
18453      * @method b4Drag
18454      * @private
18455      */
18456     b4Drag: function(e) { },
18457
18458     /**
18459      * Abstract method called during the onMouseMove event while dragging an
18460      * object.
18461      * @method onDrag
18462      * @param {Event} e the mousemove event
18463      */
18464     onDrag: function(e) { /* override this */ },
18465
18466     /**
18467      * Abstract method called when this element fist begins hovering over
18468      * another DragDrop obj
18469      * @method onDragEnter
18470      * @param {Event} e the mousemove event
18471      * @param {String|DragDrop[]} id In POINT mode, the element
18472      * id this is hovering over.  In INTERSECT mode, an array of one or more
18473      * dragdrop items being hovered over.
18474      */
18475     onDragEnter: function(e, id) { /* override this */ },
18476
18477     /**
18478      * Code that executes immediately before the onDragOver event
18479      * @method b4DragOver
18480      * @private
18481      */
18482     b4DragOver: function(e) { },
18483
18484     /**
18485      * Abstract method called when this element is hovering over another
18486      * DragDrop obj
18487      * @method onDragOver
18488      * @param {Event} e the mousemove event
18489      * @param {String|DragDrop[]} id In POINT mode, the element
18490      * id this is hovering over.  In INTERSECT mode, an array of dd items
18491      * being hovered over.
18492      */
18493     onDragOver: function(e, id) { /* override this */ },
18494
18495     /**
18496      * Code that executes immediately before the onDragOut event
18497      * @method b4DragOut
18498      * @private
18499      */
18500     b4DragOut: function(e) { },
18501
18502     /**
18503      * Abstract method called when we are no longer hovering over an element
18504      * @method onDragOut
18505      * @param {Event} e the mousemove event
18506      * @param {String|DragDrop[]} id In POINT mode, the element
18507      * id this was hovering over.  In INTERSECT mode, an array of dd items
18508      * that the mouse is no longer over.
18509      */
18510     onDragOut: function(e, id) { /* override this */ },
18511
18512     /**
18513      * Code that executes immediately before the onDragDrop event
18514      * @method b4DragDrop
18515      * @private
18516      */
18517     b4DragDrop: function(e) { },
18518
18519     /**
18520      * Abstract method called when this item is dropped on another DragDrop
18521      * obj
18522      * @method onDragDrop
18523      * @param {Event} e the mouseup event
18524      * @param {String|DragDrop[]} id In POINT mode, the element
18525      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18526      * was dropped on.
18527      */
18528     onDragDrop: function(e, id) { /* override this */ },
18529
18530     /**
18531      * Abstract method called when this item is dropped on an area with no
18532      * drop target
18533      * @method onInvalidDrop
18534      * @param {Event} e the mouseup event
18535      */
18536     onInvalidDrop: function(e) { /* override this */ },
18537
18538     /**
18539      * Code that executes immediately before the endDrag event
18540      * @method b4EndDrag
18541      * @private
18542      */
18543     b4EndDrag: function(e) { },
18544
18545     /**
18546      * Fired when we are done dragging the object
18547      * @method endDrag
18548      * @param {Event} e the mouseup event
18549      */
18550     endDrag: function(e) { /* override this */ },
18551
18552     /**
18553      * Code executed immediately before the onMouseDown event
18554      * @method b4MouseDown
18555      * @param {Event} e the mousedown event
18556      * @private
18557      */
18558     b4MouseDown: function(e) {  },
18559
18560     /**
18561      * Event handler that fires when a drag/drop obj gets a mousedown
18562      * @method onMouseDown
18563      * @param {Event} e the mousedown event
18564      */
18565     onMouseDown: function(e) { /* override this */ },
18566
18567     /**
18568      * Event handler that fires when a drag/drop obj gets a mouseup
18569      * @method onMouseUp
18570      * @param {Event} e the mouseup event
18571      */
18572     onMouseUp: function(e) { /* override this */ },
18573
18574     /**
18575      * Override the onAvailable method to do what is needed after the initial
18576      * position was determined.
18577      * @method onAvailable
18578      */
18579     onAvailable: function () {
18580     },
18581
18582     /*
18583      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18584      * @type Object
18585      */
18586     defaultPadding : {left:0, right:0, top:0, bottom:0},
18587
18588     /*
18589      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18590  *
18591  * Usage:
18592  <pre><code>
18593  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18594                 { dragElId: "existingProxyDiv" });
18595  dd.startDrag = function(){
18596      this.constrainTo("parent-id");
18597  };
18598  </code></pre>
18599  * Or you can initalize it using the {@link Roo.Element} object:
18600  <pre><code>
18601  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18602      startDrag : function(){
18603          this.constrainTo("parent-id");
18604      }
18605  });
18606  </code></pre>
18607      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18608      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18609      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18610      * an object containing the sides to pad. For example: {right:10, bottom:10}
18611      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18612      */
18613     constrainTo : function(constrainTo, pad, inContent){
18614         if(typeof pad == "number"){
18615             pad = {left: pad, right:pad, top:pad, bottom:pad};
18616         }
18617         pad = pad || this.defaultPadding;
18618         var b = Roo.get(this.getEl()).getBox();
18619         var ce = Roo.get(constrainTo);
18620         var s = ce.getScroll();
18621         var c, cd = ce.dom;
18622         if(cd == document.body){
18623             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18624         }else{
18625             xy = ce.getXY();
18626             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18627         }
18628
18629
18630         var topSpace = b.y - c.y;
18631         var leftSpace = b.x - c.x;
18632
18633         this.resetConstraints();
18634         this.setXConstraint(leftSpace - (pad.left||0), // left
18635                 c.width - leftSpace - b.width - (pad.right||0) //right
18636         );
18637         this.setYConstraint(topSpace - (pad.top||0), //top
18638                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18639         );
18640     },
18641
18642     /**
18643      * Returns a reference to the linked element
18644      * @method getEl
18645      * @return {HTMLElement} the html element
18646      */
18647     getEl: function() {
18648         if (!this._domRef) {
18649             this._domRef = Roo.getDom(this.id);
18650         }
18651
18652         return this._domRef;
18653     },
18654
18655     /**
18656      * Returns a reference to the actual element to drag.  By default this is
18657      * the same as the html element, but it can be assigned to another
18658      * element. An example of this can be found in Roo.dd.DDProxy
18659      * @method getDragEl
18660      * @return {HTMLElement} the html element
18661      */
18662     getDragEl: function() {
18663         return Roo.getDom(this.dragElId);
18664     },
18665
18666     /**
18667      * Sets up the DragDrop object.  Must be called in the constructor of any
18668      * Roo.dd.DragDrop subclass
18669      * @method init
18670      * @param id the id of the linked element
18671      * @param {String} sGroup the group of related items
18672      * @param {object} config configuration attributes
18673      */
18674     init: function(id, sGroup, config) {
18675         this.initTarget(id, sGroup, config);
18676         if (!Roo.isTouch) {
18677             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18678         }
18679         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18680         // Event.on(this.id, "selectstart", Event.preventDefault);
18681     },
18682
18683     /**
18684      * Initializes Targeting functionality only... the object does not
18685      * get a mousedown handler.
18686      * @method initTarget
18687      * @param id the id of the linked element
18688      * @param {String} sGroup the group of related items
18689      * @param {object} config configuration attributes
18690      */
18691     initTarget: function(id, sGroup, config) {
18692
18693         // configuration attributes
18694         this.config = config || {};
18695
18696         // create a local reference to the drag and drop manager
18697         this.DDM = Roo.dd.DDM;
18698         // initialize the groups array
18699         this.groups = {};
18700
18701         // assume that we have an element reference instead of an id if the
18702         // parameter is not a string
18703         if (typeof id !== "string") {
18704             id = Roo.id(id);
18705         }
18706
18707         // set the id
18708         this.id = id;
18709
18710         // add to an interaction group
18711         this.addToGroup((sGroup) ? sGroup : "default");
18712
18713         // We don't want to register this as the handle with the manager
18714         // so we just set the id rather than calling the setter.
18715         this.handleElId = id;
18716
18717         // the linked element is the element that gets dragged by default
18718         this.setDragElId(id);
18719
18720         // by default, clicked anchors will not start drag operations.
18721         this.invalidHandleTypes = { A: "A" };
18722         this.invalidHandleIds = {};
18723         this.invalidHandleClasses = [];
18724
18725         this.applyConfig();
18726
18727         this.handleOnAvailable();
18728     },
18729
18730     /**
18731      * Applies the configuration parameters that were passed into the constructor.
18732      * This is supposed to happen at each level through the inheritance chain.  So
18733      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18734      * DragDrop in order to get all of the parameters that are available in
18735      * each object.
18736      * @method applyConfig
18737      */
18738     applyConfig: function() {
18739
18740         // configurable properties:
18741         //    padding, isTarget, maintainOffset, primaryButtonOnly
18742         this.padding           = this.config.padding || [0, 0, 0, 0];
18743         this.isTarget          = (this.config.isTarget !== false);
18744         this.maintainOffset    = (this.config.maintainOffset);
18745         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18746
18747     },
18748
18749     /**
18750      * Executed when the linked element is available
18751      * @method handleOnAvailable
18752      * @private
18753      */
18754     handleOnAvailable: function() {
18755         this.available = true;
18756         this.resetConstraints();
18757         this.onAvailable();
18758     },
18759
18760      /**
18761      * Configures the padding for the target zone in px.  Effectively expands
18762      * (or reduces) the virtual object size for targeting calculations.
18763      * Supports css-style shorthand; if only one parameter is passed, all sides
18764      * will have that padding, and if only two are passed, the top and bottom
18765      * will have the first param, the left and right the second.
18766      * @method setPadding
18767      * @param {int} iTop    Top pad
18768      * @param {int} iRight  Right pad
18769      * @param {int} iBot    Bot pad
18770      * @param {int} iLeft   Left pad
18771      */
18772     setPadding: function(iTop, iRight, iBot, iLeft) {
18773         // this.padding = [iLeft, iRight, iTop, iBot];
18774         if (!iRight && 0 !== iRight) {
18775             this.padding = [iTop, iTop, iTop, iTop];
18776         } else if (!iBot && 0 !== iBot) {
18777             this.padding = [iTop, iRight, iTop, iRight];
18778         } else {
18779             this.padding = [iTop, iRight, iBot, iLeft];
18780         }
18781     },
18782
18783     /**
18784      * Stores the initial placement of the linked element.
18785      * @method setInitialPosition
18786      * @param {int} diffX   the X offset, default 0
18787      * @param {int} diffY   the Y offset, default 0
18788      */
18789     setInitPosition: function(diffX, diffY) {
18790         var el = this.getEl();
18791
18792         if (!this.DDM.verifyEl(el)) {
18793             return;
18794         }
18795
18796         var dx = diffX || 0;
18797         var dy = diffY || 0;
18798
18799         var p = Dom.getXY( el );
18800
18801         this.initPageX = p[0] - dx;
18802         this.initPageY = p[1] - dy;
18803
18804         this.lastPageX = p[0];
18805         this.lastPageY = p[1];
18806
18807
18808         this.setStartPosition(p);
18809     },
18810
18811     /**
18812      * Sets the start position of the element.  This is set when the obj
18813      * is initialized, the reset when a drag is started.
18814      * @method setStartPosition
18815      * @param pos current position (from previous lookup)
18816      * @private
18817      */
18818     setStartPosition: function(pos) {
18819         var p = pos || Dom.getXY( this.getEl() );
18820         this.deltaSetXY = null;
18821
18822         this.startPageX = p[0];
18823         this.startPageY = p[1];
18824     },
18825
18826     /**
18827      * Add this instance to a group of related drag/drop objects.  All
18828      * instances belong to at least one group, and can belong to as many
18829      * groups as needed.
18830      * @method addToGroup
18831      * @param sGroup {string} the name of the group
18832      */
18833     addToGroup: function(sGroup) {
18834         this.groups[sGroup] = true;
18835         this.DDM.regDragDrop(this, sGroup);
18836     },
18837
18838     /**
18839      * Remove's this instance from the supplied interaction group
18840      * @method removeFromGroup
18841      * @param {string}  sGroup  The group to drop
18842      */
18843     removeFromGroup: function(sGroup) {
18844         if (this.groups[sGroup]) {
18845             delete this.groups[sGroup];
18846         }
18847
18848         this.DDM.removeDDFromGroup(this, sGroup);
18849     },
18850
18851     /**
18852      * Allows you to specify that an element other than the linked element
18853      * will be moved with the cursor during a drag
18854      * @method setDragElId
18855      * @param id {string} the id of the element that will be used to initiate the drag
18856      */
18857     setDragElId: function(id) {
18858         this.dragElId = id;
18859     },
18860
18861     /**
18862      * Allows you to specify a child of the linked element that should be
18863      * used to initiate the drag operation.  An example of this would be if
18864      * you have a content div with text and links.  Clicking anywhere in the
18865      * content area would normally start the drag operation.  Use this method
18866      * to specify that an element inside of the content div is the element
18867      * that starts the drag operation.
18868      * @method setHandleElId
18869      * @param id {string} the id of the element that will be used to
18870      * initiate the drag.
18871      */
18872     setHandleElId: function(id) {
18873         if (typeof id !== "string") {
18874             id = Roo.id(id);
18875         }
18876         this.handleElId = id;
18877         this.DDM.regHandle(this.id, id);
18878     },
18879
18880     /**
18881      * Allows you to set an element outside of the linked element as a drag
18882      * handle
18883      * @method setOuterHandleElId
18884      * @param id the id of the element that will be used to initiate the drag
18885      */
18886     setOuterHandleElId: function(id) {
18887         if (typeof id !== "string") {
18888             id = Roo.id(id);
18889         }
18890         Event.on(id, "mousedown",
18891                 this.handleMouseDown, this);
18892         this.setHandleElId(id);
18893
18894         this.hasOuterHandles = true;
18895     },
18896
18897     /**
18898      * Remove all drag and drop hooks for this element
18899      * @method unreg
18900      */
18901     unreg: function() {
18902         Event.un(this.id, "mousedown",
18903                 this.handleMouseDown);
18904         Event.un(this.id, "touchstart",
18905                 this.handleMouseDown);
18906         this._domRef = null;
18907         this.DDM._remove(this);
18908     },
18909
18910     destroy : function(){
18911         this.unreg();
18912     },
18913
18914     /**
18915      * Returns true if this instance is locked, or the drag drop mgr is locked
18916      * (meaning that all drag/drop is disabled on the page.)
18917      * @method isLocked
18918      * @return {boolean} true if this obj or all drag/drop is locked, else
18919      * false
18920      */
18921     isLocked: function() {
18922         return (this.DDM.isLocked() || this.locked);
18923     },
18924
18925     /**
18926      * Fired when this object is clicked
18927      * @method handleMouseDown
18928      * @param {Event} e
18929      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18930      * @private
18931      */
18932     handleMouseDown: function(e, oDD){
18933      
18934         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18935             //Roo.log('not touch/ button !=0');
18936             return;
18937         }
18938         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18939             return; // double touch..
18940         }
18941         
18942
18943         if (this.isLocked()) {
18944             //Roo.log('locked');
18945             return;
18946         }
18947
18948         this.DDM.refreshCache(this.groups);
18949 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18950         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18951         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18952             //Roo.log('no outer handes or not over target');
18953                 // do nothing.
18954         } else {
18955 //            Roo.log('check validator');
18956             if (this.clickValidator(e)) {
18957 //                Roo.log('validate success');
18958                 // set the initial element position
18959                 this.setStartPosition();
18960
18961
18962                 this.b4MouseDown(e);
18963                 this.onMouseDown(e);
18964
18965                 this.DDM.handleMouseDown(e, this);
18966
18967                 this.DDM.stopEvent(e);
18968             } else {
18969
18970
18971             }
18972         }
18973     },
18974
18975     clickValidator: function(e) {
18976         var target = e.getTarget();
18977         return ( this.isValidHandleChild(target) &&
18978                     (this.id == this.handleElId ||
18979                         this.DDM.handleWasClicked(target, this.id)) );
18980     },
18981
18982     /**
18983      * Allows you to specify a tag name that should not start a drag operation
18984      * when clicked.  This is designed to facilitate embedding links within a
18985      * drag handle that do something other than start the drag.
18986      * @method addInvalidHandleType
18987      * @param {string} tagName the type of element to exclude
18988      */
18989     addInvalidHandleType: function(tagName) {
18990         var type = tagName.toUpperCase();
18991         this.invalidHandleTypes[type] = type;
18992     },
18993
18994     /**
18995      * Lets you to specify an element id for a child of a drag handle
18996      * that should not initiate a drag
18997      * @method addInvalidHandleId
18998      * @param {string} id the element id of the element you wish to ignore
18999      */
19000     addInvalidHandleId: function(id) {
19001         if (typeof id !== "string") {
19002             id = Roo.id(id);
19003         }
19004         this.invalidHandleIds[id] = id;
19005     },
19006
19007     /**
19008      * Lets you specify a css class of elements that will not initiate a drag
19009      * @method addInvalidHandleClass
19010      * @param {string} cssClass the class of the elements you wish to ignore
19011      */
19012     addInvalidHandleClass: function(cssClass) {
19013         this.invalidHandleClasses.push(cssClass);
19014     },
19015
19016     /**
19017      * Unsets an excluded tag name set by addInvalidHandleType
19018      * @method removeInvalidHandleType
19019      * @param {string} tagName the type of element to unexclude
19020      */
19021     removeInvalidHandleType: function(tagName) {
19022         var type = tagName.toUpperCase();
19023         // this.invalidHandleTypes[type] = null;
19024         delete this.invalidHandleTypes[type];
19025     },
19026
19027     /**
19028      * Unsets an invalid handle id
19029      * @method removeInvalidHandleId
19030      * @param {string} id the id of the element to re-enable
19031      */
19032     removeInvalidHandleId: function(id) {
19033         if (typeof id !== "string") {
19034             id = Roo.id(id);
19035         }
19036         delete this.invalidHandleIds[id];
19037     },
19038
19039     /**
19040      * Unsets an invalid css class
19041      * @method removeInvalidHandleClass
19042      * @param {string} cssClass the class of the element(s) you wish to
19043      * re-enable
19044      */
19045     removeInvalidHandleClass: function(cssClass) {
19046         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19047             if (this.invalidHandleClasses[i] == cssClass) {
19048                 delete this.invalidHandleClasses[i];
19049             }
19050         }
19051     },
19052
19053     /**
19054      * Checks the tag exclusion list to see if this click should be ignored
19055      * @method isValidHandleChild
19056      * @param {HTMLElement} node the HTMLElement to evaluate
19057      * @return {boolean} true if this is a valid tag type, false if not
19058      */
19059     isValidHandleChild: function(node) {
19060
19061         var valid = true;
19062         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19063         var nodeName;
19064         try {
19065             nodeName = node.nodeName.toUpperCase();
19066         } catch(e) {
19067             nodeName = node.nodeName;
19068         }
19069         valid = valid && !this.invalidHandleTypes[nodeName];
19070         valid = valid && !this.invalidHandleIds[node.id];
19071
19072         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19073             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19074         }
19075
19076
19077         return valid;
19078
19079     },
19080
19081     /**
19082      * Create the array of horizontal tick marks if an interval was specified
19083      * in setXConstraint().
19084      * @method setXTicks
19085      * @private
19086      */
19087     setXTicks: function(iStartX, iTickSize) {
19088         this.xTicks = [];
19089         this.xTickSize = iTickSize;
19090
19091         var tickMap = {};
19092
19093         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19094             if (!tickMap[i]) {
19095                 this.xTicks[this.xTicks.length] = i;
19096                 tickMap[i] = true;
19097             }
19098         }
19099
19100         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19101             if (!tickMap[i]) {
19102                 this.xTicks[this.xTicks.length] = i;
19103                 tickMap[i] = true;
19104             }
19105         }
19106
19107         this.xTicks.sort(this.DDM.numericSort) ;
19108     },
19109
19110     /**
19111      * Create the array of vertical tick marks if an interval was specified in
19112      * setYConstraint().
19113      * @method setYTicks
19114      * @private
19115      */
19116     setYTicks: function(iStartY, iTickSize) {
19117         this.yTicks = [];
19118         this.yTickSize = iTickSize;
19119
19120         var tickMap = {};
19121
19122         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19123             if (!tickMap[i]) {
19124                 this.yTicks[this.yTicks.length] = i;
19125                 tickMap[i] = true;
19126             }
19127         }
19128
19129         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19130             if (!tickMap[i]) {
19131                 this.yTicks[this.yTicks.length] = i;
19132                 tickMap[i] = true;
19133             }
19134         }
19135
19136         this.yTicks.sort(this.DDM.numericSort) ;
19137     },
19138
19139     /**
19140      * By default, the element can be dragged any place on the screen.  Use
19141      * this method to limit the horizontal travel of the element.  Pass in
19142      * 0,0 for the parameters if you want to lock the drag to the y axis.
19143      * @method setXConstraint
19144      * @param {int} iLeft the number of pixels the element can move to the left
19145      * @param {int} iRight the number of pixels the element can move to the
19146      * right
19147      * @param {int} iTickSize optional parameter for specifying that the
19148      * element
19149      * should move iTickSize pixels at a time.
19150      */
19151     setXConstraint: function(iLeft, iRight, iTickSize) {
19152         this.leftConstraint = iLeft;
19153         this.rightConstraint = iRight;
19154
19155         this.minX = this.initPageX - iLeft;
19156         this.maxX = this.initPageX + iRight;
19157         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19158
19159         this.constrainX = true;
19160     },
19161
19162     /**
19163      * Clears any constraints applied to this instance.  Also clears ticks
19164      * since they can't exist independent of a constraint at this time.
19165      * @method clearConstraints
19166      */
19167     clearConstraints: function() {
19168         this.constrainX = false;
19169         this.constrainY = false;
19170         this.clearTicks();
19171     },
19172
19173     /**
19174      * Clears any tick interval defined for this instance
19175      * @method clearTicks
19176      */
19177     clearTicks: function() {
19178         this.xTicks = null;
19179         this.yTicks = null;
19180         this.xTickSize = 0;
19181         this.yTickSize = 0;
19182     },
19183
19184     /**
19185      * By default, the element can be dragged any place on the screen.  Set
19186      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19187      * parameters if you want to lock the drag to the x axis.
19188      * @method setYConstraint
19189      * @param {int} iUp the number of pixels the element can move up
19190      * @param {int} iDown the number of pixels the element can move down
19191      * @param {int} iTickSize optional parameter for specifying that the
19192      * element should move iTickSize pixels at a time.
19193      */
19194     setYConstraint: function(iUp, iDown, iTickSize) {
19195         this.topConstraint = iUp;
19196         this.bottomConstraint = iDown;
19197
19198         this.minY = this.initPageY - iUp;
19199         this.maxY = this.initPageY + iDown;
19200         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19201
19202         this.constrainY = true;
19203
19204     },
19205
19206     /**
19207      * resetConstraints must be called if you manually reposition a dd element.
19208      * @method resetConstraints
19209      * @param {boolean} maintainOffset
19210      */
19211     resetConstraints: function() {
19212
19213
19214         // Maintain offsets if necessary
19215         if (this.initPageX || this.initPageX === 0) {
19216             // figure out how much this thing has moved
19217             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19218             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19219
19220             this.setInitPosition(dx, dy);
19221
19222         // This is the first time we have detected the element's position
19223         } else {
19224             this.setInitPosition();
19225         }
19226
19227         if (this.constrainX) {
19228             this.setXConstraint( this.leftConstraint,
19229                                  this.rightConstraint,
19230                                  this.xTickSize        );
19231         }
19232
19233         if (this.constrainY) {
19234             this.setYConstraint( this.topConstraint,
19235                                  this.bottomConstraint,
19236                                  this.yTickSize         );
19237         }
19238     },
19239
19240     /**
19241      * Normally the drag element is moved pixel by pixel, but we can specify
19242      * that it move a number of pixels at a time.  This method resolves the
19243      * location when we have it set up like this.
19244      * @method getTick
19245      * @param {int} val where we want to place the object
19246      * @param {int[]} tickArray sorted array of valid points
19247      * @return {int} the closest tick
19248      * @private
19249      */
19250     getTick: function(val, tickArray) {
19251
19252         if (!tickArray) {
19253             // If tick interval is not defined, it is effectively 1 pixel,
19254             // so we return the value passed to us.
19255             return val;
19256         } else if (tickArray[0] >= val) {
19257             // The value is lower than the first tick, so we return the first
19258             // tick.
19259             return tickArray[0];
19260         } else {
19261             for (var i=0, len=tickArray.length; i<len; ++i) {
19262                 var next = i + 1;
19263                 if (tickArray[next] && tickArray[next] >= val) {
19264                     var diff1 = val - tickArray[i];
19265                     var diff2 = tickArray[next] - val;
19266                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19267                 }
19268             }
19269
19270             // The value is larger than the last tick, so we return the last
19271             // tick.
19272             return tickArray[tickArray.length - 1];
19273         }
19274     },
19275
19276     /**
19277      * toString method
19278      * @method toString
19279      * @return {string} string representation of the dd obj
19280      */
19281     toString: function() {
19282         return ("DragDrop " + this.id);
19283     }
19284
19285 });
19286
19287 })();
19288 /*
19289  * Based on:
19290  * Ext JS Library 1.1.1
19291  * Copyright(c) 2006-2007, Ext JS, LLC.
19292  *
19293  * Originally Released Under LGPL - original licence link has changed is not relivant.
19294  *
19295  * Fork - LGPL
19296  * <script type="text/javascript">
19297  */
19298
19299
19300 /**
19301  * The drag and drop utility provides a framework for building drag and drop
19302  * applications.  In addition to enabling drag and drop for specific elements,
19303  * the drag and drop elements are tracked by the manager class, and the
19304  * interactions between the various elements are tracked during the drag and
19305  * the implementing code is notified about these important moments.
19306  */
19307
19308 // Only load the library once.  Rewriting the manager class would orphan
19309 // existing drag and drop instances.
19310 if (!Roo.dd.DragDropMgr) {
19311
19312 /**
19313  * @class Roo.dd.DragDropMgr
19314  * DragDropMgr is a singleton that tracks the element interaction for
19315  * all DragDrop items in the window.  Generally, you will not call
19316  * this class directly, but it does have helper methods that could
19317  * be useful in your DragDrop implementations.
19318  * @singleton
19319  */
19320 Roo.dd.DragDropMgr = function() {
19321
19322     var Event = Roo.EventManager;
19323
19324     return {
19325
19326         /**
19327          * Two dimensional Array of registered DragDrop objects.  The first
19328          * dimension is the DragDrop item group, the second the DragDrop
19329          * object.
19330          * @property ids
19331          * @type {string: string}
19332          * @private
19333          * @static
19334          */
19335         ids: {},
19336
19337         /**
19338          * Array of element ids defined as drag handles.  Used to determine
19339          * if the element that generated the mousedown event is actually the
19340          * handle and not the html element itself.
19341          * @property handleIds
19342          * @type {string: string}
19343          * @private
19344          * @static
19345          */
19346         handleIds: {},
19347
19348         /**
19349          * the DragDrop object that is currently being dragged
19350          * @property dragCurrent
19351          * @type DragDrop
19352          * @private
19353          * @static
19354          **/
19355         dragCurrent: null,
19356
19357         /**
19358          * the DragDrop object(s) that are being hovered over
19359          * @property dragOvers
19360          * @type Array
19361          * @private
19362          * @static
19363          */
19364         dragOvers: {},
19365
19366         /**
19367          * the X distance between the cursor and the object being dragged
19368          * @property deltaX
19369          * @type int
19370          * @private
19371          * @static
19372          */
19373         deltaX: 0,
19374
19375         /**
19376          * the Y distance between the cursor and the object being dragged
19377          * @property deltaY
19378          * @type int
19379          * @private
19380          * @static
19381          */
19382         deltaY: 0,
19383
19384         /**
19385          * Flag to determine if we should prevent the default behavior of the
19386          * events we define. By default this is true, but this can be set to
19387          * false if you need the default behavior (not recommended)
19388          * @property preventDefault
19389          * @type boolean
19390          * @static
19391          */
19392         preventDefault: true,
19393
19394         /**
19395          * Flag to determine if we should stop the propagation of the events
19396          * we generate. This is true by default but you may want to set it to
19397          * false if the html element contains other features that require the
19398          * mouse click.
19399          * @property stopPropagation
19400          * @type boolean
19401          * @static
19402          */
19403         stopPropagation: true,
19404
19405         /**
19406          * Internal flag that is set to true when drag and drop has been
19407          * intialized
19408          * @property initialized
19409          * @private
19410          * @static
19411          */
19412         initalized: false,
19413
19414         /**
19415          * All drag and drop can be disabled.
19416          * @property locked
19417          * @private
19418          * @static
19419          */
19420         locked: false,
19421
19422         /**
19423          * Called the first time an element is registered.
19424          * @method init
19425          * @private
19426          * @static
19427          */
19428         init: function() {
19429             this.initialized = true;
19430         },
19431
19432         /**
19433          * In point mode, drag and drop interaction is defined by the
19434          * location of the cursor during the drag/drop
19435          * @property POINT
19436          * @type int
19437          * @static
19438          */
19439         POINT: 0,
19440
19441         /**
19442          * In intersect mode, drag and drop interactio nis defined by the
19443          * overlap of two or more drag and drop objects.
19444          * @property INTERSECT
19445          * @type int
19446          * @static
19447          */
19448         INTERSECT: 1,
19449
19450         /**
19451          * The current drag and drop mode.  Default: POINT
19452          * @property mode
19453          * @type int
19454          * @static
19455          */
19456         mode: 0,
19457
19458         /**
19459          * Runs method on all drag and drop objects
19460          * @method _execOnAll
19461          * @private
19462          * @static
19463          */
19464         _execOnAll: function(sMethod, args) {
19465             for (var i in this.ids) {
19466                 for (var j in this.ids[i]) {
19467                     var oDD = this.ids[i][j];
19468                     if (! this.isTypeOfDD(oDD)) {
19469                         continue;
19470                     }
19471                     oDD[sMethod].apply(oDD, args);
19472                 }
19473             }
19474         },
19475
19476         /**
19477          * Drag and drop initialization.  Sets up the global event handlers
19478          * @method _onLoad
19479          * @private
19480          * @static
19481          */
19482         _onLoad: function() {
19483
19484             this.init();
19485
19486             if (!Roo.isTouch) {
19487                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19488                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19489             }
19490             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19491             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19492             
19493             Event.on(window,   "unload",    this._onUnload, this, true);
19494             Event.on(window,   "resize",    this._onResize, this, true);
19495             // Event.on(window,   "mouseout",    this._test);
19496
19497         },
19498
19499         /**
19500          * Reset constraints on all drag and drop objs
19501          * @method _onResize
19502          * @private
19503          * @static
19504          */
19505         _onResize: function(e) {
19506             this._execOnAll("resetConstraints", []);
19507         },
19508
19509         /**
19510          * Lock all drag and drop functionality
19511          * @method lock
19512          * @static
19513          */
19514         lock: function() { this.locked = true; },
19515
19516         /**
19517          * Unlock all drag and drop functionality
19518          * @method unlock
19519          * @static
19520          */
19521         unlock: function() { this.locked = false; },
19522
19523         /**
19524          * Is drag and drop locked?
19525          * @method isLocked
19526          * @return {boolean} True if drag and drop is locked, false otherwise.
19527          * @static
19528          */
19529         isLocked: function() { return this.locked; },
19530
19531         /**
19532          * Location cache that is set for all drag drop objects when a drag is
19533          * initiated, cleared when the drag is finished.
19534          * @property locationCache
19535          * @private
19536          * @static
19537          */
19538         locationCache: {},
19539
19540         /**
19541          * Set useCache to false if you want to force object the lookup of each
19542          * drag and drop linked element constantly during a drag.
19543          * @property useCache
19544          * @type boolean
19545          * @static
19546          */
19547         useCache: true,
19548
19549         /**
19550          * The number of pixels that the mouse needs to move after the
19551          * mousedown before the drag is initiated.  Default=3;
19552          * @property clickPixelThresh
19553          * @type int
19554          * @static
19555          */
19556         clickPixelThresh: 3,
19557
19558         /**
19559          * The number of milliseconds after the mousedown event to initiate the
19560          * drag if we don't get a mouseup event. Default=1000
19561          * @property clickTimeThresh
19562          * @type int
19563          * @static
19564          */
19565         clickTimeThresh: 350,
19566
19567         /**
19568          * Flag that indicates that either the drag pixel threshold or the
19569          * mousdown time threshold has been met
19570          * @property dragThreshMet
19571          * @type boolean
19572          * @private
19573          * @static
19574          */
19575         dragThreshMet: false,
19576
19577         /**
19578          * Timeout used for the click time threshold
19579          * @property clickTimeout
19580          * @type Object
19581          * @private
19582          * @static
19583          */
19584         clickTimeout: null,
19585
19586         /**
19587          * The X position of the mousedown event stored for later use when a
19588          * drag threshold is met.
19589          * @property startX
19590          * @type int
19591          * @private
19592          * @static
19593          */
19594         startX: 0,
19595
19596         /**
19597          * The Y position of the mousedown event stored for later use when a
19598          * drag threshold is met.
19599          * @property startY
19600          * @type int
19601          * @private
19602          * @static
19603          */
19604         startY: 0,
19605
19606         /**
19607          * Each DragDrop instance must be registered with the DragDropMgr.
19608          * This is executed in DragDrop.init()
19609          * @method regDragDrop
19610          * @param {DragDrop} oDD the DragDrop object to register
19611          * @param {String} sGroup the name of the group this element belongs to
19612          * @static
19613          */
19614         regDragDrop: function(oDD, sGroup) {
19615             if (!this.initialized) { this.init(); }
19616
19617             if (!this.ids[sGroup]) {
19618                 this.ids[sGroup] = {};
19619             }
19620             this.ids[sGroup][oDD.id] = oDD;
19621         },
19622
19623         /**
19624          * Removes the supplied dd instance from the supplied group. Executed
19625          * by DragDrop.removeFromGroup, so don't call this function directly.
19626          * @method removeDDFromGroup
19627          * @private
19628          * @static
19629          */
19630         removeDDFromGroup: function(oDD, sGroup) {
19631             if (!this.ids[sGroup]) {
19632                 this.ids[sGroup] = {};
19633             }
19634
19635             var obj = this.ids[sGroup];
19636             if (obj && obj[oDD.id]) {
19637                 delete obj[oDD.id];
19638             }
19639         },
19640
19641         /**
19642          * Unregisters a drag and drop item.  This is executed in
19643          * DragDrop.unreg, use that method instead of calling this directly.
19644          * @method _remove
19645          * @private
19646          * @static
19647          */
19648         _remove: function(oDD) {
19649             for (var g in oDD.groups) {
19650                 if (g && this.ids[g][oDD.id]) {
19651                     delete this.ids[g][oDD.id];
19652                 }
19653             }
19654             delete this.handleIds[oDD.id];
19655         },
19656
19657         /**
19658          * Each DragDrop handle element must be registered.  This is done
19659          * automatically when executing DragDrop.setHandleElId()
19660          * @method regHandle
19661          * @param {String} sDDId the DragDrop id this element is a handle for
19662          * @param {String} sHandleId the id of the element that is the drag
19663          * handle
19664          * @static
19665          */
19666         regHandle: function(sDDId, sHandleId) {
19667             if (!this.handleIds[sDDId]) {
19668                 this.handleIds[sDDId] = {};
19669             }
19670             this.handleIds[sDDId][sHandleId] = sHandleId;
19671         },
19672
19673         /**
19674          * Utility function to determine if a given element has been
19675          * registered as a drag drop item.
19676          * @method isDragDrop
19677          * @param {String} id the element id to check
19678          * @return {boolean} true if this element is a DragDrop item,
19679          * false otherwise
19680          * @static
19681          */
19682         isDragDrop: function(id) {
19683             return ( this.getDDById(id) ) ? true : false;
19684         },
19685
19686         /**
19687          * Returns the drag and drop instances that are in all groups the
19688          * passed in instance belongs to.
19689          * @method getRelated
19690          * @param {DragDrop} p_oDD the obj to get related data for
19691          * @param {boolean} bTargetsOnly if true, only return targetable objs
19692          * @return {DragDrop[]} the related instances
19693          * @static
19694          */
19695         getRelated: function(p_oDD, bTargetsOnly) {
19696             var oDDs = [];
19697             for (var i in p_oDD.groups) {
19698                 for (j in this.ids[i]) {
19699                     var dd = this.ids[i][j];
19700                     if (! this.isTypeOfDD(dd)) {
19701                         continue;
19702                     }
19703                     if (!bTargetsOnly || dd.isTarget) {
19704                         oDDs[oDDs.length] = dd;
19705                     }
19706                 }
19707             }
19708
19709             return oDDs;
19710         },
19711
19712         /**
19713          * Returns true if the specified dd target is a legal target for
19714          * the specifice drag obj
19715          * @method isLegalTarget
19716          * @param {DragDrop} the drag obj
19717          * @param {DragDrop} the target
19718          * @return {boolean} true if the target is a legal target for the
19719          * dd obj
19720          * @static
19721          */
19722         isLegalTarget: function (oDD, oTargetDD) {
19723             var targets = this.getRelated(oDD, true);
19724             for (var i=0, len=targets.length;i<len;++i) {
19725                 if (targets[i].id == oTargetDD.id) {
19726                     return true;
19727                 }
19728             }
19729
19730             return false;
19731         },
19732
19733         /**
19734          * My goal is to be able to transparently determine if an object is
19735          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19736          * returns "object", oDD.constructor.toString() always returns
19737          * "DragDrop" and not the name of the subclass.  So for now it just
19738          * evaluates a well-known variable in DragDrop.
19739          * @method isTypeOfDD
19740          * @param {Object} the object to evaluate
19741          * @return {boolean} true if typeof oDD = DragDrop
19742          * @static
19743          */
19744         isTypeOfDD: function (oDD) {
19745             return (oDD && oDD.__ygDragDrop);
19746         },
19747
19748         /**
19749          * Utility function to determine if a given element has been
19750          * registered as a drag drop handle for the given Drag Drop object.
19751          * @method isHandle
19752          * @param {String} id the element id to check
19753          * @return {boolean} true if this element is a DragDrop handle, false
19754          * otherwise
19755          * @static
19756          */
19757         isHandle: function(sDDId, sHandleId) {
19758             return ( this.handleIds[sDDId] &&
19759                             this.handleIds[sDDId][sHandleId] );
19760         },
19761
19762         /**
19763          * Returns the DragDrop instance for a given id
19764          * @method getDDById
19765          * @param {String} id the id of the DragDrop object
19766          * @return {DragDrop} the drag drop object, null if it is not found
19767          * @static
19768          */
19769         getDDById: function(id) {
19770             for (var i in this.ids) {
19771                 if (this.ids[i][id]) {
19772                     return this.ids[i][id];
19773                 }
19774             }
19775             return null;
19776         },
19777
19778         /**
19779          * Fired after a registered DragDrop object gets the mousedown event.
19780          * Sets up the events required to track the object being dragged
19781          * @method handleMouseDown
19782          * @param {Event} e the event
19783          * @param oDD the DragDrop object being dragged
19784          * @private
19785          * @static
19786          */
19787         handleMouseDown: function(e, oDD) {
19788             if(Roo.QuickTips){
19789                 Roo.QuickTips.disable();
19790             }
19791             this.currentTarget = e.getTarget();
19792
19793             this.dragCurrent = oDD;
19794
19795             var el = oDD.getEl();
19796
19797             // track start position
19798             this.startX = e.getPageX();
19799             this.startY = e.getPageY();
19800
19801             this.deltaX = this.startX - el.offsetLeft;
19802             this.deltaY = this.startY - el.offsetTop;
19803
19804             this.dragThreshMet = false;
19805
19806             this.clickTimeout = setTimeout(
19807                     function() {
19808                         var DDM = Roo.dd.DDM;
19809                         DDM.startDrag(DDM.startX, DDM.startY);
19810                     },
19811                     this.clickTimeThresh );
19812         },
19813
19814         /**
19815          * Fired when either the drag pixel threshol or the mousedown hold
19816          * time threshold has been met.
19817          * @method startDrag
19818          * @param x {int} the X position of the original mousedown
19819          * @param y {int} the Y position of the original mousedown
19820          * @static
19821          */
19822         startDrag: function(x, y) {
19823             clearTimeout(this.clickTimeout);
19824             if (this.dragCurrent) {
19825                 this.dragCurrent.b4StartDrag(x, y);
19826                 this.dragCurrent.startDrag(x, y);
19827             }
19828             this.dragThreshMet = true;
19829         },
19830
19831         /**
19832          * Internal function to handle the mouseup event.  Will be invoked
19833          * from the context of the document.
19834          * @method handleMouseUp
19835          * @param {Event} e the event
19836          * @private
19837          * @static
19838          */
19839         handleMouseUp: function(e) {
19840
19841             if(Roo.QuickTips){
19842                 Roo.QuickTips.enable();
19843             }
19844             if (! this.dragCurrent) {
19845                 return;
19846             }
19847
19848             clearTimeout(this.clickTimeout);
19849
19850             if (this.dragThreshMet) {
19851                 this.fireEvents(e, true);
19852             } else {
19853             }
19854
19855             this.stopDrag(e);
19856
19857             this.stopEvent(e);
19858         },
19859
19860         /**
19861          * Utility to stop event propagation and event default, if these
19862          * features are turned on.
19863          * @method stopEvent
19864          * @param {Event} e the event as returned by this.getEvent()
19865          * @static
19866          */
19867         stopEvent: function(e){
19868             if(this.stopPropagation) {
19869                 e.stopPropagation();
19870             }
19871
19872             if (this.preventDefault) {
19873                 e.preventDefault();
19874             }
19875         },
19876
19877         /**
19878          * Internal function to clean up event handlers after the drag
19879          * operation is complete
19880          * @method stopDrag
19881          * @param {Event} e the event
19882          * @private
19883          * @static
19884          */
19885         stopDrag: function(e) {
19886             // Fire the drag end event for the item that was dragged
19887             if (this.dragCurrent) {
19888                 if (this.dragThreshMet) {
19889                     this.dragCurrent.b4EndDrag(e);
19890                     this.dragCurrent.endDrag(e);
19891                 }
19892
19893                 this.dragCurrent.onMouseUp(e);
19894             }
19895
19896             this.dragCurrent = null;
19897             this.dragOvers = {};
19898         },
19899
19900         /**
19901          * Internal function to handle the mousemove event.  Will be invoked
19902          * from the context of the html element.
19903          *
19904          * @TODO figure out what we can do about mouse events lost when the
19905          * user drags objects beyond the window boundary.  Currently we can
19906          * detect this in internet explorer by verifying that the mouse is
19907          * down during the mousemove event.  Firefox doesn't give us the
19908          * button state on the mousemove event.
19909          * @method handleMouseMove
19910          * @param {Event} e the event
19911          * @private
19912          * @static
19913          */
19914         handleMouseMove: function(e) {
19915             if (! this.dragCurrent) {
19916                 return true;
19917             }
19918
19919             // var button = e.which || e.button;
19920
19921             // check for IE mouseup outside of page boundary
19922             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19923                 this.stopEvent(e);
19924                 return this.handleMouseUp(e);
19925             }
19926
19927             if (!this.dragThreshMet) {
19928                 var diffX = Math.abs(this.startX - e.getPageX());
19929                 var diffY = Math.abs(this.startY - e.getPageY());
19930                 if (diffX > this.clickPixelThresh ||
19931                             diffY > this.clickPixelThresh) {
19932                     this.startDrag(this.startX, this.startY);
19933                 }
19934             }
19935
19936             if (this.dragThreshMet) {
19937                 this.dragCurrent.b4Drag(e);
19938                 this.dragCurrent.onDrag(e);
19939                 if(!this.dragCurrent.moveOnly){
19940                     this.fireEvents(e, false);
19941                 }
19942             }
19943
19944             this.stopEvent(e);
19945
19946             return true;
19947         },
19948
19949         /**
19950          * Iterates over all of the DragDrop elements to find ones we are
19951          * hovering over or dropping on
19952          * @method fireEvents
19953          * @param {Event} e the event
19954          * @param {boolean} isDrop is this a drop op or a mouseover op?
19955          * @private
19956          * @static
19957          */
19958         fireEvents: function(e, isDrop) {
19959             var dc = this.dragCurrent;
19960
19961             // If the user did the mouse up outside of the window, we could
19962             // get here even though we have ended the drag.
19963             if (!dc || dc.isLocked()) {
19964                 return;
19965             }
19966
19967             var pt = e.getPoint();
19968
19969             // cache the previous dragOver array
19970             var oldOvers = [];
19971
19972             var outEvts   = [];
19973             var overEvts  = [];
19974             var dropEvts  = [];
19975             var enterEvts = [];
19976
19977             // Check to see if the object(s) we were hovering over is no longer
19978             // being hovered over so we can fire the onDragOut event
19979             for (var i in this.dragOvers) {
19980
19981                 var ddo = this.dragOvers[i];
19982
19983                 if (! this.isTypeOfDD(ddo)) {
19984                     continue;
19985                 }
19986
19987                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19988                     outEvts.push( ddo );
19989                 }
19990
19991                 oldOvers[i] = true;
19992                 delete this.dragOvers[i];
19993             }
19994
19995             for (var sGroup in dc.groups) {
19996
19997                 if ("string" != typeof sGroup) {
19998                     continue;
19999                 }
20000
20001                 for (i in this.ids[sGroup]) {
20002                     var oDD = this.ids[sGroup][i];
20003                     if (! this.isTypeOfDD(oDD)) {
20004                         continue;
20005                     }
20006
20007                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20008                         if (this.isOverTarget(pt, oDD, this.mode)) {
20009                             // look for drop interactions
20010                             if (isDrop) {
20011                                 dropEvts.push( oDD );
20012                             // look for drag enter and drag over interactions
20013                             } else {
20014
20015                                 // initial drag over: dragEnter fires
20016                                 if (!oldOvers[oDD.id]) {
20017                                     enterEvts.push( oDD );
20018                                 // subsequent drag overs: dragOver fires
20019                                 } else {
20020                                     overEvts.push( oDD );
20021                                 }
20022
20023                                 this.dragOvers[oDD.id] = oDD;
20024                             }
20025                         }
20026                     }
20027                 }
20028             }
20029
20030             if (this.mode) {
20031                 if (outEvts.length) {
20032                     dc.b4DragOut(e, outEvts);
20033                     dc.onDragOut(e, outEvts);
20034                 }
20035
20036                 if (enterEvts.length) {
20037                     dc.onDragEnter(e, enterEvts);
20038                 }
20039
20040                 if (overEvts.length) {
20041                     dc.b4DragOver(e, overEvts);
20042                     dc.onDragOver(e, overEvts);
20043                 }
20044
20045                 if (dropEvts.length) {
20046                     dc.b4DragDrop(e, dropEvts);
20047                     dc.onDragDrop(e, dropEvts);
20048                 }
20049
20050             } else {
20051                 // fire dragout events
20052                 var len = 0;
20053                 for (i=0, len=outEvts.length; i<len; ++i) {
20054                     dc.b4DragOut(e, outEvts[i].id);
20055                     dc.onDragOut(e, outEvts[i].id);
20056                 }
20057
20058                 // fire enter events
20059                 for (i=0,len=enterEvts.length; i<len; ++i) {
20060                     // dc.b4DragEnter(e, oDD.id);
20061                     dc.onDragEnter(e, enterEvts[i].id);
20062                 }
20063
20064                 // fire over events
20065                 for (i=0,len=overEvts.length; i<len; ++i) {
20066                     dc.b4DragOver(e, overEvts[i].id);
20067                     dc.onDragOver(e, overEvts[i].id);
20068                 }
20069
20070                 // fire drop events
20071                 for (i=0, len=dropEvts.length; i<len; ++i) {
20072                     dc.b4DragDrop(e, dropEvts[i].id);
20073                     dc.onDragDrop(e, dropEvts[i].id);
20074                 }
20075
20076             }
20077
20078             // notify about a drop that did not find a target
20079             if (isDrop && !dropEvts.length) {
20080                 dc.onInvalidDrop(e);
20081             }
20082
20083         },
20084
20085         /**
20086          * Helper function for getting the best match from the list of drag
20087          * and drop objects returned by the drag and drop events when we are
20088          * in INTERSECT mode.  It returns either the first object that the
20089          * cursor is over, or the object that has the greatest overlap with
20090          * the dragged element.
20091          * @method getBestMatch
20092          * @param  {DragDrop[]} dds The array of drag and drop objects
20093          * targeted
20094          * @return {DragDrop}       The best single match
20095          * @static
20096          */
20097         getBestMatch: function(dds) {
20098             var winner = null;
20099             // Return null if the input is not what we expect
20100             //if (!dds || !dds.length || dds.length == 0) {
20101                // winner = null;
20102             // If there is only one item, it wins
20103             //} else if (dds.length == 1) {
20104
20105             var len = dds.length;
20106
20107             if (len == 1) {
20108                 winner = dds[0];
20109             } else {
20110                 // Loop through the targeted items
20111                 for (var i=0; i<len; ++i) {
20112                     var dd = dds[i];
20113                     // If the cursor is over the object, it wins.  If the
20114                     // cursor is over multiple matches, the first one we come
20115                     // to wins.
20116                     if (dd.cursorIsOver) {
20117                         winner = dd;
20118                         break;
20119                     // Otherwise the object with the most overlap wins
20120                     } else {
20121                         if (!winner ||
20122                             winner.overlap.getArea() < dd.overlap.getArea()) {
20123                             winner = dd;
20124                         }
20125                     }
20126                 }
20127             }
20128
20129             return winner;
20130         },
20131
20132         /**
20133          * Refreshes the cache of the top-left and bottom-right points of the
20134          * drag and drop objects in the specified group(s).  This is in the
20135          * format that is stored in the drag and drop instance, so typical
20136          * usage is:
20137          * <code>
20138          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20139          * </code>
20140          * Alternatively:
20141          * <code>
20142          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20143          * </code>
20144          * @TODO this really should be an indexed array.  Alternatively this
20145          * method could accept both.
20146          * @method refreshCache
20147          * @param {Object} groups an associative array of groups to refresh
20148          * @static
20149          */
20150         refreshCache: function(groups) {
20151             for (var sGroup in groups) {
20152                 if ("string" != typeof sGroup) {
20153                     continue;
20154                 }
20155                 for (var i in this.ids[sGroup]) {
20156                     var oDD = this.ids[sGroup][i];
20157
20158                     if (this.isTypeOfDD(oDD)) {
20159                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20160                         var loc = this.getLocation(oDD);
20161                         if (loc) {
20162                             this.locationCache[oDD.id] = loc;
20163                         } else {
20164                             delete this.locationCache[oDD.id];
20165                             // this will unregister the drag and drop object if
20166                             // the element is not in a usable state
20167                             // oDD.unreg();
20168                         }
20169                     }
20170                 }
20171             }
20172         },
20173
20174         /**
20175          * This checks to make sure an element exists and is in the DOM.  The
20176          * main purpose is to handle cases where innerHTML is used to remove
20177          * drag and drop objects from the DOM.  IE provides an 'unspecified
20178          * error' when trying to access the offsetParent of such an element
20179          * @method verifyEl
20180          * @param {HTMLElement} el the element to check
20181          * @return {boolean} true if the element looks usable
20182          * @static
20183          */
20184         verifyEl: function(el) {
20185             if (el) {
20186                 var parent;
20187                 if(Roo.isIE){
20188                     try{
20189                         parent = el.offsetParent;
20190                     }catch(e){}
20191                 }else{
20192                     parent = el.offsetParent;
20193                 }
20194                 if (parent) {
20195                     return true;
20196                 }
20197             }
20198
20199             return false;
20200         },
20201
20202         /**
20203          * Returns a Region object containing the drag and drop element's position
20204          * and size, including the padding configured for it
20205          * @method getLocation
20206          * @param {DragDrop} oDD the drag and drop object to get the
20207          *                       location for
20208          * @return {Roo.lib.Region} a Region object representing the total area
20209          *                             the element occupies, including any padding
20210          *                             the instance is configured for.
20211          * @static
20212          */
20213         getLocation: function(oDD) {
20214             if (! this.isTypeOfDD(oDD)) {
20215                 return null;
20216             }
20217
20218             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20219
20220             try {
20221                 pos= Roo.lib.Dom.getXY(el);
20222             } catch (e) { }
20223
20224             if (!pos) {
20225                 return null;
20226             }
20227
20228             x1 = pos[0];
20229             x2 = x1 + el.offsetWidth;
20230             y1 = pos[1];
20231             y2 = y1 + el.offsetHeight;
20232
20233             t = y1 - oDD.padding[0];
20234             r = x2 + oDD.padding[1];
20235             b = y2 + oDD.padding[2];
20236             l = x1 - oDD.padding[3];
20237
20238             return new Roo.lib.Region( t, r, b, l );
20239         },
20240
20241         /**
20242          * Checks the cursor location to see if it over the target
20243          * @method isOverTarget
20244          * @param {Roo.lib.Point} pt The point to evaluate
20245          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20246          * @return {boolean} true if the mouse is over the target
20247          * @private
20248          * @static
20249          */
20250         isOverTarget: function(pt, oTarget, intersect) {
20251             // use cache if available
20252             var loc = this.locationCache[oTarget.id];
20253             if (!loc || !this.useCache) {
20254                 loc = this.getLocation(oTarget);
20255                 this.locationCache[oTarget.id] = loc;
20256
20257             }
20258
20259             if (!loc) {
20260                 return false;
20261             }
20262
20263             oTarget.cursorIsOver = loc.contains( pt );
20264
20265             // DragDrop is using this as a sanity check for the initial mousedown
20266             // in this case we are done.  In POINT mode, if the drag obj has no
20267             // contraints, we are also done. Otherwise we need to evaluate the
20268             // location of the target as related to the actual location of the
20269             // dragged element.
20270             var dc = this.dragCurrent;
20271             if (!dc || !dc.getTargetCoord ||
20272                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20273                 return oTarget.cursorIsOver;
20274             }
20275
20276             oTarget.overlap = null;
20277
20278             // Get the current location of the drag element, this is the
20279             // location of the mouse event less the delta that represents
20280             // where the original mousedown happened on the element.  We
20281             // need to consider constraints and ticks as well.
20282             var pos = dc.getTargetCoord(pt.x, pt.y);
20283
20284             var el = dc.getDragEl();
20285             var curRegion = new Roo.lib.Region( pos.y,
20286                                                    pos.x + el.offsetWidth,
20287                                                    pos.y + el.offsetHeight,
20288                                                    pos.x );
20289
20290             var overlap = curRegion.intersect(loc);
20291
20292             if (overlap) {
20293                 oTarget.overlap = overlap;
20294                 return (intersect) ? true : oTarget.cursorIsOver;
20295             } else {
20296                 return false;
20297             }
20298         },
20299
20300         /**
20301          * unload event handler
20302          * @method _onUnload
20303          * @private
20304          * @static
20305          */
20306         _onUnload: function(e, me) {
20307             Roo.dd.DragDropMgr.unregAll();
20308         },
20309
20310         /**
20311          * Cleans up the drag and drop events and objects.
20312          * @method unregAll
20313          * @private
20314          * @static
20315          */
20316         unregAll: function() {
20317
20318             if (this.dragCurrent) {
20319                 this.stopDrag();
20320                 this.dragCurrent = null;
20321             }
20322
20323             this._execOnAll("unreg", []);
20324
20325             for (i in this.elementCache) {
20326                 delete this.elementCache[i];
20327             }
20328
20329             this.elementCache = {};
20330             this.ids = {};
20331         },
20332
20333         /**
20334          * A cache of DOM elements
20335          * @property elementCache
20336          * @private
20337          * @static
20338          */
20339         elementCache: {},
20340
20341         /**
20342          * Get the wrapper for the DOM element specified
20343          * @method getElWrapper
20344          * @param {String} id the id of the element to get
20345          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20346          * @private
20347          * @deprecated This wrapper isn't that useful
20348          * @static
20349          */
20350         getElWrapper: function(id) {
20351             var oWrapper = this.elementCache[id];
20352             if (!oWrapper || !oWrapper.el) {
20353                 oWrapper = this.elementCache[id] =
20354                     new this.ElementWrapper(Roo.getDom(id));
20355             }
20356             return oWrapper;
20357         },
20358
20359         /**
20360          * Returns the actual DOM element
20361          * @method getElement
20362          * @param {String} id the id of the elment to get
20363          * @return {Object} The element
20364          * @deprecated use Roo.getDom instead
20365          * @static
20366          */
20367         getElement: function(id) {
20368             return Roo.getDom(id);
20369         },
20370
20371         /**
20372          * Returns the style property for the DOM element (i.e.,
20373          * document.getElById(id).style)
20374          * @method getCss
20375          * @param {String} id the id of the elment to get
20376          * @return {Object} The style property of the element
20377          * @deprecated use Roo.getDom instead
20378          * @static
20379          */
20380         getCss: function(id) {
20381             var el = Roo.getDom(id);
20382             return (el) ? el.style : null;
20383         },
20384
20385         /**
20386          * Inner class for cached elements
20387          * @class DragDropMgr.ElementWrapper
20388          * @for DragDropMgr
20389          * @private
20390          * @deprecated
20391          */
20392         ElementWrapper: function(el) {
20393                 /**
20394                  * The element
20395                  * @property el
20396                  */
20397                 this.el = el || null;
20398                 /**
20399                  * The element id
20400                  * @property id
20401                  */
20402                 this.id = this.el && el.id;
20403                 /**
20404                  * A reference to the style property
20405                  * @property css
20406                  */
20407                 this.css = this.el && el.style;
20408             },
20409
20410         /**
20411          * Returns the X position of an html element
20412          * @method getPosX
20413          * @param el the element for which to get the position
20414          * @return {int} the X coordinate
20415          * @for DragDropMgr
20416          * @deprecated use Roo.lib.Dom.getX instead
20417          * @static
20418          */
20419         getPosX: function(el) {
20420             return Roo.lib.Dom.getX(el);
20421         },
20422
20423         /**
20424          * Returns the Y position of an html element
20425          * @method getPosY
20426          * @param el the element for which to get the position
20427          * @return {int} the Y coordinate
20428          * @deprecated use Roo.lib.Dom.getY instead
20429          * @static
20430          */
20431         getPosY: function(el) {
20432             return Roo.lib.Dom.getY(el);
20433         },
20434
20435         /**
20436          * Swap two nodes.  In IE, we use the native method, for others we
20437          * emulate the IE behavior
20438          * @method swapNode
20439          * @param n1 the first node to swap
20440          * @param n2 the other node to swap
20441          * @static
20442          */
20443         swapNode: function(n1, n2) {
20444             if (n1.swapNode) {
20445                 n1.swapNode(n2);
20446             } else {
20447                 var p = n2.parentNode;
20448                 var s = n2.nextSibling;
20449
20450                 if (s == n1) {
20451                     p.insertBefore(n1, n2);
20452                 } else if (n2 == n1.nextSibling) {
20453                     p.insertBefore(n2, n1);
20454                 } else {
20455                     n1.parentNode.replaceChild(n2, n1);
20456                     p.insertBefore(n1, s);
20457                 }
20458             }
20459         },
20460
20461         /**
20462          * Returns the current scroll position
20463          * @method getScroll
20464          * @private
20465          * @static
20466          */
20467         getScroll: function () {
20468             var t, l, dde=document.documentElement, db=document.body;
20469             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20470                 t = dde.scrollTop;
20471                 l = dde.scrollLeft;
20472             } else if (db) {
20473                 t = db.scrollTop;
20474                 l = db.scrollLeft;
20475             } else {
20476
20477             }
20478             return { top: t, left: l };
20479         },
20480
20481         /**
20482          * Returns the specified element style property
20483          * @method getStyle
20484          * @param {HTMLElement} el          the element
20485          * @param {string}      styleProp   the style property
20486          * @return {string} The value of the style property
20487          * @deprecated use Roo.lib.Dom.getStyle
20488          * @static
20489          */
20490         getStyle: function(el, styleProp) {
20491             return Roo.fly(el).getStyle(styleProp);
20492         },
20493
20494         /**
20495          * Gets the scrollTop
20496          * @method getScrollTop
20497          * @return {int} the document's scrollTop
20498          * @static
20499          */
20500         getScrollTop: function () { return this.getScroll().top; },
20501
20502         /**
20503          * Gets the scrollLeft
20504          * @method getScrollLeft
20505          * @return {int} the document's scrollTop
20506          * @static
20507          */
20508         getScrollLeft: function () { return this.getScroll().left; },
20509
20510         /**
20511          * Sets the x/y position of an element to the location of the
20512          * target element.
20513          * @method moveToEl
20514          * @param {HTMLElement} moveEl      The element to move
20515          * @param {HTMLElement} targetEl    The position reference element
20516          * @static
20517          */
20518         moveToEl: function (moveEl, targetEl) {
20519             var aCoord = Roo.lib.Dom.getXY(targetEl);
20520             Roo.lib.Dom.setXY(moveEl, aCoord);
20521         },
20522
20523         /**
20524          * Numeric array sort function
20525          * @method numericSort
20526          * @static
20527          */
20528         numericSort: function(a, b) { return (a - b); },
20529
20530         /**
20531          * Internal counter
20532          * @property _timeoutCount
20533          * @private
20534          * @static
20535          */
20536         _timeoutCount: 0,
20537
20538         /**
20539          * Trying to make the load order less important.  Without this we get
20540          * an error if this file is loaded before the Event Utility.
20541          * @method _addListeners
20542          * @private
20543          * @static
20544          */
20545         _addListeners: function() {
20546             var DDM = Roo.dd.DDM;
20547             if ( Roo.lib.Event && document ) {
20548                 DDM._onLoad();
20549             } else {
20550                 if (DDM._timeoutCount > 2000) {
20551                 } else {
20552                     setTimeout(DDM._addListeners, 10);
20553                     if (document && document.body) {
20554                         DDM._timeoutCount += 1;
20555                     }
20556                 }
20557             }
20558         },
20559
20560         /**
20561          * Recursively searches the immediate parent and all child nodes for
20562          * the handle element in order to determine wheter or not it was
20563          * clicked.
20564          * @method handleWasClicked
20565          * @param node the html element to inspect
20566          * @static
20567          */
20568         handleWasClicked: function(node, id) {
20569             if (this.isHandle(id, node.id)) {
20570                 return true;
20571             } else {
20572                 // check to see if this is a text node child of the one we want
20573                 var p = node.parentNode;
20574
20575                 while (p) {
20576                     if (this.isHandle(id, p.id)) {
20577                         return true;
20578                     } else {
20579                         p = p.parentNode;
20580                     }
20581                 }
20582             }
20583
20584             return false;
20585         }
20586
20587     };
20588
20589 }();
20590
20591 // shorter alias, save a few bytes
20592 Roo.dd.DDM = Roo.dd.DragDropMgr;
20593 Roo.dd.DDM._addListeners();
20594
20595 }/*
20596  * Based on:
20597  * Ext JS Library 1.1.1
20598  * Copyright(c) 2006-2007, Ext JS, LLC.
20599  *
20600  * Originally Released Under LGPL - original licence link has changed is not relivant.
20601  *
20602  * Fork - LGPL
20603  * <script type="text/javascript">
20604  */
20605
20606 /**
20607  * @class Roo.dd.DD
20608  * A DragDrop implementation where the linked element follows the
20609  * mouse cursor during a drag.
20610  * @extends Roo.dd.DragDrop
20611  * @constructor
20612  * @param {String} id the id of the linked element
20613  * @param {String} sGroup the group of related DragDrop items
20614  * @param {object} config an object containing configurable attributes
20615  *                Valid properties for DD:
20616  *                    scroll
20617  */
20618 Roo.dd.DD = function(id, sGroup, config) {
20619     if (id) {
20620         this.init(id, sGroup, config);
20621     }
20622 };
20623
20624 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20625
20626     /**
20627      * When set to true, the utility automatically tries to scroll the browser
20628      * window wehn a drag and drop element is dragged near the viewport boundary.
20629      * Defaults to true.
20630      * @property scroll
20631      * @type boolean
20632      */
20633     scroll: true,
20634
20635     /**
20636      * Sets the pointer offset to the distance between the linked element's top
20637      * left corner and the location the element was clicked
20638      * @method autoOffset
20639      * @param {int} iPageX the X coordinate of the click
20640      * @param {int} iPageY the Y coordinate of the click
20641      */
20642     autoOffset: function(iPageX, iPageY) {
20643         var x = iPageX - this.startPageX;
20644         var y = iPageY - this.startPageY;
20645         this.setDelta(x, y);
20646     },
20647
20648     /**
20649      * Sets the pointer offset.  You can call this directly to force the
20650      * offset to be in a particular location (e.g., pass in 0,0 to set it
20651      * to the center of the object)
20652      * @method setDelta
20653      * @param {int} iDeltaX the distance from the left
20654      * @param {int} iDeltaY the distance from the top
20655      */
20656     setDelta: function(iDeltaX, iDeltaY) {
20657         this.deltaX = iDeltaX;
20658         this.deltaY = iDeltaY;
20659     },
20660
20661     /**
20662      * Sets the drag element to the location of the mousedown or click event,
20663      * maintaining the cursor location relative to the location on the element
20664      * that was clicked.  Override this if you want to place the element in a
20665      * location other than where the cursor is.
20666      * @method setDragElPos
20667      * @param {int} iPageX the X coordinate of the mousedown or drag event
20668      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20669      */
20670     setDragElPos: function(iPageX, iPageY) {
20671         // the first time we do this, we are going to check to make sure
20672         // the element has css positioning
20673
20674         var el = this.getDragEl();
20675         this.alignElWithMouse(el, iPageX, iPageY);
20676     },
20677
20678     /**
20679      * Sets the element to the location of the mousedown or click event,
20680      * maintaining the cursor location relative to the location on the element
20681      * that was clicked.  Override this if you want to place the element in a
20682      * location other than where the cursor is.
20683      * @method alignElWithMouse
20684      * @param {HTMLElement} el the element to move
20685      * @param {int} iPageX the X coordinate of the mousedown or drag event
20686      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20687      */
20688     alignElWithMouse: function(el, iPageX, iPageY) {
20689         var oCoord = this.getTargetCoord(iPageX, iPageY);
20690         var fly = el.dom ? el : Roo.fly(el);
20691         if (!this.deltaSetXY) {
20692             var aCoord = [oCoord.x, oCoord.y];
20693             fly.setXY(aCoord);
20694             var newLeft = fly.getLeft(true);
20695             var newTop  = fly.getTop(true);
20696             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20697         } else {
20698             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20699         }
20700
20701         this.cachePosition(oCoord.x, oCoord.y);
20702         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20703         return oCoord;
20704     },
20705
20706     /**
20707      * Saves the most recent position so that we can reset the constraints and
20708      * tick marks on-demand.  We need to know this so that we can calculate the
20709      * number of pixels the element is offset from its original position.
20710      * @method cachePosition
20711      * @param iPageX the current x position (optional, this just makes it so we
20712      * don't have to look it up again)
20713      * @param iPageY the current y position (optional, this just makes it so we
20714      * don't have to look it up again)
20715      */
20716     cachePosition: function(iPageX, iPageY) {
20717         if (iPageX) {
20718             this.lastPageX = iPageX;
20719             this.lastPageY = iPageY;
20720         } else {
20721             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20722             this.lastPageX = aCoord[0];
20723             this.lastPageY = aCoord[1];
20724         }
20725     },
20726
20727     /**
20728      * Auto-scroll the window if the dragged object has been moved beyond the
20729      * visible window boundary.
20730      * @method autoScroll
20731      * @param {int} x the drag element's x position
20732      * @param {int} y the drag element's y position
20733      * @param {int} h the height of the drag element
20734      * @param {int} w the width of the drag element
20735      * @private
20736      */
20737     autoScroll: function(x, y, h, w) {
20738
20739         if (this.scroll) {
20740             // The client height
20741             var clientH = Roo.lib.Dom.getViewWidth();
20742
20743             // The client width
20744             var clientW = Roo.lib.Dom.getViewHeight();
20745
20746             // The amt scrolled down
20747             var st = this.DDM.getScrollTop();
20748
20749             // The amt scrolled right
20750             var sl = this.DDM.getScrollLeft();
20751
20752             // Location of the bottom of the element
20753             var bot = h + y;
20754
20755             // Location of the right of the element
20756             var right = w + x;
20757
20758             // The distance from the cursor to the bottom of the visible area,
20759             // adjusted so that we don't scroll if the cursor is beyond the
20760             // element drag constraints
20761             var toBot = (clientH + st - y - this.deltaY);
20762
20763             // The distance from the cursor to the right of the visible area
20764             var toRight = (clientW + sl - x - this.deltaX);
20765
20766
20767             // How close to the edge the cursor must be before we scroll
20768             // var thresh = (document.all) ? 100 : 40;
20769             var thresh = 40;
20770
20771             // How many pixels to scroll per autoscroll op.  This helps to reduce
20772             // clunky scrolling. IE is more sensitive about this ... it needs this
20773             // value to be higher.
20774             var scrAmt = (document.all) ? 80 : 30;
20775
20776             // Scroll down if we are near the bottom of the visible page and the
20777             // obj extends below the crease
20778             if ( bot > clientH && toBot < thresh ) {
20779                 window.scrollTo(sl, st + scrAmt);
20780             }
20781
20782             // Scroll up if the window is scrolled down and the top of the object
20783             // goes above the top border
20784             if ( y < st && st > 0 && y - st < thresh ) {
20785                 window.scrollTo(sl, st - scrAmt);
20786             }
20787
20788             // Scroll right if the obj is beyond the right border and the cursor is
20789             // near the border.
20790             if ( right > clientW && toRight < thresh ) {
20791                 window.scrollTo(sl + scrAmt, st);
20792             }
20793
20794             // Scroll left if the window has been scrolled to the right and the obj
20795             // extends past the left border
20796             if ( x < sl && sl > 0 && x - sl < thresh ) {
20797                 window.scrollTo(sl - scrAmt, st);
20798             }
20799         }
20800     },
20801
20802     /**
20803      * Finds the location the element should be placed if we want to move
20804      * it to where the mouse location less the click offset would place us.
20805      * @method getTargetCoord
20806      * @param {int} iPageX the X coordinate of the click
20807      * @param {int} iPageY the Y coordinate of the click
20808      * @return an object that contains the coordinates (Object.x and Object.y)
20809      * @private
20810      */
20811     getTargetCoord: function(iPageX, iPageY) {
20812
20813
20814         var x = iPageX - this.deltaX;
20815         var y = iPageY - this.deltaY;
20816
20817         if (this.constrainX) {
20818             if (x < this.minX) { x = this.minX; }
20819             if (x > this.maxX) { x = this.maxX; }
20820         }
20821
20822         if (this.constrainY) {
20823             if (y < this.minY) { y = this.minY; }
20824             if (y > this.maxY) { y = this.maxY; }
20825         }
20826
20827         x = this.getTick(x, this.xTicks);
20828         y = this.getTick(y, this.yTicks);
20829
20830
20831         return {x:x, y:y};
20832     },
20833
20834     /*
20835      * Sets up config options specific to this class. Overrides
20836      * Roo.dd.DragDrop, but all versions of this method through the
20837      * inheritance chain are called
20838      */
20839     applyConfig: function() {
20840         Roo.dd.DD.superclass.applyConfig.call(this);
20841         this.scroll = (this.config.scroll !== false);
20842     },
20843
20844     /*
20845      * Event that fires prior to the onMouseDown event.  Overrides
20846      * Roo.dd.DragDrop.
20847      */
20848     b4MouseDown: function(e) {
20849         // this.resetConstraints();
20850         this.autoOffset(e.getPageX(),
20851                             e.getPageY());
20852     },
20853
20854     /*
20855      * Event that fires prior to the onDrag event.  Overrides
20856      * Roo.dd.DragDrop.
20857      */
20858     b4Drag: function(e) {
20859         this.setDragElPos(e.getPageX(),
20860                             e.getPageY());
20861     },
20862
20863     toString: function() {
20864         return ("DD " + this.id);
20865     }
20866
20867     //////////////////////////////////////////////////////////////////////////
20868     // Debugging ygDragDrop events that can be overridden
20869     //////////////////////////////////////////////////////////////////////////
20870     /*
20871     startDrag: function(x, y) {
20872     },
20873
20874     onDrag: function(e) {
20875     },
20876
20877     onDragEnter: function(e, id) {
20878     },
20879
20880     onDragOver: function(e, id) {
20881     },
20882
20883     onDragOut: function(e, id) {
20884     },
20885
20886     onDragDrop: function(e, id) {
20887     },
20888
20889     endDrag: function(e) {
20890     }
20891
20892     */
20893
20894 });/*
20895  * Based on:
20896  * Ext JS Library 1.1.1
20897  * Copyright(c) 2006-2007, Ext JS, LLC.
20898  *
20899  * Originally Released Under LGPL - original licence link has changed is not relivant.
20900  *
20901  * Fork - LGPL
20902  * <script type="text/javascript">
20903  */
20904
20905 /**
20906  * @class Roo.dd.DDProxy
20907  * A DragDrop implementation that inserts an empty, bordered div into
20908  * the document that follows the cursor during drag operations.  At the time of
20909  * the click, the frame div is resized to the dimensions of the linked html
20910  * element, and moved to the exact location of the linked element.
20911  *
20912  * References to the "frame" element refer to the single proxy element that
20913  * was created to be dragged in place of all DDProxy elements on the
20914  * page.
20915  *
20916  * @extends Roo.dd.DD
20917  * @constructor
20918  * @param {String} id the id of the linked html element
20919  * @param {String} sGroup the group of related DragDrop objects
20920  * @param {object} config an object containing configurable attributes
20921  *                Valid properties for DDProxy in addition to those in DragDrop:
20922  *                   resizeFrame, centerFrame, dragElId
20923  */
20924 Roo.dd.DDProxy = function(id, sGroup, config) {
20925     if (id) {
20926         this.init(id, sGroup, config);
20927         this.initFrame();
20928     }
20929 };
20930
20931 /**
20932  * The default drag frame div id
20933  * @property Roo.dd.DDProxy.dragElId
20934  * @type String
20935  * @static
20936  */
20937 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20938
20939 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20940
20941     /**
20942      * By default we resize the drag frame to be the same size as the element
20943      * we want to drag (this is to get the frame effect).  We can turn it off
20944      * if we want a different behavior.
20945      * @property resizeFrame
20946      * @type boolean
20947      */
20948     resizeFrame: true,
20949
20950     /**
20951      * By default the frame is positioned exactly where the drag element is, so
20952      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20953      * you do not have constraints on the obj is to have the drag frame centered
20954      * around the cursor.  Set centerFrame to true for this effect.
20955      * @property centerFrame
20956      * @type boolean
20957      */
20958     centerFrame: false,
20959
20960     /**
20961      * Creates the proxy element if it does not yet exist
20962      * @method createFrame
20963      */
20964     createFrame: function() {
20965         var self = this;
20966         var body = document.body;
20967
20968         if (!body || !body.firstChild) {
20969             setTimeout( function() { self.createFrame(); }, 50 );
20970             return;
20971         }
20972
20973         var div = this.getDragEl();
20974
20975         if (!div) {
20976             div    = document.createElement("div");
20977             div.id = this.dragElId;
20978             var s  = div.style;
20979
20980             s.position   = "absolute";
20981             s.visibility = "hidden";
20982             s.cursor     = "move";
20983             s.border     = "2px solid #aaa";
20984             s.zIndex     = 999;
20985
20986             // appendChild can blow up IE if invoked prior to the window load event
20987             // while rendering a table.  It is possible there are other scenarios
20988             // that would cause this to happen as well.
20989             body.insertBefore(div, body.firstChild);
20990         }
20991     },
20992
20993     /**
20994      * Initialization for the drag frame element.  Must be called in the
20995      * constructor of all subclasses
20996      * @method initFrame
20997      */
20998     initFrame: function() {
20999         this.createFrame();
21000     },
21001
21002     applyConfig: function() {
21003         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21004
21005         this.resizeFrame = (this.config.resizeFrame !== false);
21006         this.centerFrame = (this.config.centerFrame);
21007         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21008     },
21009
21010     /**
21011      * Resizes the drag frame to the dimensions of the clicked object, positions
21012      * it over the object, and finally displays it
21013      * @method showFrame
21014      * @param {int} iPageX X click position
21015      * @param {int} iPageY Y click position
21016      * @private
21017      */
21018     showFrame: function(iPageX, iPageY) {
21019         var el = this.getEl();
21020         var dragEl = this.getDragEl();
21021         var s = dragEl.style;
21022
21023         this._resizeProxy();
21024
21025         if (this.centerFrame) {
21026             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21027                            Math.round(parseInt(s.height, 10)/2) );
21028         }
21029
21030         this.setDragElPos(iPageX, iPageY);
21031
21032         Roo.fly(dragEl).show();
21033     },
21034
21035     /**
21036      * The proxy is automatically resized to the dimensions of the linked
21037      * element when a drag is initiated, unless resizeFrame is set to false
21038      * @method _resizeProxy
21039      * @private
21040      */
21041     _resizeProxy: function() {
21042         if (this.resizeFrame) {
21043             var el = this.getEl();
21044             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21045         }
21046     },
21047
21048     // overrides Roo.dd.DragDrop
21049     b4MouseDown: function(e) {
21050         var x = e.getPageX();
21051         var y = e.getPageY();
21052         this.autoOffset(x, y);
21053         this.setDragElPos(x, y);
21054     },
21055
21056     // overrides Roo.dd.DragDrop
21057     b4StartDrag: function(x, y) {
21058         // show the drag frame
21059         this.showFrame(x, y);
21060     },
21061
21062     // overrides Roo.dd.DragDrop
21063     b4EndDrag: function(e) {
21064         Roo.fly(this.getDragEl()).hide();
21065     },
21066
21067     // overrides Roo.dd.DragDrop
21068     // By default we try to move the element to the last location of the frame.
21069     // This is so that the default behavior mirrors that of Roo.dd.DD.
21070     endDrag: function(e) {
21071
21072         var lel = this.getEl();
21073         var del = this.getDragEl();
21074
21075         // Show the drag frame briefly so we can get its position
21076         del.style.visibility = "";
21077
21078         this.beforeMove();
21079         // Hide the linked element before the move to get around a Safari
21080         // rendering bug.
21081         lel.style.visibility = "hidden";
21082         Roo.dd.DDM.moveToEl(lel, del);
21083         del.style.visibility = "hidden";
21084         lel.style.visibility = "";
21085
21086         this.afterDrag();
21087     },
21088
21089     beforeMove : function(){
21090
21091     },
21092
21093     afterDrag : function(){
21094
21095     },
21096
21097     toString: function() {
21098         return ("DDProxy " + this.id);
21099     }
21100
21101 });
21102 /*
21103  * Based on:
21104  * Ext JS Library 1.1.1
21105  * Copyright(c) 2006-2007, Ext JS, LLC.
21106  *
21107  * Originally Released Under LGPL - original licence link has changed is not relivant.
21108  *
21109  * Fork - LGPL
21110  * <script type="text/javascript">
21111  */
21112
21113  /**
21114  * @class Roo.dd.DDTarget
21115  * A DragDrop implementation that does not move, but can be a drop
21116  * target.  You would get the same result by simply omitting implementation
21117  * for the event callbacks, but this way we reduce the processing cost of the
21118  * event listener and the callbacks.
21119  * @extends Roo.dd.DragDrop
21120  * @constructor
21121  * @param {String} id the id of the element that is a drop target
21122  * @param {String} sGroup the group of related DragDrop objects
21123  * @param {object} config an object containing configurable attributes
21124  *                 Valid properties for DDTarget in addition to those in
21125  *                 DragDrop:
21126  *                    none
21127  */
21128 Roo.dd.DDTarget = function(id, sGroup, config) {
21129     if (id) {
21130         this.initTarget(id, sGroup, config);
21131     }
21132     if (config && (config.listeners || config.events)) { 
21133         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21134             listeners : config.listeners || {}, 
21135             events : config.events || {} 
21136         });    
21137     }
21138 };
21139
21140 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21141 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21142     toString: function() {
21143         return ("DDTarget " + this.id);
21144     }
21145 });
21146 /*
21147  * Based on:
21148  * Ext JS Library 1.1.1
21149  * Copyright(c) 2006-2007, Ext JS, LLC.
21150  *
21151  * Originally Released Under LGPL - original licence link has changed is not relivant.
21152  *
21153  * Fork - LGPL
21154  * <script type="text/javascript">
21155  */
21156  
21157
21158 /**
21159  * @class Roo.dd.ScrollManager
21160  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21161  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21162  * @singleton
21163  */
21164 Roo.dd.ScrollManager = function(){
21165     var ddm = Roo.dd.DragDropMgr;
21166     var els = {};
21167     var dragEl = null;
21168     var proc = {};
21169     
21170     
21171     
21172     var onStop = function(e){
21173         dragEl = null;
21174         clearProc();
21175     };
21176     
21177     var triggerRefresh = function(){
21178         if(ddm.dragCurrent){
21179              ddm.refreshCache(ddm.dragCurrent.groups);
21180         }
21181     };
21182     
21183     var doScroll = function(){
21184         if(ddm.dragCurrent){
21185             var dds = Roo.dd.ScrollManager;
21186             if(!dds.animate){
21187                 if(proc.el.scroll(proc.dir, dds.increment)){
21188                     triggerRefresh();
21189                 }
21190             }else{
21191                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21192             }
21193         }
21194     };
21195     
21196     var clearProc = function(){
21197         if(proc.id){
21198             clearInterval(proc.id);
21199         }
21200         proc.id = 0;
21201         proc.el = null;
21202         proc.dir = "";
21203     };
21204     
21205     var startProc = function(el, dir){
21206          Roo.log('scroll startproc');
21207         clearProc();
21208         proc.el = el;
21209         proc.dir = dir;
21210         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21211     };
21212     
21213     var onFire = function(e, isDrop){
21214        
21215         if(isDrop || !ddm.dragCurrent){ return; }
21216         var dds = Roo.dd.ScrollManager;
21217         if(!dragEl || dragEl != ddm.dragCurrent){
21218             dragEl = ddm.dragCurrent;
21219             // refresh regions on drag start
21220             dds.refreshCache();
21221         }
21222         
21223         var xy = Roo.lib.Event.getXY(e);
21224         var pt = new Roo.lib.Point(xy[0], xy[1]);
21225         for(var id in els){
21226             var el = els[id], r = el._region;
21227             if(r && r.contains(pt) && el.isScrollable()){
21228                 if(r.bottom - pt.y <= dds.thresh){
21229                     if(proc.el != el){
21230                         startProc(el, "down");
21231                     }
21232                     return;
21233                 }else if(r.right - pt.x <= dds.thresh){
21234                     if(proc.el != el){
21235                         startProc(el, "left");
21236                     }
21237                     return;
21238                 }else if(pt.y - r.top <= dds.thresh){
21239                     if(proc.el != el){
21240                         startProc(el, "up");
21241                     }
21242                     return;
21243                 }else if(pt.x - r.left <= dds.thresh){
21244                     if(proc.el != el){
21245                         startProc(el, "right");
21246                     }
21247                     return;
21248                 }
21249             }
21250         }
21251         clearProc();
21252     };
21253     
21254     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21255     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21256     
21257     return {
21258         /**
21259          * Registers new overflow element(s) to auto scroll
21260          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21261          */
21262         register : function(el){
21263             if(el instanceof Array){
21264                 for(var i = 0, len = el.length; i < len; i++) {
21265                         this.register(el[i]);
21266                 }
21267             }else{
21268                 el = Roo.get(el);
21269                 els[el.id] = el;
21270             }
21271             Roo.dd.ScrollManager.els = els;
21272         },
21273         
21274         /**
21275          * Unregisters overflow element(s) so they are no longer scrolled
21276          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21277          */
21278         unregister : function(el){
21279             if(el instanceof Array){
21280                 for(var i = 0, len = el.length; i < len; i++) {
21281                         this.unregister(el[i]);
21282                 }
21283             }else{
21284                 el = Roo.get(el);
21285                 delete els[el.id];
21286             }
21287         },
21288         
21289         /**
21290          * The number of pixels from the edge of a container the pointer needs to be to 
21291          * trigger scrolling (defaults to 25)
21292          * @type Number
21293          */
21294         thresh : 25,
21295         
21296         /**
21297          * The number of pixels to scroll in each scroll increment (defaults to 50)
21298          * @type Number
21299          */
21300         increment : 100,
21301         
21302         /**
21303          * The frequency of scrolls in milliseconds (defaults to 500)
21304          * @type Number
21305          */
21306         frequency : 500,
21307         
21308         /**
21309          * True to animate the scroll (defaults to true)
21310          * @type Boolean
21311          */
21312         animate: true,
21313         
21314         /**
21315          * The animation duration in seconds - 
21316          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21317          * @type Number
21318          */
21319         animDuration: .4,
21320         
21321         /**
21322          * Manually trigger a cache refresh.
21323          */
21324         refreshCache : function(){
21325             for(var id in els){
21326                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21327                     els[id]._region = els[id].getRegion();
21328                 }
21329             }
21330         }
21331     };
21332 }();/*
21333  * Based on:
21334  * Ext JS Library 1.1.1
21335  * Copyright(c) 2006-2007, Ext JS, LLC.
21336  *
21337  * Originally Released Under LGPL - original licence link has changed is not relivant.
21338  *
21339  * Fork - LGPL
21340  * <script type="text/javascript">
21341  */
21342  
21343
21344 /**
21345  * @class Roo.dd.Registry
21346  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21347  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21348  * @singleton
21349  */
21350 Roo.dd.Registry = function(){
21351     var elements = {}; 
21352     var handles = {}; 
21353     var autoIdSeed = 0;
21354
21355     var getId = function(el, autogen){
21356         if(typeof el == "string"){
21357             return el;
21358         }
21359         var id = el.id;
21360         if(!id && autogen !== false){
21361             id = "roodd-" + (++autoIdSeed);
21362             el.id = id;
21363         }
21364         return id;
21365     };
21366     
21367     return {
21368     /**
21369      * Register a drag drop element
21370      * @param {String|HTMLElement} element The id or DOM node to register
21371      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21372      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21373      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21374      * populated in the data object (if applicable):
21375      * <pre>
21376 Value      Description<br />
21377 ---------  ------------------------------------------<br />
21378 handles    Array of DOM nodes that trigger dragging<br />
21379            for the element being registered<br />
21380 isHandle   True if the element passed in triggers<br />
21381            dragging itself, else false
21382 </pre>
21383      */
21384         register : function(el, data){
21385             data = data || {};
21386             if(typeof el == "string"){
21387                 el = document.getElementById(el);
21388             }
21389             data.ddel = el;
21390             elements[getId(el)] = data;
21391             if(data.isHandle !== false){
21392                 handles[data.ddel.id] = data;
21393             }
21394             if(data.handles){
21395                 var hs = data.handles;
21396                 for(var i = 0, len = hs.length; i < len; i++){
21397                         handles[getId(hs[i])] = data;
21398                 }
21399             }
21400         },
21401
21402     /**
21403      * Unregister a drag drop element
21404      * @param {String|HTMLElement}  element The id or DOM node to unregister
21405      */
21406         unregister : function(el){
21407             var id = getId(el, false);
21408             var data = elements[id];
21409             if(data){
21410                 delete elements[id];
21411                 if(data.handles){
21412                     var hs = data.handles;
21413                     for(var i = 0, len = hs.length; i < len; i++){
21414                         delete handles[getId(hs[i], false)];
21415                     }
21416                 }
21417             }
21418         },
21419
21420     /**
21421      * Returns the handle registered for a DOM Node by id
21422      * @param {String|HTMLElement} id The DOM node or id to look up
21423      * @return {Object} handle The custom handle data
21424      */
21425         getHandle : function(id){
21426             if(typeof id != "string"){ // must be element?
21427                 id = id.id;
21428             }
21429             return handles[id];
21430         },
21431
21432     /**
21433      * Returns the handle that is registered for the DOM node that is the target of the event
21434      * @param {Event} e The event
21435      * @return {Object} handle The custom handle data
21436      */
21437         getHandleFromEvent : function(e){
21438             var t = Roo.lib.Event.getTarget(e);
21439             return t ? handles[t.id] : null;
21440         },
21441
21442     /**
21443      * Returns a custom data object that is registered for a DOM node by id
21444      * @param {String|HTMLElement} id The DOM node or id to look up
21445      * @return {Object} data The custom data
21446      */
21447         getTarget : function(id){
21448             if(typeof id != "string"){ // must be element?
21449                 id = id.id;
21450             }
21451             return elements[id];
21452         },
21453
21454     /**
21455      * Returns a custom data object that is registered for the DOM node that is the target of the event
21456      * @param {Event} e The event
21457      * @return {Object} data The custom data
21458      */
21459         getTargetFromEvent : function(e){
21460             var t = Roo.lib.Event.getTarget(e);
21461             return t ? elements[t.id] || handles[t.id] : null;
21462         }
21463     };
21464 }();/*
21465  * Based on:
21466  * Ext JS Library 1.1.1
21467  * Copyright(c) 2006-2007, Ext JS, LLC.
21468  *
21469  * Originally Released Under LGPL - original licence link has changed is not relivant.
21470  *
21471  * Fork - LGPL
21472  * <script type="text/javascript">
21473  */
21474  
21475
21476 /**
21477  * @class Roo.dd.StatusProxy
21478  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21479  * default drag proxy used by all Roo.dd components.
21480  * @constructor
21481  * @param {Object} config
21482  */
21483 Roo.dd.StatusProxy = function(config){
21484     Roo.apply(this, config);
21485     this.id = this.id || Roo.id();
21486     this.el = new Roo.Layer({
21487         dh: {
21488             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21489                 {tag: "div", cls: "x-dd-drop-icon"},
21490                 {tag: "div", cls: "x-dd-drag-ghost"}
21491             ]
21492         }, 
21493         shadow: !config || config.shadow !== false
21494     });
21495     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21496     this.dropStatus = this.dropNotAllowed;
21497 };
21498
21499 Roo.dd.StatusProxy.prototype = {
21500     /**
21501      * @cfg {String} dropAllowed
21502      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21503      */
21504     dropAllowed : "x-dd-drop-ok",
21505     /**
21506      * @cfg {String} dropNotAllowed
21507      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21508      */
21509     dropNotAllowed : "x-dd-drop-nodrop",
21510
21511     /**
21512      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21513      * over the current target element.
21514      * @param {String} cssClass The css class for the new drop status indicator image
21515      */
21516     setStatus : function(cssClass){
21517         cssClass = cssClass || this.dropNotAllowed;
21518         if(this.dropStatus != cssClass){
21519             this.el.replaceClass(this.dropStatus, cssClass);
21520             this.dropStatus = cssClass;
21521         }
21522     },
21523
21524     /**
21525      * Resets the status indicator to the default dropNotAllowed value
21526      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21527      */
21528     reset : function(clearGhost){
21529         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21530         this.dropStatus = this.dropNotAllowed;
21531         if(clearGhost){
21532             this.ghost.update("");
21533         }
21534     },
21535
21536     /**
21537      * Updates the contents of the ghost element
21538      * @param {String} html The html that will replace the current innerHTML of the ghost element
21539      */
21540     update : function(html){
21541         if(typeof html == "string"){
21542             this.ghost.update(html);
21543         }else{
21544             this.ghost.update("");
21545             html.style.margin = "0";
21546             this.ghost.dom.appendChild(html);
21547         }
21548         // ensure float = none set?? cant remember why though.
21549         var el = this.ghost.dom.firstChild;
21550                 if(el){
21551                         Roo.fly(el).setStyle('float', 'none');
21552                 }
21553     },
21554     
21555     /**
21556      * Returns the underlying proxy {@link Roo.Layer}
21557      * @return {Roo.Layer} el
21558     */
21559     getEl : function(){
21560         return this.el;
21561     },
21562
21563     /**
21564      * Returns the ghost element
21565      * @return {Roo.Element} el
21566      */
21567     getGhost : function(){
21568         return this.ghost;
21569     },
21570
21571     /**
21572      * Hides the proxy
21573      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21574      */
21575     hide : function(clear){
21576         this.el.hide();
21577         if(clear){
21578             this.reset(true);
21579         }
21580     },
21581
21582     /**
21583      * Stops the repair animation if it's currently running
21584      */
21585     stop : function(){
21586         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21587             this.anim.stop();
21588         }
21589     },
21590
21591     /**
21592      * Displays this proxy
21593      */
21594     show : function(){
21595         this.el.show();
21596     },
21597
21598     /**
21599      * Force the Layer to sync its shadow and shim positions to the element
21600      */
21601     sync : function(){
21602         this.el.sync();
21603     },
21604
21605     /**
21606      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21607      * invalid drop operation by the item being dragged.
21608      * @param {Array} xy The XY position of the element ([x, y])
21609      * @param {Function} callback The function to call after the repair is complete
21610      * @param {Object} scope The scope in which to execute the callback
21611      */
21612     repair : function(xy, callback, scope){
21613         this.callback = callback;
21614         this.scope = scope;
21615         if(xy && this.animRepair !== false){
21616             this.el.addClass("x-dd-drag-repair");
21617             this.el.hideUnders(true);
21618             this.anim = this.el.shift({
21619                 duration: this.repairDuration || .5,
21620                 easing: 'easeOut',
21621                 xy: xy,
21622                 stopFx: true,
21623                 callback: this.afterRepair,
21624                 scope: this
21625             });
21626         }else{
21627             this.afterRepair();
21628         }
21629     },
21630
21631     // private
21632     afterRepair : function(){
21633         this.hide(true);
21634         if(typeof this.callback == "function"){
21635             this.callback.call(this.scope || this);
21636         }
21637         this.callback = null;
21638         this.scope = null;
21639     }
21640 };/*
21641  * Based on:
21642  * Ext JS Library 1.1.1
21643  * Copyright(c) 2006-2007, Ext JS, LLC.
21644  *
21645  * Originally Released Under LGPL - original licence link has changed is not relivant.
21646  *
21647  * Fork - LGPL
21648  * <script type="text/javascript">
21649  */
21650
21651 /**
21652  * @class Roo.dd.DragSource
21653  * @extends Roo.dd.DDProxy
21654  * A simple class that provides the basic implementation needed to make any element draggable.
21655  * @constructor
21656  * @param {String/HTMLElement/Element} el The container element
21657  * @param {Object} config
21658  */
21659 Roo.dd.DragSource = function(el, config){
21660     this.el = Roo.get(el);
21661     this.dragData = {};
21662     
21663     Roo.apply(this, config);
21664     
21665     if(!this.proxy){
21666         this.proxy = new Roo.dd.StatusProxy();
21667     }
21668
21669     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21670           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21671     
21672     this.dragging = false;
21673 };
21674
21675 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21676     /**
21677      * @cfg {String} dropAllowed
21678      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21679      */
21680     dropAllowed : "x-dd-drop-ok",
21681     /**
21682      * @cfg {String} dropNotAllowed
21683      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21684      */
21685     dropNotAllowed : "x-dd-drop-nodrop",
21686
21687     /**
21688      * Returns the data object associated with this drag source
21689      * @return {Object} data An object containing arbitrary data
21690      */
21691     getDragData : function(e){
21692         return this.dragData;
21693     },
21694
21695     // private
21696     onDragEnter : function(e, id){
21697         var target = Roo.dd.DragDropMgr.getDDById(id);
21698         this.cachedTarget = target;
21699         if(this.beforeDragEnter(target, e, id) !== false){
21700             if(target.isNotifyTarget){
21701                 var status = target.notifyEnter(this, e, this.dragData);
21702                 this.proxy.setStatus(status);
21703             }else{
21704                 this.proxy.setStatus(this.dropAllowed);
21705             }
21706             
21707             if(this.afterDragEnter){
21708                 /**
21709                  * An empty function by default, but provided so that you can perform a custom action
21710                  * when the dragged item enters the drop target by providing an implementation.
21711                  * @param {Roo.dd.DragDrop} target The drop target
21712                  * @param {Event} e The event object
21713                  * @param {String} id The id of the dragged element
21714                  * @method afterDragEnter
21715                  */
21716                 this.afterDragEnter(target, e, id);
21717             }
21718         }
21719     },
21720
21721     /**
21722      * An empty function by default, but provided so that you can perform a custom action
21723      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21724      * @param {Roo.dd.DragDrop} target The drop target
21725      * @param {Event} e The event object
21726      * @param {String} id The id of the dragged element
21727      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21728      */
21729     beforeDragEnter : function(target, e, id){
21730         return true;
21731     },
21732
21733     // private
21734     alignElWithMouse: function() {
21735         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21736         this.proxy.sync();
21737     },
21738
21739     // private
21740     onDragOver : function(e, id){
21741         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21742         if(this.beforeDragOver(target, e, id) !== false){
21743             if(target.isNotifyTarget){
21744                 var status = target.notifyOver(this, e, this.dragData);
21745                 this.proxy.setStatus(status);
21746             }
21747
21748             if(this.afterDragOver){
21749                 /**
21750                  * An empty function by default, but provided so that you can perform a custom action
21751                  * while the dragged item is over the drop target by providing an implementation.
21752                  * @param {Roo.dd.DragDrop} target The drop target
21753                  * @param {Event} e The event object
21754                  * @param {String} id The id of the dragged element
21755                  * @method afterDragOver
21756                  */
21757                 this.afterDragOver(target, e, id);
21758             }
21759         }
21760     },
21761
21762     /**
21763      * An empty function by default, but provided so that you can perform a custom action
21764      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21765      * @param {Roo.dd.DragDrop} target The drop target
21766      * @param {Event} e The event object
21767      * @param {String} id The id of the dragged element
21768      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21769      */
21770     beforeDragOver : function(target, e, id){
21771         return true;
21772     },
21773
21774     // private
21775     onDragOut : function(e, id){
21776         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21777         if(this.beforeDragOut(target, e, id) !== false){
21778             if(target.isNotifyTarget){
21779                 target.notifyOut(this, e, this.dragData);
21780             }
21781             this.proxy.reset();
21782             if(this.afterDragOut){
21783                 /**
21784                  * An empty function by default, but provided so that you can perform a custom action
21785                  * after the dragged item is dragged out of the target without dropping.
21786                  * @param {Roo.dd.DragDrop} target The drop target
21787                  * @param {Event} e The event object
21788                  * @param {String} id The id of the dragged element
21789                  * @method afterDragOut
21790                  */
21791                 this.afterDragOut(target, e, id);
21792             }
21793         }
21794         this.cachedTarget = null;
21795     },
21796
21797     /**
21798      * An empty function by default, but provided so that you can perform a custom action before the dragged
21799      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21800      * @param {Roo.dd.DragDrop} target The drop target
21801      * @param {Event} e The event object
21802      * @param {String} id The id of the dragged element
21803      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21804      */
21805     beforeDragOut : function(target, e, id){
21806         return true;
21807     },
21808     
21809     // private
21810     onDragDrop : function(e, id){
21811         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21812         if(this.beforeDragDrop(target, e, id) !== false){
21813             if(target.isNotifyTarget){
21814                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21815                     this.onValidDrop(target, e, id);
21816                 }else{
21817                     this.onInvalidDrop(target, e, id);
21818                 }
21819             }else{
21820                 this.onValidDrop(target, e, id);
21821             }
21822             
21823             if(this.afterDragDrop){
21824                 /**
21825                  * An empty function by default, but provided so that you can perform a custom action
21826                  * after a valid drag drop has occurred by providing an implementation.
21827                  * @param {Roo.dd.DragDrop} target The drop target
21828                  * @param {Event} e The event object
21829                  * @param {String} id The id of the dropped element
21830                  * @method afterDragDrop
21831                  */
21832                 this.afterDragDrop(target, e, id);
21833             }
21834         }
21835         delete this.cachedTarget;
21836     },
21837
21838     /**
21839      * An empty function by default, but provided so that you can perform a custom action before the dragged
21840      * item is dropped onto the target and optionally cancel the onDragDrop.
21841      * @param {Roo.dd.DragDrop} target The drop target
21842      * @param {Event} e The event object
21843      * @param {String} id The id of the dragged element
21844      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21845      */
21846     beforeDragDrop : function(target, e, id){
21847         return true;
21848     },
21849
21850     // private
21851     onValidDrop : function(target, e, id){
21852         this.hideProxy();
21853         if(this.afterValidDrop){
21854             /**
21855              * An empty function by default, but provided so that you can perform a custom action
21856              * after a valid drop has occurred by providing an implementation.
21857              * @param {Object} target The target DD 
21858              * @param {Event} e The event object
21859              * @param {String} id The id of the dropped element
21860              * @method afterInvalidDrop
21861              */
21862             this.afterValidDrop(target, e, id);
21863         }
21864     },
21865
21866     // private
21867     getRepairXY : function(e, data){
21868         return this.el.getXY();  
21869     },
21870
21871     // private
21872     onInvalidDrop : function(target, e, id){
21873         this.beforeInvalidDrop(target, e, id);
21874         if(this.cachedTarget){
21875             if(this.cachedTarget.isNotifyTarget){
21876                 this.cachedTarget.notifyOut(this, e, this.dragData);
21877             }
21878             this.cacheTarget = null;
21879         }
21880         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21881
21882         if(this.afterInvalidDrop){
21883             /**
21884              * An empty function by default, but provided so that you can perform a custom action
21885              * after an invalid drop has occurred by providing an implementation.
21886              * @param {Event} e The event object
21887              * @param {String} id The id of the dropped element
21888              * @method afterInvalidDrop
21889              */
21890             this.afterInvalidDrop(e, id);
21891         }
21892     },
21893
21894     // private
21895     afterRepair : function(){
21896         if(Roo.enableFx){
21897             this.el.highlight(this.hlColor || "c3daf9");
21898         }
21899         this.dragging = false;
21900     },
21901
21902     /**
21903      * An empty function by default, but provided so that you can perform a custom action after an invalid
21904      * drop has occurred.
21905      * @param {Roo.dd.DragDrop} target The drop target
21906      * @param {Event} e The event object
21907      * @param {String} id The id of the dragged element
21908      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21909      */
21910     beforeInvalidDrop : function(target, e, id){
21911         return true;
21912     },
21913
21914     // private
21915     handleMouseDown : function(e){
21916         if(this.dragging) {
21917             return;
21918         }
21919         var data = this.getDragData(e);
21920         if(data && this.onBeforeDrag(data, e) !== false){
21921             this.dragData = data;
21922             this.proxy.stop();
21923             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21924         } 
21925     },
21926
21927     /**
21928      * An empty function by default, but provided so that you can perform a custom action before the initial
21929      * drag event begins and optionally cancel it.
21930      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21931      * @param {Event} e The event object
21932      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21933      */
21934     onBeforeDrag : function(data, e){
21935         return true;
21936     },
21937
21938     /**
21939      * An empty function by default, but provided so that you can perform a custom action once the initial
21940      * drag event has begun.  The drag cannot be canceled from this function.
21941      * @param {Number} x The x position of the click on the dragged object
21942      * @param {Number} y The y position of the click on the dragged object
21943      */
21944     onStartDrag : Roo.emptyFn,
21945
21946     // private - YUI override
21947     startDrag : function(x, y){
21948         this.proxy.reset();
21949         this.dragging = true;
21950         this.proxy.update("");
21951         this.onInitDrag(x, y);
21952         this.proxy.show();
21953     },
21954
21955     // private
21956     onInitDrag : function(x, y){
21957         var clone = this.el.dom.cloneNode(true);
21958         clone.id = Roo.id(); // prevent duplicate ids
21959         this.proxy.update(clone);
21960         this.onStartDrag(x, y);
21961         return true;
21962     },
21963
21964     /**
21965      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21966      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21967      */
21968     getProxy : function(){
21969         return this.proxy;  
21970     },
21971
21972     /**
21973      * Hides the drag source's {@link Roo.dd.StatusProxy}
21974      */
21975     hideProxy : function(){
21976         this.proxy.hide();  
21977         this.proxy.reset(true);
21978         this.dragging = false;
21979     },
21980
21981     // private
21982     triggerCacheRefresh : function(){
21983         Roo.dd.DDM.refreshCache(this.groups);
21984     },
21985
21986     // private - override to prevent hiding
21987     b4EndDrag: function(e) {
21988     },
21989
21990     // private - override to prevent moving
21991     endDrag : function(e){
21992         this.onEndDrag(this.dragData, e);
21993     },
21994
21995     // private
21996     onEndDrag : function(data, e){
21997     },
21998     
21999     // private - pin to cursor
22000     autoOffset : function(x, y) {
22001         this.setDelta(-12, -20);
22002     }    
22003 });/*
22004  * Based on:
22005  * Ext JS Library 1.1.1
22006  * Copyright(c) 2006-2007, Ext JS, LLC.
22007  *
22008  * Originally Released Under LGPL - original licence link has changed is not relivant.
22009  *
22010  * Fork - LGPL
22011  * <script type="text/javascript">
22012  */
22013
22014
22015 /**
22016  * @class Roo.dd.DropTarget
22017  * @extends Roo.dd.DDTarget
22018  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22019  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22020  * @constructor
22021  * @param {String/HTMLElement/Element} el The container element
22022  * @param {Object} config
22023  */
22024 Roo.dd.DropTarget = function(el, config){
22025     this.el = Roo.get(el);
22026     
22027     var listeners = false; ;
22028     if (config && config.listeners) {
22029         listeners= config.listeners;
22030         delete config.listeners;
22031     }
22032     Roo.apply(this, config);
22033     
22034     if(this.containerScroll){
22035         Roo.dd.ScrollManager.register(this.el);
22036     }
22037     this.addEvents( {
22038          /**
22039          * @scope Roo.dd.DropTarget
22040          */
22041          
22042          /**
22043          * @event enter
22044          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22045          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22046          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22047          * 
22048          * IMPORTANT : it should set this.overClass and this.dropAllowed
22049          * 
22050          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22051          * @param {Event} e The event
22052          * @param {Object} data An object containing arbitrary data supplied by the drag source
22053          */
22054         "enter" : true,
22055         
22056          /**
22057          * @event over
22058          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22059          * This method will be called on every mouse movement while the drag source is over the drop target.
22060          * This default implementation simply returns the dropAllowed config value.
22061          * 
22062          * IMPORTANT : it should set this.dropAllowed
22063          * 
22064          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22065          * @param {Event} e The event
22066          * @param {Object} data An object containing arbitrary data supplied by the drag source
22067          
22068          */
22069         "over" : true,
22070         /**
22071          * @event out
22072          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22073          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22074          * overClass (if any) from the drop element.
22075          * 
22076          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22077          * @param {Event} e The event
22078          * @param {Object} data An object containing arbitrary data supplied by the drag source
22079          */
22080          "out" : true,
22081          
22082         /**
22083          * @event drop
22084          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22085          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22086          * implementation that does something to process the drop event and returns true so that the drag source's
22087          * repair action does not run.
22088          * 
22089          * IMPORTANT : it should set this.success
22090          * 
22091          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22092          * @param {Event} e The event
22093          * @param {Object} data An object containing arbitrary data supplied by the drag source
22094         */
22095          "drop" : true
22096     });
22097             
22098      
22099     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22100         this.el.dom, 
22101         this.ddGroup || this.group,
22102         {
22103             isTarget: true,
22104             listeners : listeners || {} 
22105            
22106         
22107         }
22108     );
22109
22110 };
22111
22112 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22113     /**
22114      * @cfg {String} overClass
22115      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22116      */
22117      /**
22118      * @cfg {String} ddGroup
22119      * The drag drop group to handle drop events for
22120      */
22121      
22122     /**
22123      * @cfg {String} dropAllowed
22124      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22125      */
22126     dropAllowed : "x-dd-drop-ok",
22127     /**
22128      * @cfg {String} dropNotAllowed
22129      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22130      */
22131     dropNotAllowed : "x-dd-drop-nodrop",
22132     /**
22133      * @cfg {boolean} success
22134      * set this after drop listener.. 
22135      */
22136     success : false,
22137     /**
22138      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22139      * if the drop point is valid for over/enter..
22140      */
22141     valid : false,
22142     // private
22143     isTarget : true,
22144
22145     // private
22146     isNotifyTarget : true,
22147     
22148     /**
22149      * @hide
22150      */
22151     notifyEnter : function(dd, e, data)
22152     {
22153         this.valid = true;
22154         this.fireEvent('enter', dd, e, data);
22155         if(this.overClass){
22156             this.el.addClass(this.overClass);
22157         }
22158         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22159             this.valid ? this.dropAllowed : this.dropNotAllowed
22160         );
22161     },
22162
22163     /**
22164      * @hide
22165      */
22166     notifyOver : function(dd, e, data)
22167     {
22168         this.valid = true;
22169         this.fireEvent('over', dd, e, data);
22170         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22171             this.valid ? this.dropAllowed : this.dropNotAllowed
22172         );
22173     },
22174
22175     /**
22176      * @hide
22177      */
22178     notifyOut : function(dd, e, data)
22179     {
22180         this.fireEvent('out', dd, e, data);
22181         if(this.overClass){
22182             this.el.removeClass(this.overClass);
22183         }
22184     },
22185
22186     /**
22187      * @hide
22188      */
22189     notifyDrop : function(dd, e, data)
22190     {
22191         this.success = false;
22192         this.fireEvent('drop', dd, e, data);
22193         return this.success;
22194     }
22195 });/*
22196  * Based on:
22197  * Ext JS Library 1.1.1
22198  * Copyright(c) 2006-2007, Ext JS, LLC.
22199  *
22200  * Originally Released Under LGPL - original licence link has changed is not relivant.
22201  *
22202  * Fork - LGPL
22203  * <script type="text/javascript">
22204  */
22205
22206
22207 /**
22208  * @class Roo.dd.DragZone
22209  * @extends Roo.dd.DragSource
22210  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22211  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22212  * @constructor
22213  * @param {String/HTMLElement/Element} el The container element
22214  * @param {Object} config
22215  */
22216 Roo.dd.DragZone = function(el, config){
22217     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22218     if(this.containerScroll){
22219         Roo.dd.ScrollManager.register(this.el);
22220     }
22221 };
22222
22223 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22224     /**
22225      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22226      * for auto scrolling during drag operations.
22227      */
22228     /**
22229      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22230      * method after a failed drop (defaults to "c3daf9" - light blue)
22231      */
22232
22233     /**
22234      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22235      * for a valid target to drag based on the mouse down. Override this method
22236      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22237      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22238      * @param {EventObject} e The mouse down event
22239      * @return {Object} The dragData
22240      */
22241     getDragData : function(e){
22242         return Roo.dd.Registry.getHandleFromEvent(e);
22243     },
22244     
22245     /**
22246      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22247      * this.dragData.ddel
22248      * @param {Number} x The x position of the click on the dragged object
22249      * @param {Number} y The y position of the click on the dragged object
22250      * @return {Boolean} true to continue the drag, false to cancel
22251      */
22252     onInitDrag : function(x, y){
22253         this.proxy.update(this.dragData.ddel.cloneNode(true));
22254         this.onStartDrag(x, y);
22255         return true;
22256     },
22257     
22258     /**
22259      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22260      */
22261     afterRepair : function(){
22262         if(Roo.enableFx){
22263             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22264         }
22265         this.dragging = false;
22266     },
22267
22268     /**
22269      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22270      * the XY of this.dragData.ddel
22271      * @param {EventObject} e The mouse up event
22272      * @return {Array} The xy location (e.g. [100, 200])
22273      */
22274     getRepairXY : function(e){
22275         return Roo.Element.fly(this.dragData.ddel).getXY();  
22276     }
22277 });/*
22278  * Based on:
22279  * Ext JS Library 1.1.1
22280  * Copyright(c) 2006-2007, Ext JS, LLC.
22281  *
22282  * Originally Released Under LGPL - original licence link has changed is not relivant.
22283  *
22284  * Fork - LGPL
22285  * <script type="text/javascript">
22286  */
22287 /**
22288  * @class Roo.dd.DropZone
22289  * @extends Roo.dd.DropTarget
22290  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22291  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22292  * @constructor
22293  * @param {String/HTMLElement/Element} el The container element
22294  * @param {Object} config
22295  */
22296 Roo.dd.DropZone = function(el, config){
22297     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22298 };
22299
22300 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22301     /**
22302      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22303      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22304      * provide your own custom lookup.
22305      * @param {Event} e The event
22306      * @return {Object} data The custom data
22307      */
22308     getTargetFromEvent : function(e){
22309         return Roo.dd.Registry.getTargetFromEvent(e);
22310     },
22311
22312     /**
22313      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22314      * that it has registered.  This method has no default implementation and should be overridden to provide
22315      * node-specific processing if necessary.
22316      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22317      * {@link #getTargetFromEvent} for this node)
22318      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22319      * @param {Event} e The event
22320      * @param {Object} data An object containing arbitrary data supplied by the drag source
22321      */
22322     onNodeEnter : function(n, dd, e, data){
22323         
22324     },
22325
22326     /**
22327      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22328      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22329      * overridden to provide the proper feedback.
22330      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22331      * {@link #getTargetFromEvent} for this node)
22332      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22333      * @param {Event} e The event
22334      * @param {Object} data An object containing arbitrary data supplied by the drag source
22335      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22336      * underlying {@link Roo.dd.StatusProxy} can be updated
22337      */
22338     onNodeOver : function(n, dd, e, data){
22339         return this.dropAllowed;
22340     },
22341
22342     /**
22343      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22344      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22345      * node-specific processing if necessary.
22346      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22347      * {@link #getTargetFromEvent} for this node)
22348      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22349      * @param {Event} e The event
22350      * @param {Object} data An object containing arbitrary data supplied by the drag source
22351      */
22352     onNodeOut : function(n, dd, e, data){
22353         
22354     },
22355
22356     /**
22357      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22358      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22359      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22360      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22361      * {@link #getTargetFromEvent} for this node)
22362      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22363      * @param {Event} e The event
22364      * @param {Object} data An object containing arbitrary data supplied by the drag source
22365      * @return {Boolean} True if the drop was valid, else false
22366      */
22367     onNodeDrop : function(n, dd, e, data){
22368         return false;
22369     },
22370
22371     /**
22372      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22373      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22374      * it should be overridden to provide the proper feedback if necessary.
22375      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22376      * @param {Event} e The event
22377      * @param {Object} data An object containing arbitrary data supplied by the drag source
22378      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22379      * underlying {@link Roo.dd.StatusProxy} can be updated
22380      */
22381     onContainerOver : function(dd, e, data){
22382         return this.dropNotAllowed;
22383     },
22384
22385     /**
22386      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22387      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22388      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22389      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22391      * @param {Event} e The event
22392      * @param {Object} data An object containing arbitrary data supplied by the drag source
22393      * @return {Boolean} True if the drop was valid, else false
22394      */
22395     onContainerDrop : function(dd, e, data){
22396         return false;
22397     },
22398
22399     /**
22400      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22401      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22402      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22403      * you should override this method and provide a custom implementation.
22404      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22405      * @param {Event} e The event
22406      * @param {Object} data An object containing arbitrary data supplied by the drag source
22407      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22408      * underlying {@link Roo.dd.StatusProxy} can be updated
22409      */
22410     notifyEnter : function(dd, e, data){
22411         return this.dropNotAllowed;
22412     },
22413
22414     /**
22415      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22416      * This method will be called on every mouse movement while the drag source is over the drop zone.
22417      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22418      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22419      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22420      * registered node, it will call {@link #onContainerOver}.
22421      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22422      * @param {Event} e The event
22423      * @param {Object} data An object containing arbitrary data supplied by the drag source
22424      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22425      * underlying {@link Roo.dd.StatusProxy} can be updated
22426      */
22427     notifyOver : function(dd, e, data){
22428         var n = this.getTargetFromEvent(e);
22429         if(!n){ // not over valid drop target
22430             if(this.lastOverNode){
22431                 this.onNodeOut(this.lastOverNode, dd, e, data);
22432                 this.lastOverNode = null;
22433             }
22434             return this.onContainerOver(dd, e, data);
22435         }
22436         if(this.lastOverNode != n){
22437             if(this.lastOverNode){
22438                 this.onNodeOut(this.lastOverNode, dd, e, data);
22439             }
22440             this.onNodeEnter(n, dd, e, data);
22441             this.lastOverNode = n;
22442         }
22443         return this.onNodeOver(n, dd, e, data);
22444     },
22445
22446     /**
22447      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22448      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22449      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22450      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22451      * @param {Event} e The event
22452      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22453      */
22454     notifyOut : function(dd, e, data){
22455         if(this.lastOverNode){
22456             this.onNodeOut(this.lastOverNode, dd, e, data);
22457             this.lastOverNode = null;
22458         }
22459     },
22460
22461     /**
22462      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22463      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22464      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22465      * otherwise it will call {@link #onContainerDrop}.
22466      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22467      * @param {Event} e The event
22468      * @param {Object} data An object containing arbitrary data supplied by the drag source
22469      * @return {Boolean} True if the drop was valid, else false
22470      */
22471     notifyDrop : function(dd, e, data){
22472         if(this.lastOverNode){
22473             this.onNodeOut(this.lastOverNode, dd, e, data);
22474             this.lastOverNode = null;
22475         }
22476         var n = this.getTargetFromEvent(e);
22477         return n ?
22478             this.onNodeDrop(n, dd, e, data) :
22479             this.onContainerDrop(dd, e, data);
22480     },
22481
22482     // private
22483     triggerCacheRefresh : function(){
22484         Roo.dd.DDM.refreshCache(this.groups);
22485     }  
22486 });/*
22487  * Based on:
22488  * Ext JS Library 1.1.1
22489  * Copyright(c) 2006-2007, Ext JS, LLC.
22490  *
22491  * Originally Released Under LGPL - original licence link has changed is not relivant.
22492  *
22493  * Fork - LGPL
22494  * <script type="text/javascript">
22495  */
22496
22497
22498 /**
22499  * @class Roo.data.SortTypes
22500  * @singleton
22501  * Defines the default sorting (casting?) comparison functions used when sorting data.
22502  */
22503 Roo.data.SortTypes = {
22504     /**
22505      * Default sort that does nothing
22506      * @param {Mixed} s The value being converted
22507      * @return {Mixed} The comparison value
22508      */
22509     none : function(s){
22510         return s;
22511     },
22512     
22513     /**
22514      * The regular expression used to strip tags
22515      * @type {RegExp}
22516      * @property
22517      */
22518     stripTagsRE : /<\/?[^>]+>/gi,
22519     
22520     /**
22521      * Strips all HTML tags to sort on text only
22522      * @param {Mixed} s The value being converted
22523      * @return {String} The comparison value
22524      */
22525     asText : function(s){
22526         return String(s).replace(this.stripTagsRE, "");
22527     },
22528     
22529     /**
22530      * Strips all HTML tags to sort on text only - Case insensitive
22531      * @param {Mixed} s The value being converted
22532      * @return {String} The comparison value
22533      */
22534     asUCText : function(s){
22535         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22536     },
22537     
22538     /**
22539      * Case insensitive string
22540      * @param {Mixed} s The value being converted
22541      * @return {String} The comparison value
22542      */
22543     asUCString : function(s) {
22544         return String(s).toUpperCase();
22545     },
22546     
22547     /**
22548      * Date sorting
22549      * @param {Mixed} s The value being converted
22550      * @return {Number} The comparison value
22551      */
22552     asDate : function(s) {
22553         if(!s){
22554             return 0;
22555         }
22556         if(s instanceof Date){
22557             return s.getTime();
22558         }
22559         return Date.parse(String(s));
22560     },
22561     
22562     /**
22563      * Float sorting
22564      * @param {Mixed} s The value being converted
22565      * @return {Float} The comparison value
22566      */
22567     asFloat : function(s) {
22568         var val = parseFloat(String(s).replace(/,/g, ""));
22569         if(isNaN(val)) {
22570             val = 0;
22571         }
22572         return val;
22573     },
22574     
22575     /**
22576      * Integer sorting
22577      * @param {Mixed} s The value being converted
22578      * @return {Number} The comparison value
22579      */
22580     asInt : function(s) {
22581         var val = parseInt(String(s).replace(/,/g, ""));
22582         if(isNaN(val)) {
22583             val = 0;
22584         }
22585         return val;
22586     }
22587 };/*
22588  * Based on:
22589  * Ext JS Library 1.1.1
22590  * Copyright(c) 2006-2007, Ext JS, LLC.
22591  *
22592  * Originally Released Under LGPL - original licence link has changed is not relivant.
22593  *
22594  * Fork - LGPL
22595  * <script type="text/javascript">
22596  */
22597
22598 /**
22599 * @class Roo.data.Record
22600  * Instances of this class encapsulate both record <em>definition</em> information, and record
22601  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22602  * to access Records cached in an {@link Roo.data.Store} object.<br>
22603  * <p>
22604  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22605  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22606  * objects.<br>
22607  * <p>
22608  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22609  * @constructor
22610  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22611  * {@link #create}. The parameters are the same.
22612  * @param {Array} data An associative Array of data values keyed by the field name.
22613  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22614  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22615  * not specified an integer id is generated.
22616  */
22617 Roo.data.Record = function(data, id){
22618     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22619     this.data = data;
22620 };
22621
22622 /**
22623  * Generate a constructor for a specific record layout.
22624  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22625  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22626  * Each field definition object may contain the following properties: <ul>
22627  * <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,
22628  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22629  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22630  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22631  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22632  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22633  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22634  * this may be omitted.</p></li>
22635  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22636  * <ul><li>auto (Default, implies no conversion)</li>
22637  * <li>string</li>
22638  * <li>int</li>
22639  * <li>float</li>
22640  * <li>boolean</li>
22641  * <li>date</li></ul></p></li>
22642  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22643  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22644  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22645  * by the Reader into an object that will be stored in the Record. It is passed the
22646  * following parameters:<ul>
22647  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22648  * </ul></p></li>
22649  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22650  * </ul>
22651  * <br>usage:<br><pre><code>
22652 var TopicRecord = Roo.data.Record.create(
22653     {name: 'title', mapping: 'topic_title'},
22654     {name: 'author', mapping: 'username'},
22655     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22656     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22657     {name: 'lastPoster', mapping: 'user2'},
22658     {name: 'excerpt', mapping: 'post_text'}
22659 );
22660
22661 var myNewRecord = new TopicRecord({
22662     title: 'Do my job please',
22663     author: 'noobie',
22664     totalPosts: 1,
22665     lastPost: new Date(),
22666     lastPoster: 'Animal',
22667     excerpt: 'No way dude!'
22668 });
22669 myStore.add(myNewRecord);
22670 </code></pre>
22671  * @method create
22672  * @static
22673  */
22674 Roo.data.Record.create = function(o){
22675     var f = function(){
22676         f.superclass.constructor.apply(this, arguments);
22677     };
22678     Roo.extend(f, Roo.data.Record);
22679     var p = f.prototype;
22680     p.fields = new Roo.util.MixedCollection(false, function(field){
22681         return field.name;
22682     });
22683     for(var i = 0, len = o.length; i < len; i++){
22684         p.fields.add(new Roo.data.Field(o[i]));
22685     }
22686     f.getField = function(name){
22687         return p.fields.get(name);  
22688     };
22689     return f;
22690 };
22691
22692 Roo.data.Record.AUTO_ID = 1000;
22693 Roo.data.Record.EDIT = 'edit';
22694 Roo.data.Record.REJECT = 'reject';
22695 Roo.data.Record.COMMIT = 'commit';
22696
22697 Roo.data.Record.prototype = {
22698     /**
22699      * Readonly flag - true if this record has been modified.
22700      * @type Boolean
22701      */
22702     dirty : false,
22703     editing : false,
22704     error: null,
22705     modified: null,
22706
22707     // private
22708     join : function(store){
22709         this.store = store;
22710     },
22711
22712     /**
22713      * Set the named field to the specified value.
22714      * @param {String} name The name of the field to set.
22715      * @param {Object} value The value to set the field to.
22716      */
22717     set : function(name, value){
22718         if(this.data[name] == value){
22719             return;
22720         }
22721         this.dirty = true;
22722         if(!this.modified){
22723             this.modified = {};
22724         }
22725         if(typeof this.modified[name] == 'undefined'){
22726             this.modified[name] = this.data[name];
22727         }
22728         this.data[name] = value;
22729         if(!this.editing && this.store){
22730             this.store.afterEdit(this);
22731         }       
22732     },
22733
22734     /**
22735      * Get the value of the named field.
22736      * @param {String} name The name of the field to get the value of.
22737      * @return {Object} The value of the field.
22738      */
22739     get : function(name){
22740         return this.data[name]; 
22741     },
22742
22743     // private
22744     beginEdit : function(){
22745         this.editing = true;
22746         this.modified = {}; 
22747     },
22748
22749     // private
22750     cancelEdit : function(){
22751         this.editing = false;
22752         delete this.modified;
22753     },
22754
22755     // private
22756     endEdit : function(){
22757         this.editing = false;
22758         if(this.dirty && this.store){
22759             this.store.afterEdit(this);
22760         }
22761     },
22762
22763     /**
22764      * Usually called by the {@link Roo.data.Store} which owns the Record.
22765      * Rejects all changes made to the Record since either creation, or the last commit operation.
22766      * Modified fields are reverted to their original values.
22767      * <p>
22768      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22769      * of reject operations.
22770      */
22771     reject : function(){
22772         var m = this.modified;
22773         for(var n in m){
22774             if(typeof m[n] != "function"){
22775                 this.data[n] = m[n];
22776             }
22777         }
22778         this.dirty = false;
22779         delete this.modified;
22780         this.editing = false;
22781         if(this.store){
22782             this.store.afterReject(this);
22783         }
22784     },
22785
22786     /**
22787      * Usually called by the {@link Roo.data.Store} which owns the Record.
22788      * Commits all changes made to the Record since either creation, or the last commit operation.
22789      * <p>
22790      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22791      * of commit operations.
22792      */
22793     commit : function(){
22794         this.dirty = false;
22795         delete this.modified;
22796         this.editing = false;
22797         if(this.store){
22798             this.store.afterCommit(this);
22799         }
22800     },
22801
22802     // private
22803     hasError : function(){
22804         return this.error != null;
22805     },
22806
22807     // private
22808     clearError : function(){
22809         this.error = null;
22810     },
22811
22812     /**
22813      * Creates a copy of this record.
22814      * @param {String} id (optional) A new record id if you don't want to use this record's id
22815      * @return {Record}
22816      */
22817     copy : function(newId) {
22818         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22819     }
22820 };/*
22821  * Based on:
22822  * Ext JS Library 1.1.1
22823  * Copyright(c) 2006-2007, Ext JS, LLC.
22824  *
22825  * Originally Released Under LGPL - original licence link has changed is not relivant.
22826  *
22827  * Fork - LGPL
22828  * <script type="text/javascript">
22829  */
22830
22831
22832
22833 /**
22834  * @class Roo.data.Store
22835  * @extends Roo.util.Observable
22836  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22837  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22838  * <p>
22839  * 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
22840  * has no knowledge of the format of the data returned by the Proxy.<br>
22841  * <p>
22842  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22843  * instances from the data object. These records are cached and made available through accessor functions.
22844  * @constructor
22845  * Creates a new Store.
22846  * @param {Object} config A config object containing the objects needed for the Store to access data,
22847  * and read the data into Records.
22848  */
22849 Roo.data.Store = function(config){
22850     this.data = new Roo.util.MixedCollection(false);
22851     this.data.getKey = function(o){
22852         return o.id;
22853     };
22854     this.baseParams = {};
22855     // private
22856     this.paramNames = {
22857         "start" : "start",
22858         "limit" : "limit",
22859         "sort" : "sort",
22860         "dir" : "dir",
22861         "multisort" : "_multisort"
22862     };
22863
22864     if(config && config.data){
22865         this.inlineData = config.data;
22866         delete config.data;
22867     }
22868
22869     Roo.apply(this, config);
22870     
22871     if(this.reader){ // reader passed
22872         this.reader = Roo.factory(this.reader, Roo.data);
22873         this.reader.xmodule = this.xmodule || false;
22874         if(!this.recordType){
22875             this.recordType = this.reader.recordType;
22876         }
22877         if(this.reader.onMetaChange){
22878             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22879         }
22880     }
22881
22882     if(this.recordType){
22883         this.fields = this.recordType.prototype.fields;
22884     }
22885     this.modified = [];
22886
22887     this.addEvents({
22888         /**
22889          * @event datachanged
22890          * Fires when the data cache has changed, and a widget which is using this Store
22891          * as a Record cache should refresh its view.
22892          * @param {Store} this
22893          */
22894         datachanged : true,
22895         /**
22896          * @event metachange
22897          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22898          * @param {Store} this
22899          * @param {Object} meta The JSON metadata
22900          */
22901         metachange : true,
22902         /**
22903          * @event add
22904          * Fires when Records have been added to the Store
22905          * @param {Store} this
22906          * @param {Roo.data.Record[]} records The array of Records added
22907          * @param {Number} index The index at which the record(s) were added
22908          */
22909         add : true,
22910         /**
22911          * @event remove
22912          * Fires when a Record has been removed from the Store
22913          * @param {Store} this
22914          * @param {Roo.data.Record} record The Record that was removed
22915          * @param {Number} index The index at which the record was removed
22916          */
22917         remove : true,
22918         /**
22919          * @event update
22920          * Fires when a Record has been updated
22921          * @param {Store} this
22922          * @param {Roo.data.Record} record The Record that was updated
22923          * @param {String} operation The update operation being performed.  Value may be one of:
22924          * <pre><code>
22925  Roo.data.Record.EDIT
22926  Roo.data.Record.REJECT
22927  Roo.data.Record.COMMIT
22928          * </code></pre>
22929          */
22930         update : true,
22931         /**
22932          * @event clear
22933          * Fires when the data cache has been cleared.
22934          * @param {Store} this
22935          */
22936         clear : true,
22937         /**
22938          * @event beforeload
22939          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22940          * the load action will be canceled.
22941          * @param {Store} this
22942          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22943          */
22944         beforeload : true,
22945         /**
22946          * @event beforeloadadd
22947          * Fires after a new set of Records has been loaded.
22948          * @param {Store} this
22949          * @param {Roo.data.Record[]} records The Records that were loaded
22950          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22951          */
22952         beforeloadadd : true,
22953         /**
22954          * @event load
22955          * Fires after a new set of Records has been loaded, before they are added to the store.
22956          * @param {Store} this
22957          * @param {Roo.data.Record[]} records The Records that were loaded
22958          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22959          * @params {Object} return from reader
22960          */
22961         load : true,
22962         /**
22963          * @event loadexception
22964          * Fires if an exception occurs in the Proxy during loading.
22965          * Called with the signature of the Proxy's "loadexception" event.
22966          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22967          * 
22968          * @param {Proxy} 
22969          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22970          * @param {Object} load options 
22971          * @param {Object} jsonData from your request (normally this contains the Exception)
22972          */
22973         loadexception : true
22974     });
22975     
22976     if(this.proxy){
22977         this.proxy = Roo.factory(this.proxy, Roo.data);
22978         this.proxy.xmodule = this.xmodule || false;
22979         this.relayEvents(this.proxy,  ["loadexception"]);
22980     }
22981     this.sortToggle = {};
22982     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22983
22984     Roo.data.Store.superclass.constructor.call(this);
22985
22986     if(this.inlineData){
22987         this.loadData(this.inlineData);
22988         delete this.inlineData;
22989     }
22990 };
22991
22992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22993      /**
22994     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22995     * without a remote query - used by combo/forms at present.
22996     */
22997     
22998     /**
22999     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23000     */
23001     /**
23002     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23003     */
23004     /**
23005     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23006     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23007     */
23008     /**
23009     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23010     * on any HTTP request
23011     */
23012     /**
23013     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23014     */
23015     /**
23016     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23017     */
23018     multiSort: false,
23019     /**
23020     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23021     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23022     */
23023     remoteSort : false,
23024
23025     /**
23026     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23027      * loaded or when a record is removed. (defaults to false).
23028     */
23029     pruneModifiedRecords : false,
23030
23031     // private
23032     lastOptions : null,
23033
23034     /**
23035      * Add Records to the Store and fires the add event.
23036      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23037      */
23038     add : function(records){
23039         records = [].concat(records);
23040         for(var i = 0, len = records.length; i < len; i++){
23041             records[i].join(this);
23042         }
23043         var index = this.data.length;
23044         this.data.addAll(records);
23045         this.fireEvent("add", this, records, index);
23046     },
23047
23048     /**
23049      * Remove a Record from the Store and fires the remove event.
23050      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23051      */
23052     remove : function(record){
23053         var index = this.data.indexOf(record);
23054         this.data.removeAt(index);
23055  
23056         if(this.pruneModifiedRecords){
23057             this.modified.remove(record);
23058         }
23059         this.fireEvent("remove", this, record, index);
23060     },
23061
23062     /**
23063      * Remove all Records from the Store and fires the clear event.
23064      */
23065     removeAll : function(){
23066         this.data.clear();
23067         if(this.pruneModifiedRecords){
23068             this.modified = [];
23069         }
23070         this.fireEvent("clear", this);
23071     },
23072
23073     /**
23074      * Inserts Records to the Store at the given index and fires the add event.
23075      * @param {Number} index The start index at which to insert the passed Records.
23076      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23077      */
23078     insert : function(index, records){
23079         records = [].concat(records);
23080         for(var i = 0, len = records.length; i < len; i++){
23081             this.data.insert(index, records[i]);
23082             records[i].join(this);
23083         }
23084         this.fireEvent("add", this, records, index);
23085     },
23086
23087     /**
23088      * Get the index within the cache of the passed Record.
23089      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23090      * @return {Number} The index of the passed Record. Returns -1 if not found.
23091      */
23092     indexOf : function(record){
23093         return this.data.indexOf(record);
23094     },
23095
23096     /**
23097      * Get the index within the cache of the Record with the passed id.
23098      * @param {String} id The id of the Record to find.
23099      * @return {Number} The index of the Record. Returns -1 if not found.
23100      */
23101     indexOfId : function(id){
23102         return this.data.indexOfKey(id);
23103     },
23104
23105     /**
23106      * Get the Record with the specified id.
23107      * @param {String} id The id of the Record to find.
23108      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23109      */
23110     getById : function(id){
23111         return this.data.key(id);
23112     },
23113
23114     /**
23115      * Get the Record at the specified index.
23116      * @param {Number} index The index of the Record to find.
23117      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23118      */
23119     getAt : function(index){
23120         return this.data.itemAt(index);
23121     },
23122
23123     /**
23124      * Returns a range of Records between specified indices.
23125      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23126      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23127      * @return {Roo.data.Record[]} An array of Records
23128      */
23129     getRange : function(start, end){
23130         return this.data.getRange(start, end);
23131     },
23132
23133     // private
23134     storeOptions : function(o){
23135         o = Roo.apply({}, o);
23136         delete o.callback;
23137         delete o.scope;
23138         this.lastOptions = o;
23139     },
23140
23141     /**
23142      * Loads the Record cache from the configured Proxy using the configured Reader.
23143      * <p>
23144      * If using remote paging, then the first load call must specify the <em>start</em>
23145      * and <em>limit</em> properties in the options.params property to establish the initial
23146      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23147      * <p>
23148      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23149      * and this call will return before the new data has been loaded. Perform any post-processing
23150      * in a callback function, or in a "load" event handler.</strong>
23151      * <p>
23152      * @param {Object} options An object containing properties which control loading options:<ul>
23153      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23154      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23155      * passed the following arguments:<ul>
23156      * <li>r : Roo.data.Record[]</li>
23157      * <li>options: Options object from the load call</li>
23158      * <li>success: Boolean success indicator</li></ul></li>
23159      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23160      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23161      * </ul>
23162      */
23163     load : function(options){
23164         options = options || {};
23165         if(this.fireEvent("beforeload", this, options) !== false){
23166             this.storeOptions(options);
23167             var p = Roo.apply(options.params || {}, this.baseParams);
23168             // if meta was not loaded from remote source.. try requesting it.
23169             if (!this.reader.metaFromRemote) {
23170                 p._requestMeta = 1;
23171             }
23172             if(this.sortInfo && this.remoteSort){
23173                 var pn = this.paramNames;
23174                 p[pn["sort"]] = this.sortInfo.field;
23175                 p[pn["dir"]] = this.sortInfo.direction;
23176             }
23177             if (this.multiSort) {
23178                 var pn = this.paramNames;
23179                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23180             }
23181             
23182             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23183         }
23184     },
23185
23186     /**
23187      * Reloads the Record cache from the configured Proxy using the configured Reader and
23188      * the options from the last load operation performed.
23189      * @param {Object} options (optional) An object containing properties which may override the options
23190      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23191      * the most recently used options are reused).
23192      */
23193     reload : function(options){
23194         this.load(Roo.applyIf(options||{}, this.lastOptions));
23195     },
23196
23197     // private
23198     // Called as a callback by the Reader during a load operation.
23199     loadRecords : function(o, options, success){
23200         if(!o || success === false){
23201             if(success !== false){
23202                 this.fireEvent("load", this, [], options, o);
23203             }
23204             if(options.callback){
23205                 options.callback.call(options.scope || this, [], options, false);
23206             }
23207             return;
23208         }
23209         // if data returned failure - throw an exception.
23210         if (o.success === false) {
23211             // show a message if no listener is registered.
23212             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23213                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23214             }
23215             // loadmask wil be hooked into this..
23216             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23217             return;
23218         }
23219         var r = o.records, t = o.totalRecords || r.length;
23220         
23221         this.fireEvent("beforeloadadd", this, r, options, o);
23222         
23223         if(!options || options.add !== true){
23224             if(this.pruneModifiedRecords){
23225                 this.modified = [];
23226             }
23227             for(var i = 0, len = r.length; i < len; i++){
23228                 r[i].join(this);
23229             }
23230             if(this.snapshot){
23231                 this.data = this.snapshot;
23232                 delete this.snapshot;
23233             }
23234             this.data.clear();
23235             this.data.addAll(r);
23236             this.totalLength = t;
23237             this.applySort();
23238             this.fireEvent("datachanged", this);
23239         }else{
23240             this.totalLength = Math.max(t, this.data.length+r.length);
23241             this.add(r);
23242         }
23243         
23244         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23245                 
23246             var e = new Roo.data.Record({});
23247
23248             e.set(this.parent.displayField, this.parent.emptyTitle);
23249             e.set(this.parent.valueField, '');
23250
23251             this.insert(0, e);
23252         }
23253             
23254         this.fireEvent("load", this, r, options, o);
23255         if(options.callback){
23256             options.callback.call(options.scope || this, r, options, true);
23257         }
23258     },
23259
23260
23261     /**
23262      * Loads data from a passed data block. A Reader which understands the format of the data
23263      * must have been configured in the constructor.
23264      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23265      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23266      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23267      */
23268     loadData : function(o, append){
23269         var r = this.reader.readRecords(o);
23270         this.loadRecords(r, {add: append}, true);
23271     },
23272     
23273      /**
23274      * using 'cn' the nested child reader read the child array into it's child stores.
23275      * @param {Object} rec The record with a 'children array
23276      */
23277     loadDataFromChildren : function(rec)
23278     {
23279         this.loadData(this.reader.toLoadData(rec));
23280     },
23281     
23282
23283     /**
23284      * Gets the number of cached records.
23285      * <p>
23286      * <em>If using paging, this may not be the total size of the dataset. If the data object
23287      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23288      * the data set size</em>
23289      */
23290     getCount : function(){
23291         return this.data.length || 0;
23292     },
23293
23294     /**
23295      * Gets the total number of records in the dataset as returned by the server.
23296      * <p>
23297      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23298      * the dataset size</em>
23299      */
23300     getTotalCount : function(){
23301         return this.totalLength || 0;
23302     },
23303
23304     /**
23305      * Returns the sort state of the Store as an object with two properties:
23306      * <pre><code>
23307  field {String} The name of the field by which the Records are sorted
23308  direction {String} The sort order, "ASC" or "DESC"
23309      * </code></pre>
23310      */
23311     getSortState : function(){
23312         return this.sortInfo;
23313     },
23314
23315     // private
23316     applySort : function(){
23317         if(this.sortInfo && !this.remoteSort){
23318             var s = this.sortInfo, f = s.field;
23319             var st = this.fields.get(f).sortType;
23320             var fn = function(r1, r2){
23321                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23322                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23323             };
23324             this.data.sort(s.direction, fn);
23325             if(this.snapshot && this.snapshot != this.data){
23326                 this.snapshot.sort(s.direction, fn);
23327             }
23328         }
23329     },
23330
23331     /**
23332      * Sets the default sort column and order to be used by the next load operation.
23333      * @param {String} fieldName The name of the field to sort by.
23334      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23335      */
23336     setDefaultSort : function(field, dir){
23337         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23338     },
23339
23340     /**
23341      * Sort the Records.
23342      * If remote sorting is used, the sort is performed on the server, and the cache is
23343      * reloaded. If local sorting is used, the cache is sorted internally.
23344      * @param {String} fieldName The name of the field to sort by.
23345      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23346      */
23347     sort : function(fieldName, dir){
23348         var f = this.fields.get(fieldName);
23349         if(!dir){
23350             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23351             
23352             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23353                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23354             }else{
23355                 dir = f.sortDir;
23356             }
23357         }
23358         this.sortToggle[f.name] = dir;
23359         this.sortInfo = {field: f.name, direction: dir};
23360         if(!this.remoteSort){
23361             this.applySort();
23362             this.fireEvent("datachanged", this);
23363         }else{
23364             this.load(this.lastOptions);
23365         }
23366     },
23367
23368     /**
23369      * Calls the specified function for each of the Records in the cache.
23370      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23371      * Returning <em>false</em> aborts and exits the iteration.
23372      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23373      */
23374     each : function(fn, scope){
23375         this.data.each(fn, scope);
23376     },
23377
23378     /**
23379      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23380      * (e.g., during paging).
23381      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23382      */
23383     getModifiedRecords : function(){
23384         return this.modified;
23385     },
23386
23387     // private
23388     createFilterFn : function(property, value, anyMatch){
23389         if(!value.exec){ // not a regex
23390             value = String(value);
23391             if(value.length == 0){
23392                 return false;
23393             }
23394             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23395         }
23396         return function(r){
23397             return value.test(r.data[property]);
23398         };
23399     },
23400
23401     /**
23402      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23403      * @param {String} property A field on your records
23404      * @param {Number} start The record index to start at (defaults to 0)
23405      * @param {Number} end The last record index to include (defaults to length - 1)
23406      * @return {Number} The sum
23407      */
23408     sum : function(property, start, end){
23409         var rs = this.data.items, v = 0;
23410         start = start || 0;
23411         end = (end || end === 0) ? end : rs.length-1;
23412
23413         for(var i = start; i <= end; i++){
23414             v += (rs[i].data[property] || 0);
23415         }
23416         return v;
23417     },
23418
23419     /**
23420      * Filter the records by a specified property.
23421      * @param {String} field A field on your records
23422      * @param {String/RegExp} value Either a string that the field
23423      * should start with or a RegExp to test against the field
23424      * @param {Boolean} anyMatch True to match any part not just the beginning
23425      */
23426     filter : function(property, value, anyMatch){
23427         var fn = this.createFilterFn(property, value, anyMatch);
23428         return fn ? this.filterBy(fn) : this.clearFilter();
23429     },
23430
23431     /**
23432      * Filter by a function. The specified function will be called with each
23433      * record in this data source. If the function returns true the record is included,
23434      * otherwise it is filtered.
23435      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23436      * @param {Object} scope (optional) The scope of the function (defaults to this)
23437      */
23438     filterBy : function(fn, scope){
23439         this.snapshot = this.snapshot || this.data;
23440         this.data = this.queryBy(fn, scope||this);
23441         this.fireEvent("datachanged", this);
23442     },
23443
23444     /**
23445      * Query the records by a specified property.
23446      * @param {String} field A field on your records
23447      * @param {String/RegExp} value Either a string that the field
23448      * should start with or a RegExp to test against the field
23449      * @param {Boolean} anyMatch True to match any part not just the beginning
23450      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23451      */
23452     query : function(property, value, anyMatch){
23453         var fn = this.createFilterFn(property, value, anyMatch);
23454         return fn ? this.queryBy(fn) : this.data.clone();
23455     },
23456
23457     /**
23458      * Query by a function. The specified function will be called with each
23459      * record in this data source. If the function returns true the record is included
23460      * in the results.
23461      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23462      * @param {Object} scope (optional) The scope of the function (defaults to this)
23463       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23464      **/
23465     queryBy : function(fn, scope){
23466         var data = this.snapshot || this.data;
23467         return data.filterBy(fn, scope||this);
23468     },
23469
23470     /**
23471      * Collects unique values for a particular dataIndex from this store.
23472      * @param {String} dataIndex The property to collect
23473      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23474      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23475      * @return {Array} An array of the unique values
23476      **/
23477     collect : function(dataIndex, allowNull, bypassFilter){
23478         var d = (bypassFilter === true && this.snapshot) ?
23479                 this.snapshot.items : this.data.items;
23480         var v, sv, r = [], l = {};
23481         for(var i = 0, len = d.length; i < len; i++){
23482             v = d[i].data[dataIndex];
23483             sv = String(v);
23484             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23485                 l[sv] = true;
23486                 r[r.length] = v;
23487             }
23488         }
23489         return r;
23490     },
23491
23492     /**
23493      * Revert to a view of the Record cache with no filtering applied.
23494      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23495      */
23496     clearFilter : function(suppressEvent){
23497         if(this.snapshot && this.snapshot != this.data){
23498             this.data = this.snapshot;
23499             delete this.snapshot;
23500             if(suppressEvent !== true){
23501                 this.fireEvent("datachanged", this);
23502             }
23503         }
23504     },
23505
23506     // private
23507     afterEdit : function(record){
23508         if(this.modified.indexOf(record) == -1){
23509             this.modified.push(record);
23510         }
23511         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23512     },
23513     
23514     // private
23515     afterReject : function(record){
23516         this.modified.remove(record);
23517         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23518     },
23519
23520     // private
23521     afterCommit : function(record){
23522         this.modified.remove(record);
23523         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23524     },
23525
23526     /**
23527      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23528      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23529      */
23530     commitChanges : function(){
23531         var m = this.modified.slice(0);
23532         this.modified = [];
23533         for(var i = 0, len = m.length; i < len; i++){
23534             m[i].commit();
23535         }
23536     },
23537
23538     /**
23539      * Cancel outstanding changes on all changed records.
23540      */
23541     rejectChanges : function(){
23542         var m = this.modified.slice(0);
23543         this.modified = [];
23544         for(var i = 0, len = m.length; i < len; i++){
23545             m[i].reject();
23546         }
23547     },
23548
23549     onMetaChange : function(meta, rtype, o){
23550         this.recordType = rtype;
23551         this.fields = rtype.prototype.fields;
23552         delete this.snapshot;
23553         this.sortInfo = meta.sortInfo || this.sortInfo;
23554         this.modified = [];
23555         this.fireEvent('metachange', this, this.reader.meta);
23556     },
23557     
23558     moveIndex : function(data, type)
23559     {
23560         var index = this.indexOf(data);
23561         
23562         var newIndex = index + type;
23563         
23564         this.remove(data);
23565         
23566         this.insert(newIndex, data);
23567         
23568     }
23569 });/*
23570  * Based on:
23571  * Ext JS Library 1.1.1
23572  * Copyright(c) 2006-2007, Ext JS, LLC.
23573  *
23574  * Originally Released Under LGPL - original licence link has changed is not relivant.
23575  *
23576  * Fork - LGPL
23577  * <script type="text/javascript">
23578  */
23579
23580 /**
23581  * @class Roo.data.SimpleStore
23582  * @extends Roo.data.Store
23583  * Small helper class to make creating Stores from Array data easier.
23584  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23585  * @cfg {Array} fields An array of field definition objects, or field name strings.
23586  * @cfg {Object} an existing reader (eg. copied from another store)
23587  * @cfg {Array} data The multi-dimensional array of data
23588  * @constructor
23589  * @param {Object} config
23590  */
23591 Roo.data.SimpleStore = function(config)
23592 {
23593     Roo.data.SimpleStore.superclass.constructor.call(this, {
23594         isLocal : true,
23595         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23596                 id: config.id
23597             },
23598             Roo.data.Record.create(config.fields)
23599         ),
23600         proxy : new Roo.data.MemoryProxy(config.data)
23601     });
23602     this.load();
23603 };
23604 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23605  * Based on:
23606  * Ext JS Library 1.1.1
23607  * Copyright(c) 2006-2007, Ext JS, LLC.
23608  *
23609  * Originally Released Under LGPL - original licence link has changed is not relivant.
23610  *
23611  * Fork - LGPL
23612  * <script type="text/javascript">
23613  */
23614
23615 /**
23616 /**
23617  * @extends Roo.data.Store
23618  * @class Roo.data.JsonStore
23619  * Small helper class to make creating Stores for JSON data easier. <br/>
23620 <pre><code>
23621 var store = new Roo.data.JsonStore({
23622     url: 'get-images.php',
23623     root: 'images',
23624     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23625 });
23626 </code></pre>
23627  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23628  * JsonReader and HttpProxy (unless inline data is provided).</b>
23629  * @cfg {Array} fields An array of field definition objects, or field name strings.
23630  * @constructor
23631  * @param {Object} config
23632  */
23633 Roo.data.JsonStore = function(c){
23634     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23635         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23636         reader: new Roo.data.JsonReader(c, c.fields)
23637     }));
23638 };
23639 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23640  * Based on:
23641  * Ext JS Library 1.1.1
23642  * Copyright(c) 2006-2007, Ext JS, LLC.
23643  *
23644  * Originally Released Under LGPL - original licence link has changed is not relivant.
23645  *
23646  * Fork - LGPL
23647  * <script type="text/javascript">
23648  */
23649
23650  
23651 Roo.data.Field = function(config){
23652     if(typeof config == "string"){
23653         config = {name: config};
23654     }
23655     Roo.apply(this, config);
23656     
23657     if(!this.type){
23658         this.type = "auto";
23659     }
23660     
23661     var st = Roo.data.SortTypes;
23662     // named sortTypes are supported, here we look them up
23663     if(typeof this.sortType == "string"){
23664         this.sortType = st[this.sortType];
23665     }
23666     
23667     // set default sortType for strings and dates
23668     if(!this.sortType){
23669         switch(this.type){
23670             case "string":
23671                 this.sortType = st.asUCString;
23672                 break;
23673             case "date":
23674                 this.sortType = st.asDate;
23675                 break;
23676             default:
23677                 this.sortType = st.none;
23678         }
23679     }
23680
23681     // define once
23682     var stripRe = /[\$,%]/g;
23683
23684     // prebuilt conversion function for this field, instead of
23685     // switching every time we're reading a value
23686     if(!this.convert){
23687         var cv, dateFormat = this.dateFormat;
23688         switch(this.type){
23689             case "":
23690             case "auto":
23691             case undefined:
23692                 cv = function(v){ return v; };
23693                 break;
23694             case "string":
23695                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23696                 break;
23697             case "int":
23698                 cv = function(v){
23699                     return v !== undefined && v !== null && v !== '' ?
23700                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23701                     };
23702                 break;
23703             case "float":
23704                 cv = function(v){
23705                     return v !== undefined && v !== null && v !== '' ?
23706                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23707                     };
23708                 break;
23709             case "bool":
23710             case "boolean":
23711                 cv = function(v){ return v === true || v === "true" || v == 1; };
23712                 break;
23713             case "date":
23714                 cv = function(v){
23715                     if(!v){
23716                         return '';
23717                     }
23718                     if(v instanceof Date){
23719                         return v;
23720                     }
23721                     if(dateFormat){
23722                         if(dateFormat == "timestamp"){
23723                             return new Date(v*1000);
23724                         }
23725                         return Date.parseDate(v, dateFormat);
23726                     }
23727                     var parsed = Date.parse(v);
23728                     return parsed ? new Date(parsed) : null;
23729                 };
23730              break;
23731             
23732         }
23733         this.convert = cv;
23734     }
23735 };
23736
23737 Roo.data.Field.prototype = {
23738     dateFormat: null,
23739     defaultValue: "",
23740     mapping: null,
23741     sortType : null,
23742     sortDir : "ASC"
23743 };/*
23744  * Based on:
23745  * Ext JS Library 1.1.1
23746  * Copyright(c) 2006-2007, Ext JS, LLC.
23747  *
23748  * Originally Released Under LGPL - original licence link has changed is not relivant.
23749  *
23750  * Fork - LGPL
23751  * <script type="text/javascript">
23752  */
23753  
23754 // Base class for reading structured data from a data source.  This class is intended to be
23755 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23756
23757 /**
23758  * @class Roo.data.DataReader
23759  * Base class for reading structured data from a data source.  This class is intended to be
23760  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23761  */
23762
23763 Roo.data.DataReader = function(meta, recordType){
23764     
23765     this.meta = meta;
23766     
23767     this.recordType = recordType instanceof Array ? 
23768         Roo.data.Record.create(recordType) : recordType;
23769 };
23770
23771 Roo.data.DataReader.prototype = {
23772     
23773     
23774     readerType : 'Data',
23775      /**
23776      * Create an empty record
23777      * @param {Object} data (optional) - overlay some values
23778      * @return {Roo.data.Record} record created.
23779      */
23780     newRow :  function(d) {
23781         var da =  {};
23782         this.recordType.prototype.fields.each(function(c) {
23783             switch( c.type) {
23784                 case 'int' : da[c.name] = 0; break;
23785                 case 'date' : da[c.name] = new Date(); break;
23786                 case 'float' : da[c.name] = 0.0; break;
23787                 case 'boolean' : da[c.name] = false; break;
23788                 default : da[c.name] = ""; break;
23789             }
23790             
23791         });
23792         return new this.recordType(Roo.apply(da, d));
23793     }
23794     
23795     
23796 };/*
23797  * Based on:
23798  * Ext JS Library 1.1.1
23799  * Copyright(c) 2006-2007, Ext JS, LLC.
23800  *
23801  * Originally Released Under LGPL - original licence link has changed is not relivant.
23802  *
23803  * Fork - LGPL
23804  * <script type="text/javascript">
23805  */
23806
23807 /**
23808  * @class Roo.data.DataProxy
23809  * @extends Roo.data.Observable
23810  * This class is an abstract base class for implementations which provide retrieval of
23811  * unformatted data objects.<br>
23812  * <p>
23813  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23814  * (of the appropriate type which knows how to parse the data object) to provide a block of
23815  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23816  * <p>
23817  * Custom implementations must implement the load method as described in
23818  * {@link Roo.data.HttpProxy#load}.
23819  */
23820 Roo.data.DataProxy = function(){
23821     this.addEvents({
23822         /**
23823          * @event beforeload
23824          * Fires before a network request is made to retrieve a data object.
23825          * @param {Object} This DataProxy object.
23826          * @param {Object} params The params parameter to the load function.
23827          */
23828         beforeload : true,
23829         /**
23830          * @event load
23831          * Fires before the load method's callback is called.
23832          * @param {Object} This DataProxy object.
23833          * @param {Object} o The data object.
23834          * @param {Object} arg The callback argument object passed to the load function.
23835          */
23836         load : true,
23837         /**
23838          * @event loadexception
23839          * Fires if an Exception occurs during data retrieval.
23840          * @param {Object} This DataProxy object.
23841          * @param {Object} o The data object.
23842          * @param {Object} arg The callback argument object passed to the load function.
23843          * @param {Object} e The Exception.
23844          */
23845         loadexception : true
23846     });
23847     Roo.data.DataProxy.superclass.constructor.call(this);
23848 };
23849
23850 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23851
23852     /**
23853      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23854      */
23855 /*
23856  * Based on:
23857  * Ext JS Library 1.1.1
23858  * Copyright(c) 2006-2007, Ext JS, LLC.
23859  *
23860  * Originally Released Under LGPL - original licence link has changed is not relivant.
23861  *
23862  * Fork - LGPL
23863  * <script type="text/javascript">
23864  */
23865 /**
23866  * @class Roo.data.MemoryProxy
23867  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23868  * to the Reader when its load method is called.
23869  * @constructor
23870  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23871  */
23872 Roo.data.MemoryProxy = function(data){
23873     if (data.data) {
23874         data = data.data;
23875     }
23876     Roo.data.MemoryProxy.superclass.constructor.call(this);
23877     this.data = data;
23878 };
23879
23880 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23881     
23882     /**
23883      * Load data from the requested source (in this case an in-memory
23884      * data object passed to the constructor), read the data object into
23885      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23886      * process that block using the passed callback.
23887      * @param {Object} params This parameter is not used by the MemoryProxy class.
23888      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23889      * object into a block of Roo.data.Records.
23890      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23891      * The function must be passed <ul>
23892      * <li>The Record block object</li>
23893      * <li>The "arg" argument from the load function</li>
23894      * <li>A boolean success indicator</li>
23895      * </ul>
23896      * @param {Object} scope The scope in which to call the callback
23897      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23898      */
23899     load : function(params, reader, callback, scope, arg){
23900         params = params || {};
23901         var result;
23902         try {
23903             result = reader.readRecords(params.data ? params.data :this.data);
23904         }catch(e){
23905             this.fireEvent("loadexception", this, arg, null, e);
23906             callback.call(scope, null, arg, false);
23907             return;
23908         }
23909         callback.call(scope, result, arg, true);
23910     },
23911     
23912     // private
23913     update : function(params, records){
23914         
23915     }
23916 });/*
23917  * Based on:
23918  * Ext JS Library 1.1.1
23919  * Copyright(c) 2006-2007, Ext JS, LLC.
23920  *
23921  * Originally Released Under LGPL - original licence link has changed is not relivant.
23922  *
23923  * Fork - LGPL
23924  * <script type="text/javascript">
23925  */
23926 /**
23927  * @class Roo.data.HttpProxy
23928  * @extends Roo.data.DataProxy
23929  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23930  * configured to reference a certain URL.<br><br>
23931  * <p>
23932  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23933  * from which the running page was served.<br><br>
23934  * <p>
23935  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23936  * <p>
23937  * Be aware that to enable the browser to parse an XML document, the server must set
23938  * the Content-Type header in the HTTP response to "text/xml".
23939  * @constructor
23940  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23941  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23942  * will be used to make the request.
23943  */
23944 Roo.data.HttpProxy = function(conn){
23945     Roo.data.HttpProxy.superclass.constructor.call(this);
23946     // is conn a conn config or a real conn?
23947     this.conn = conn;
23948     this.useAjax = !conn || !conn.events;
23949   
23950 };
23951
23952 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23953     // thse are take from connection...
23954     
23955     /**
23956      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23957      */
23958     /**
23959      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23960      * extra parameters to each request made by this object. (defaults to undefined)
23961      */
23962     /**
23963      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23964      *  to each request made by this object. (defaults to undefined)
23965      */
23966     /**
23967      * @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)
23968      */
23969     /**
23970      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23971      */
23972      /**
23973      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23974      * @type Boolean
23975      */
23976   
23977
23978     /**
23979      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23980      * @type Boolean
23981      */
23982     /**
23983      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23984      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23985      * a finer-grained basis than the DataProxy events.
23986      */
23987     getConnection : function(){
23988         return this.useAjax ? Roo.Ajax : this.conn;
23989     },
23990
23991     /**
23992      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23993      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23994      * process that block using the passed callback.
23995      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23996      * for the request to the remote server.
23997      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23998      * object into a block of Roo.data.Records.
23999      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24000      * The function must be passed <ul>
24001      * <li>The Record block object</li>
24002      * <li>The "arg" argument from the load function</li>
24003      * <li>A boolean success indicator</li>
24004      * </ul>
24005      * @param {Object} scope The scope in which to call the callback
24006      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24007      */
24008     load : function(params, reader, callback, scope, arg){
24009         if(this.fireEvent("beforeload", this, params) !== false){
24010             var  o = {
24011                 params : params || {},
24012                 request: {
24013                     callback : callback,
24014                     scope : scope,
24015                     arg : arg
24016                 },
24017                 reader: reader,
24018                 callback : this.loadResponse,
24019                 scope: this
24020             };
24021             if(this.useAjax){
24022                 Roo.applyIf(o, this.conn);
24023                 if(this.activeRequest){
24024                     Roo.Ajax.abort(this.activeRequest);
24025                 }
24026                 this.activeRequest = Roo.Ajax.request(o);
24027             }else{
24028                 this.conn.request(o);
24029             }
24030         }else{
24031             callback.call(scope||this, null, arg, false);
24032         }
24033     },
24034
24035     // private
24036     loadResponse : function(o, success, response){
24037         delete this.activeRequest;
24038         if(!success){
24039             this.fireEvent("loadexception", this, o, response);
24040             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24041             return;
24042         }
24043         var result;
24044         try {
24045             result = o.reader.read(response);
24046         }catch(e){
24047             this.fireEvent("loadexception", this, o, response, e);
24048             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24049             return;
24050         }
24051         
24052         this.fireEvent("load", this, o, o.request.arg);
24053         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24054     },
24055
24056     // private
24057     update : function(dataSet){
24058
24059     },
24060
24061     // private
24062     updateResponse : function(dataSet){
24063
24064     }
24065 });/*
24066  * Based on:
24067  * Ext JS Library 1.1.1
24068  * Copyright(c) 2006-2007, Ext JS, LLC.
24069  *
24070  * Originally Released Under LGPL - original licence link has changed is not relivant.
24071  *
24072  * Fork - LGPL
24073  * <script type="text/javascript">
24074  */
24075
24076 /**
24077  * @class Roo.data.ScriptTagProxy
24078  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24079  * other than the originating domain of the running page.<br><br>
24080  * <p>
24081  * <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
24082  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24083  * <p>
24084  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24085  * source code that is used as the source inside a &lt;script> tag.<br><br>
24086  * <p>
24087  * In order for the browser to process the returned data, the server must wrap the data object
24088  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24089  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24090  * depending on whether the callback name was passed:
24091  * <p>
24092  * <pre><code>
24093 boolean scriptTag = false;
24094 String cb = request.getParameter("callback");
24095 if (cb != null) {
24096     scriptTag = true;
24097     response.setContentType("text/javascript");
24098 } else {
24099     response.setContentType("application/x-json");
24100 }
24101 Writer out = response.getWriter();
24102 if (scriptTag) {
24103     out.write(cb + "(");
24104 }
24105 out.print(dataBlock.toJsonString());
24106 if (scriptTag) {
24107     out.write(");");
24108 }
24109 </pre></code>
24110  *
24111  * @constructor
24112  * @param {Object} config A configuration object.
24113  */
24114 Roo.data.ScriptTagProxy = function(config){
24115     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24116     Roo.apply(this, config);
24117     this.head = document.getElementsByTagName("head")[0];
24118 };
24119
24120 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24121
24122 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24123     /**
24124      * @cfg {String} url The URL from which to request the data object.
24125      */
24126     /**
24127      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24128      */
24129     timeout : 30000,
24130     /**
24131      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24132      * the server the name of the callback function set up by the load call to process the returned data object.
24133      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24134      * javascript output which calls this named function passing the data object as its only parameter.
24135      */
24136     callbackParam : "callback",
24137     /**
24138      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24139      * name to the request.
24140      */
24141     nocache : true,
24142
24143     /**
24144      * Load data from the configured URL, read the data object into
24145      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24146      * process that block using the passed callback.
24147      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24148      * for the request to the remote server.
24149      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24150      * object into a block of Roo.data.Records.
24151      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24152      * The function must be passed <ul>
24153      * <li>The Record block object</li>
24154      * <li>The "arg" argument from the load function</li>
24155      * <li>A boolean success indicator</li>
24156      * </ul>
24157      * @param {Object} scope The scope in which to call the callback
24158      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24159      */
24160     load : function(params, reader, callback, scope, arg){
24161         if(this.fireEvent("beforeload", this, params) !== false){
24162
24163             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24164
24165             var url = this.url;
24166             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24167             if(this.nocache){
24168                 url += "&_dc=" + (new Date().getTime());
24169             }
24170             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24171             var trans = {
24172                 id : transId,
24173                 cb : "stcCallback"+transId,
24174                 scriptId : "stcScript"+transId,
24175                 params : params,
24176                 arg : arg,
24177                 url : url,
24178                 callback : callback,
24179                 scope : scope,
24180                 reader : reader
24181             };
24182             var conn = this;
24183
24184             window[trans.cb] = function(o){
24185                 conn.handleResponse(o, trans);
24186             };
24187
24188             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24189
24190             if(this.autoAbort !== false){
24191                 this.abort();
24192             }
24193
24194             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24195
24196             var script = document.createElement("script");
24197             script.setAttribute("src", url);
24198             script.setAttribute("type", "text/javascript");
24199             script.setAttribute("id", trans.scriptId);
24200             this.head.appendChild(script);
24201
24202             this.trans = trans;
24203         }else{
24204             callback.call(scope||this, null, arg, false);
24205         }
24206     },
24207
24208     // private
24209     isLoading : function(){
24210         return this.trans ? true : false;
24211     },
24212
24213     /**
24214      * Abort the current server request.
24215      */
24216     abort : function(){
24217         if(this.isLoading()){
24218             this.destroyTrans(this.trans);
24219         }
24220     },
24221
24222     // private
24223     destroyTrans : function(trans, isLoaded){
24224         this.head.removeChild(document.getElementById(trans.scriptId));
24225         clearTimeout(trans.timeoutId);
24226         if(isLoaded){
24227             window[trans.cb] = undefined;
24228             try{
24229                 delete window[trans.cb];
24230             }catch(e){}
24231         }else{
24232             // if hasn't been loaded, wait for load to remove it to prevent script error
24233             window[trans.cb] = function(){
24234                 window[trans.cb] = undefined;
24235                 try{
24236                     delete window[trans.cb];
24237                 }catch(e){}
24238             };
24239         }
24240     },
24241
24242     // private
24243     handleResponse : function(o, trans){
24244         this.trans = false;
24245         this.destroyTrans(trans, true);
24246         var result;
24247         try {
24248             result = trans.reader.readRecords(o);
24249         }catch(e){
24250             this.fireEvent("loadexception", this, o, trans.arg, e);
24251             trans.callback.call(trans.scope||window, null, trans.arg, false);
24252             return;
24253         }
24254         this.fireEvent("load", this, o, trans.arg);
24255         trans.callback.call(trans.scope||window, result, trans.arg, true);
24256     },
24257
24258     // private
24259     handleFailure : function(trans){
24260         this.trans = false;
24261         this.destroyTrans(trans, false);
24262         this.fireEvent("loadexception", this, null, trans.arg);
24263         trans.callback.call(trans.scope||window, null, trans.arg, false);
24264     }
24265 });/*
24266  * Based on:
24267  * Ext JS Library 1.1.1
24268  * Copyright(c) 2006-2007, Ext JS, LLC.
24269  *
24270  * Originally Released Under LGPL - original licence link has changed is not relivant.
24271  *
24272  * Fork - LGPL
24273  * <script type="text/javascript">
24274  */
24275
24276 /**
24277  * @class Roo.data.JsonReader
24278  * @extends Roo.data.DataReader
24279  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24280  * based on mappings in a provided Roo.data.Record constructor.
24281  * 
24282  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24283  * in the reply previously. 
24284  * 
24285  * <p>
24286  * Example code:
24287  * <pre><code>
24288 var RecordDef = Roo.data.Record.create([
24289     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24290     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24291 ]);
24292 var myReader = new Roo.data.JsonReader({
24293     totalProperty: "results",    // The property which contains the total dataset size (optional)
24294     root: "rows",                // The property which contains an Array of row objects
24295     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24296 }, RecordDef);
24297 </code></pre>
24298  * <p>
24299  * This would consume a JSON file like this:
24300  * <pre><code>
24301 { 'results': 2, 'rows': [
24302     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24303     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24304 }
24305 </code></pre>
24306  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24307  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24308  * paged from the remote server.
24309  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24310  * @cfg {String} root name of the property which contains the Array of row objects.
24311  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24312  * @cfg {Array} fields Array of field definition objects
24313  * @constructor
24314  * Create a new JsonReader
24315  * @param {Object} meta Metadata configuration options
24316  * @param {Object} recordType Either an Array of field definition objects,
24317  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24318  */
24319 Roo.data.JsonReader = function(meta, recordType){
24320     
24321     meta = meta || {};
24322     // set some defaults:
24323     Roo.applyIf(meta, {
24324         totalProperty: 'total',
24325         successProperty : 'success',
24326         root : 'data',
24327         id : 'id'
24328     });
24329     
24330     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24331 };
24332 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24333     
24334     readerType : 'Json',
24335     
24336     /**
24337      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24338      * Used by Store query builder to append _requestMeta to params.
24339      * 
24340      */
24341     metaFromRemote : false,
24342     /**
24343      * This method is only used by a DataProxy which has retrieved data from a remote server.
24344      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24345      * @return {Object} data A data block which is used by an Roo.data.Store object as
24346      * a cache of Roo.data.Records.
24347      */
24348     read : function(response){
24349         var json = response.responseText;
24350        
24351         var o = /* eval:var:o */ eval("("+json+")");
24352         if(!o) {
24353             throw {message: "JsonReader.read: Json object not found"};
24354         }
24355         
24356         if(o.metaData){
24357             
24358             delete this.ef;
24359             this.metaFromRemote = true;
24360             this.meta = o.metaData;
24361             this.recordType = Roo.data.Record.create(o.metaData.fields);
24362             this.onMetaChange(this.meta, this.recordType, o);
24363         }
24364         return this.readRecords(o);
24365     },
24366
24367     // private function a store will implement
24368     onMetaChange : function(meta, recordType, o){
24369
24370     },
24371
24372     /**
24373          * @ignore
24374          */
24375     simpleAccess: function(obj, subsc) {
24376         return obj[subsc];
24377     },
24378
24379         /**
24380          * @ignore
24381          */
24382     getJsonAccessor: function(){
24383         var re = /[\[\.]/;
24384         return function(expr) {
24385             try {
24386                 return(re.test(expr))
24387                     ? new Function("obj", "return obj." + expr)
24388                     : function(obj){
24389                         return obj[expr];
24390                     };
24391             } catch(e){}
24392             return Roo.emptyFn;
24393         };
24394     }(),
24395
24396     /**
24397      * Create a data block containing Roo.data.Records from an XML document.
24398      * @param {Object} o An object which contains an Array of row objects in the property specified
24399      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24400      * which contains the total size of the dataset.
24401      * @return {Object} data A data block which is used by an Roo.data.Store object as
24402      * a cache of Roo.data.Records.
24403      */
24404     readRecords : function(o){
24405         /**
24406          * After any data loads, the raw JSON data is available for further custom processing.
24407          * @type Object
24408          */
24409         this.o = o;
24410         var s = this.meta, Record = this.recordType,
24411             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24412
24413 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24414         if (!this.ef) {
24415             if(s.totalProperty) {
24416                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24417                 }
24418                 if(s.successProperty) {
24419                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24420                 }
24421                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24422                 if (s.id) {
24423                         var g = this.getJsonAccessor(s.id);
24424                         this.getId = function(rec) {
24425                                 var r = g(rec);  
24426                                 return (r === undefined || r === "") ? null : r;
24427                         };
24428                 } else {
24429                         this.getId = function(){return null;};
24430                 }
24431             this.ef = [];
24432             for(var jj = 0; jj < fl; jj++){
24433                 f = fi[jj];
24434                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24435                 this.ef[jj] = this.getJsonAccessor(map);
24436             }
24437         }
24438
24439         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24440         if(s.totalProperty){
24441             var vt = parseInt(this.getTotal(o), 10);
24442             if(!isNaN(vt)){
24443                 totalRecords = vt;
24444             }
24445         }
24446         if(s.successProperty){
24447             var vs = this.getSuccess(o);
24448             if(vs === false || vs === 'false'){
24449                 success = false;
24450             }
24451         }
24452         var records = [];
24453         for(var i = 0; i < c; i++){
24454                 var n = root[i];
24455             var values = {};
24456             var id = this.getId(n);
24457             for(var j = 0; j < fl; j++){
24458                 f = fi[j];
24459             var v = this.ef[j](n);
24460             if (!f.convert) {
24461                 Roo.log('missing convert for ' + f.name);
24462                 Roo.log(f);
24463                 continue;
24464             }
24465             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24466             }
24467             var record = new Record(values, id);
24468             record.json = n;
24469             records[i] = record;
24470         }
24471         return {
24472             raw : o,
24473             success : success,
24474             records : records,
24475             totalRecords : totalRecords
24476         };
24477     },
24478     // used when loading children.. @see loadDataFromChildren
24479     toLoadData: function(rec)
24480     {
24481         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24482         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24483         return { data : data, total : data.length };
24484         
24485     }
24486 });/*
24487  * Based on:
24488  * Ext JS Library 1.1.1
24489  * Copyright(c) 2006-2007, Ext JS, LLC.
24490  *
24491  * Originally Released Under LGPL - original licence link has changed is not relivant.
24492  *
24493  * Fork - LGPL
24494  * <script type="text/javascript">
24495  */
24496
24497 /**
24498  * @class Roo.data.XmlReader
24499  * @extends Roo.data.DataReader
24500  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24501  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24502  * <p>
24503  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24504  * header in the HTTP response must be set to "text/xml".</em>
24505  * <p>
24506  * Example code:
24507  * <pre><code>
24508 var RecordDef = Roo.data.Record.create([
24509    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24510    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24511 ]);
24512 var myReader = new Roo.data.XmlReader({
24513    totalRecords: "results", // The element which contains the total dataset size (optional)
24514    record: "row",           // The repeated element which contains row information
24515    id: "id"                 // The element within the row that provides an ID for the record (optional)
24516 }, RecordDef);
24517 </code></pre>
24518  * <p>
24519  * This would consume an XML file like this:
24520  * <pre><code>
24521 &lt;?xml?>
24522 &lt;dataset>
24523  &lt;results>2&lt;/results>
24524  &lt;row>
24525    &lt;id>1&lt;/id>
24526    &lt;name>Bill&lt;/name>
24527    &lt;occupation>Gardener&lt;/occupation>
24528  &lt;/row>
24529  &lt;row>
24530    &lt;id>2&lt;/id>
24531    &lt;name>Ben&lt;/name>
24532    &lt;occupation>Horticulturalist&lt;/occupation>
24533  &lt;/row>
24534 &lt;/dataset>
24535 </code></pre>
24536  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24537  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24538  * paged from the remote server.
24539  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24540  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24541  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24542  * a record identifier value.
24543  * @constructor
24544  * Create a new XmlReader
24545  * @param {Object} meta Metadata configuration options
24546  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24547  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24548  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24549  */
24550 Roo.data.XmlReader = function(meta, recordType){
24551     meta = meta || {};
24552     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24553 };
24554 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24555     
24556     readerType : 'Xml',
24557     
24558     /**
24559      * This method is only used by a DataProxy which has retrieved data from a remote server.
24560          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24561          * to contain a method called 'responseXML' that returns an XML document object.
24562      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24563      * a cache of Roo.data.Records.
24564      */
24565     read : function(response){
24566         var doc = response.responseXML;
24567         if(!doc) {
24568             throw {message: "XmlReader.read: XML Document not available"};
24569         }
24570         return this.readRecords(doc);
24571     },
24572
24573     /**
24574      * Create a data block containing Roo.data.Records from an XML document.
24575          * @param {Object} doc A parsed XML document.
24576      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24577      * a cache of Roo.data.Records.
24578      */
24579     readRecords : function(doc){
24580         /**
24581          * After any data loads/reads, the raw XML Document is available for further custom processing.
24582          * @type XMLDocument
24583          */
24584         this.xmlData = doc;
24585         var root = doc.documentElement || doc;
24586         var q = Roo.DomQuery;
24587         var recordType = this.recordType, fields = recordType.prototype.fields;
24588         var sid = this.meta.id;
24589         var totalRecords = 0, success = true;
24590         if(this.meta.totalRecords){
24591             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24592         }
24593         
24594         if(this.meta.success){
24595             var sv = q.selectValue(this.meta.success, root, true);
24596             success = sv !== false && sv !== 'false';
24597         }
24598         var records = [];
24599         var ns = q.select(this.meta.record, root);
24600         for(var i = 0, len = ns.length; i < len; i++) {
24601                 var n = ns[i];
24602                 var values = {};
24603                 var id = sid ? q.selectValue(sid, n) : undefined;
24604                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24605                     var f = fields.items[j];
24606                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24607                     v = f.convert(v);
24608                     values[f.name] = v;
24609                 }
24610                 var record = new recordType(values, id);
24611                 record.node = n;
24612                 records[records.length] = record;
24613             }
24614
24615             return {
24616                 success : success,
24617                 records : records,
24618                 totalRecords : totalRecords || records.length
24619             };
24620     }
24621 });/*
24622  * Based on:
24623  * Ext JS Library 1.1.1
24624  * Copyright(c) 2006-2007, Ext JS, LLC.
24625  *
24626  * Originally Released Under LGPL - original licence link has changed is not relivant.
24627  *
24628  * Fork - LGPL
24629  * <script type="text/javascript">
24630  */
24631
24632 /**
24633  * @class Roo.data.ArrayReader
24634  * @extends Roo.data.DataReader
24635  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24636  * Each element of that Array represents a row of data fields. The
24637  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24638  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24639  * <p>
24640  * Example code:.
24641  * <pre><code>
24642 var RecordDef = Roo.data.Record.create([
24643     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24644     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24645 ]);
24646 var myReader = new Roo.data.ArrayReader({
24647     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24648 }, RecordDef);
24649 </code></pre>
24650  * <p>
24651  * This would consume an Array like this:
24652  * <pre><code>
24653 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24654   </code></pre>
24655  
24656  * @constructor
24657  * Create a new JsonReader
24658  * @param {Object} meta Metadata configuration options.
24659  * @param {Object|Array} recordType Either an Array of field definition objects
24660  * 
24661  * @cfg {Array} fields Array of field definition objects
24662  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24663  * as specified to {@link Roo.data.Record#create},
24664  * or an {@link Roo.data.Record} object
24665  *
24666  * 
24667  * created using {@link Roo.data.Record#create}.
24668  */
24669 Roo.data.ArrayReader = function(meta, recordType)
24670 {    
24671     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24672 };
24673
24674 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24675     
24676       /**
24677      * Create a data block containing Roo.data.Records from an XML document.
24678      * @param {Object} o An Array of row objects which represents the dataset.
24679      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24680      * a cache of Roo.data.Records.
24681      */
24682     readRecords : function(o)
24683     {
24684         var sid = this.meta ? this.meta.id : null;
24685         var recordType = this.recordType, fields = recordType.prototype.fields;
24686         var records = [];
24687         var root = o;
24688         for(var i = 0; i < root.length; i++){
24689                 var n = root[i];
24690             var values = {};
24691             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24692             for(var j = 0, jlen = fields.length; j < jlen; j++){
24693                 var f = fields.items[j];
24694                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24695                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24696                 v = f.convert(v);
24697                 values[f.name] = v;
24698             }
24699             var record = new recordType(values, id);
24700             record.json = n;
24701             records[records.length] = record;
24702         }
24703         return {
24704             records : records,
24705             totalRecords : records.length
24706         };
24707     },
24708     // used when loading children.. @see loadDataFromChildren
24709     toLoadData: function(rec)
24710     {
24711         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24712         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24713         
24714     }
24715     
24716     
24717 });/*
24718  * Based on:
24719  * Ext JS Library 1.1.1
24720  * Copyright(c) 2006-2007, Ext JS, LLC.
24721  *
24722  * Originally Released Under LGPL - original licence link has changed is not relivant.
24723  *
24724  * Fork - LGPL
24725  * <script type="text/javascript">
24726  */
24727
24728
24729 /**
24730  * @class Roo.data.Tree
24731  * @extends Roo.util.Observable
24732  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24733  * in the tree have most standard DOM functionality.
24734  * @constructor
24735  * @param {Node} root (optional) The root node
24736  */
24737 Roo.data.Tree = function(root){
24738    this.nodeHash = {};
24739    /**
24740     * The root node for this tree
24741     * @type Node
24742     */
24743    this.root = null;
24744    if(root){
24745        this.setRootNode(root);
24746    }
24747    this.addEvents({
24748        /**
24749         * @event append
24750         * Fires when a new child node is appended to a node in this tree.
24751         * @param {Tree} tree The owner tree
24752         * @param {Node} parent The parent node
24753         * @param {Node} node The newly appended node
24754         * @param {Number} index The index of the newly appended node
24755         */
24756        "append" : true,
24757        /**
24758         * @event remove
24759         * Fires when a child node is removed from a node in this tree.
24760         * @param {Tree} tree The owner tree
24761         * @param {Node} parent The parent node
24762         * @param {Node} node The child node removed
24763         */
24764        "remove" : true,
24765        /**
24766         * @event move
24767         * Fires when a node is moved to a new location in the tree
24768         * @param {Tree} tree The owner tree
24769         * @param {Node} node The node moved
24770         * @param {Node} oldParent The old parent of this node
24771         * @param {Node} newParent The new parent of this node
24772         * @param {Number} index The index it was moved to
24773         */
24774        "move" : true,
24775        /**
24776         * @event insert
24777         * Fires when a new child node is inserted in a node in this tree.
24778         * @param {Tree} tree The owner tree
24779         * @param {Node} parent The parent node
24780         * @param {Node} node The child node inserted
24781         * @param {Node} refNode The child node the node was inserted before
24782         */
24783        "insert" : true,
24784        /**
24785         * @event beforeappend
24786         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24787         * @param {Tree} tree The owner tree
24788         * @param {Node} parent The parent node
24789         * @param {Node} node The child node to be appended
24790         */
24791        "beforeappend" : true,
24792        /**
24793         * @event beforeremove
24794         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24795         * @param {Tree} tree The owner tree
24796         * @param {Node} parent The parent node
24797         * @param {Node} node The child node to be removed
24798         */
24799        "beforeremove" : true,
24800        /**
24801         * @event beforemove
24802         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24803         * @param {Tree} tree The owner tree
24804         * @param {Node} node The node being moved
24805         * @param {Node} oldParent The parent of the node
24806         * @param {Node} newParent The new parent the node is moving to
24807         * @param {Number} index The index it is being moved to
24808         */
24809        "beforemove" : true,
24810        /**
24811         * @event beforeinsert
24812         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24813         * @param {Tree} tree The owner tree
24814         * @param {Node} parent The parent node
24815         * @param {Node} node The child node to be inserted
24816         * @param {Node} refNode The child node the node is being inserted before
24817         */
24818        "beforeinsert" : true
24819    });
24820
24821     Roo.data.Tree.superclass.constructor.call(this);
24822 };
24823
24824 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24825     pathSeparator: "/",
24826
24827     proxyNodeEvent : function(){
24828         return this.fireEvent.apply(this, arguments);
24829     },
24830
24831     /**
24832      * Returns the root node for this tree.
24833      * @return {Node}
24834      */
24835     getRootNode : function(){
24836         return this.root;
24837     },
24838
24839     /**
24840      * Sets the root node for this tree.
24841      * @param {Node} node
24842      * @return {Node}
24843      */
24844     setRootNode : function(node){
24845         this.root = node;
24846         node.ownerTree = this;
24847         node.isRoot = true;
24848         this.registerNode(node);
24849         return node;
24850     },
24851
24852     /**
24853      * Gets a node in this tree by its id.
24854      * @param {String} id
24855      * @return {Node}
24856      */
24857     getNodeById : function(id){
24858         return this.nodeHash[id];
24859     },
24860
24861     registerNode : function(node){
24862         this.nodeHash[node.id] = node;
24863     },
24864
24865     unregisterNode : function(node){
24866         delete this.nodeHash[node.id];
24867     },
24868
24869     toString : function(){
24870         return "[Tree"+(this.id?" "+this.id:"")+"]";
24871     }
24872 });
24873
24874 /**
24875  * @class Roo.data.Node
24876  * @extends Roo.util.Observable
24877  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24878  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24879  * @constructor
24880  * @param {Object} attributes The attributes/config for the node
24881  */
24882 Roo.data.Node = function(attributes){
24883     /**
24884      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24885      * @type {Object}
24886      */
24887     this.attributes = attributes || {};
24888     this.leaf = this.attributes.leaf;
24889     /**
24890      * The node id. @type String
24891      */
24892     this.id = this.attributes.id;
24893     if(!this.id){
24894         this.id = Roo.id(null, "ynode-");
24895         this.attributes.id = this.id;
24896     }
24897      
24898     
24899     /**
24900      * All child nodes of this node. @type Array
24901      */
24902     this.childNodes = [];
24903     if(!this.childNodes.indexOf){ // indexOf is a must
24904         this.childNodes.indexOf = function(o){
24905             for(var i = 0, len = this.length; i < len; i++){
24906                 if(this[i] == o) {
24907                     return i;
24908                 }
24909             }
24910             return -1;
24911         };
24912     }
24913     /**
24914      * The parent node for this node. @type Node
24915      */
24916     this.parentNode = null;
24917     /**
24918      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24919      */
24920     this.firstChild = null;
24921     /**
24922      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24923      */
24924     this.lastChild = null;
24925     /**
24926      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24927      */
24928     this.previousSibling = null;
24929     /**
24930      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24931      */
24932     this.nextSibling = null;
24933
24934     this.addEvents({
24935        /**
24936         * @event append
24937         * Fires when a new child node is appended
24938         * @param {Tree} tree The owner tree
24939         * @param {Node} this This node
24940         * @param {Node} node The newly appended node
24941         * @param {Number} index The index of the newly appended node
24942         */
24943        "append" : true,
24944        /**
24945         * @event remove
24946         * Fires when a child node is removed
24947         * @param {Tree} tree The owner tree
24948         * @param {Node} this This node
24949         * @param {Node} node The removed node
24950         */
24951        "remove" : true,
24952        /**
24953         * @event move
24954         * Fires when this node is moved to a new location in the tree
24955         * @param {Tree} tree The owner tree
24956         * @param {Node} this This node
24957         * @param {Node} oldParent The old parent of this node
24958         * @param {Node} newParent The new parent of this node
24959         * @param {Number} index The index it was moved to
24960         */
24961        "move" : true,
24962        /**
24963         * @event insert
24964         * Fires when a new child node is inserted.
24965         * @param {Tree} tree The owner tree
24966         * @param {Node} this This node
24967         * @param {Node} node The child node inserted
24968         * @param {Node} refNode The child node the node was inserted before
24969         */
24970        "insert" : true,
24971        /**
24972         * @event beforeappend
24973         * Fires before a new child is appended, return false to cancel the append.
24974         * @param {Tree} tree The owner tree
24975         * @param {Node} this This node
24976         * @param {Node} node The child node to be appended
24977         */
24978        "beforeappend" : true,
24979        /**
24980         * @event beforeremove
24981         * Fires before a child is removed, return false to cancel the remove.
24982         * @param {Tree} tree The owner tree
24983         * @param {Node} this This node
24984         * @param {Node} node The child node to be removed
24985         */
24986        "beforeremove" : true,
24987        /**
24988         * @event beforemove
24989         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24990         * @param {Tree} tree The owner tree
24991         * @param {Node} this This node
24992         * @param {Node} oldParent The parent of this node
24993         * @param {Node} newParent The new parent this node is moving to
24994         * @param {Number} index The index it is being moved to
24995         */
24996        "beforemove" : true,
24997        /**
24998         * @event beforeinsert
24999         * Fires before a new child is inserted, return false to cancel the insert.
25000         * @param {Tree} tree The owner tree
25001         * @param {Node} this This node
25002         * @param {Node} node The child node to be inserted
25003         * @param {Node} refNode The child node the node is being inserted before
25004         */
25005        "beforeinsert" : true
25006    });
25007     this.listeners = this.attributes.listeners;
25008     Roo.data.Node.superclass.constructor.call(this);
25009 };
25010
25011 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25012     fireEvent : function(evtName){
25013         // first do standard event for this node
25014         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25015             return false;
25016         }
25017         // then bubble it up to the tree if the event wasn't cancelled
25018         var ot = this.getOwnerTree();
25019         if(ot){
25020             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25021                 return false;
25022             }
25023         }
25024         return true;
25025     },
25026
25027     /**
25028      * Returns true if this node is a leaf
25029      * @return {Boolean}
25030      */
25031     isLeaf : function(){
25032         return this.leaf === true;
25033     },
25034
25035     // private
25036     setFirstChild : function(node){
25037         this.firstChild = node;
25038     },
25039
25040     //private
25041     setLastChild : function(node){
25042         this.lastChild = node;
25043     },
25044
25045
25046     /**
25047      * Returns true if this node is the last child of its parent
25048      * @return {Boolean}
25049      */
25050     isLast : function(){
25051        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25052     },
25053
25054     /**
25055      * Returns true if this node is the first child of its parent
25056      * @return {Boolean}
25057      */
25058     isFirst : function(){
25059        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25060     },
25061
25062     hasChildNodes : function(){
25063         return !this.isLeaf() && this.childNodes.length > 0;
25064     },
25065
25066     /**
25067      * Insert node(s) as the last child node of this node.
25068      * @param {Node/Array} node The node or Array of nodes to append
25069      * @return {Node} The appended node if single append, or null if an array was passed
25070      */
25071     appendChild : function(node){
25072         var multi = false;
25073         if(node instanceof Array){
25074             multi = node;
25075         }else if(arguments.length > 1){
25076             multi = arguments;
25077         }
25078         
25079         // if passed an array or multiple args do them one by one
25080         if(multi){
25081             for(var i = 0, len = multi.length; i < len; i++) {
25082                 this.appendChild(multi[i]);
25083             }
25084         }else{
25085             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25086                 return false;
25087             }
25088             var index = this.childNodes.length;
25089             var oldParent = node.parentNode;
25090             // it's a move, make sure we move it cleanly
25091             if(oldParent){
25092                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25093                     return false;
25094                 }
25095                 oldParent.removeChild(node);
25096             }
25097             
25098             index = this.childNodes.length;
25099             if(index == 0){
25100                 this.setFirstChild(node);
25101             }
25102             this.childNodes.push(node);
25103             node.parentNode = this;
25104             var ps = this.childNodes[index-1];
25105             if(ps){
25106                 node.previousSibling = ps;
25107                 ps.nextSibling = node;
25108             }else{
25109                 node.previousSibling = null;
25110             }
25111             node.nextSibling = null;
25112             this.setLastChild(node);
25113             node.setOwnerTree(this.getOwnerTree());
25114             this.fireEvent("append", this.ownerTree, this, node, index);
25115             if(this.ownerTree) {
25116                 this.ownerTree.fireEvent("appendnode", this, node, index);
25117             }
25118             if(oldParent){
25119                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25120             }
25121             return node;
25122         }
25123     },
25124
25125     /**
25126      * Removes a child node from this node.
25127      * @param {Node} node The node to remove
25128      * @return {Node} The removed node
25129      */
25130     removeChild : function(node){
25131         var index = this.childNodes.indexOf(node);
25132         if(index == -1){
25133             return false;
25134         }
25135         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25136             return false;
25137         }
25138
25139         // remove it from childNodes collection
25140         this.childNodes.splice(index, 1);
25141
25142         // update siblings
25143         if(node.previousSibling){
25144             node.previousSibling.nextSibling = node.nextSibling;
25145         }
25146         if(node.nextSibling){
25147             node.nextSibling.previousSibling = node.previousSibling;
25148         }
25149
25150         // update child refs
25151         if(this.firstChild == node){
25152             this.setFirstChild(node.nextSibling);
25153         }
25154         if(this.lastChild == node){
25155             this.setLastChild(node.previousSibling);
25156         }
25157
25158         node.setOwnerTree(null);
25159         // clear any references from the node
25160         node.parentNode = null;
25161         node.previousSibling = null;
25162         node.nextSibling = null;
25163         this.fireEvent("remove", this.ownerTree, this, node);
25164         return node;
25165     },
25166
25167     /**
25168      * Inserts the first node before the second node in this nodes childNodes collection.
25169      * @param {Node} node The node to insert
25170      * @param {Node} refNode The node to insert before (if null the node is appended)
25171      * @return {Node} The inserted node
25172      */
25173     insertBefore : function(node, refNode){
25174         if(!refNode){ // like standard Dom, refNode can be null for append
25175             return this.appendChild(node);
25176         }
25177         // nothing to do
25178         if(node == refNode){
25179             return false;
25180         }
25181
25182         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25183             return false;
25184         }
25185         var index = this.childNodes.indexOf(refNode);
25186         var oldParent = node.parentNode;
25187         var refIndex = index;
25188
25189         // when moving internally, indexes will change after remove
25190         if(oldParent == this && this.childNodes.indexOf(node) < index){
25191             refIndex--;
25192         }
25193
25194         // it's a move, make sure we move it cleanly
25195         if(oldParent){
25196             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25197                 return false;
25198             }
25199             oldParent.removeChild(node);
25200         }
25201         if(refIndex == 0){
25202             this.setFirstChild(node);
25203         }
25204         this.childNodes.splice(refIndex, 0, node);
25205         node.parentNode = this;
25206         var ps = this.childNodes[refIndex-1];
25207         if(ps){
25208             node.previousSibling = ps;
25209             ps.nextSibling = node;
25210         }else{
25211             node.previousSibling = null;
25212         }
25213         node.nextSibling = refNode;
25214         refNode.previousSibling = node;
25215         node.setOwnerTree(this.getOwnerTree());
25216         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25217         if(oldParent){
25218             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25219         }
25220         return node;
25221     },
25222
25223     /**
25224      * Returns the child node at the specified index.
25225      * @param {Number} index
25226      * @return {Node}
25227      */
25228     item : function(index){
25229         return this.childNodes[index];
25230     },
25231
25232     /**
25233      * Replaces one child node in this node with another.
25234      * @param {Node} newChild The replacement node
25235      * @param {Node} oldChild The node to replace
25236      * @return {Node} The replaced node
25237      */
25238     replaceChild : function(newChild, oldChild){
25239         this.insertBefore(newChild, oldChild);
25240         this.removeChild(oldChild);
25241         return oldChild;
25242     },
25243
25244     /**
25245      * Returns the index of a child node
25246      * @param {Node} node
25247      * @return {Number} The index of the node or -1 if it was not found
25248      */
25249     indexOf : function(child){
25250         return this.childNodes.indexOf(child);
25251     },
25252
25253     /**
25254      * Returns the tree this node is in.
25255      * @return {Tree}
25256      */
25257     getOwnerTree : function(){
25258         // if it doesn't have one, look for one
25259         if(!this.ownerTree){
25260             var p = this;
25261             while(p){
25262                 if(p.ownerTree){
25263                     this.ownerTree = p.ownerTree;
25264                     break;
25265                 }
25266                 p = p.parentNode;
25267             }
25268         }
25269         return this.ownerTree;
25270     },
25271
25272     /**
25273      * Returns depth of this node (the root node has a depth of 0)
25274      * @return {Number}
25275      */
25276     getDepth : function(){
25277         var depth = 0;
25278         var p = this;
25279         while(p.parentNode){
25280             ++depth;
25281             p = p.parentNode;
25282         }
25283         return depth;
25284     },
25285
25286     // private
25287     setOwnerTree : function(tree){
25288         // if it's move, we need to update everyone
25289         if(tree != this.ownerTree){
25290             if(this.ownerTree){
25291                 this.ownerTree.unregisterNode(this);
25292             }
25293             this.ownerTree = tree;
25294             var cs = this.childNodes;
25295             for(var i = 0, len = cs.length; i < len; i++) {
25296                 cs[i].setOwnerTree(tree);
25297             }
25298             if(tree){
25299                 tree.registerNode(this);
25300             }
25301         }
25302     },
25303
25304     /**
25305      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25306      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25307      * @return {String} The path
25308      */
25309     getPath : function(attr){
25310         attr = attr || "id";
25311         var p = this.parentNode;
25312         var b = [this.attributes[attr]];
25313         while(p){
25314             b.unshift(p.attributes[attr]);
25315             p = p.parentNode;
25316         }
25317         var sep = this.getOwnerTree().pathSeparator;
25318         return sep + b.join(sep);
25319     },
25320
25321     /**
25322      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25323      * function call will be the scope provided or the current node. The arguments to the function
25324      * will be the args provided or the current node. If the function returns false at any point,
25325      * the bubble is stopped.
25326      * @param {Function} fn The function to call
25327      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25328      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25329      */
25330     bubble : function(fn, scope, args){
25331         var p = this;
25332         while(p){
25333             if(fn.call(scope || p, args || p) === false){
25334                 break;
25335             }
25336             p = p.parentNode;
25337         }
25338     },
25339
25340     /**
25341      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25342      * function call will be the scope provided or the current node. The arguments to the function
25343      * will be the args provided or the current node. If the function returns false at any point,
25344      * the cascade is stopped on that branch.
25345      * @param {Function} fn The function to call
25346      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25347      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25348      */
25349     cascade : function(fn, scope, args){
25350         if(fn.call(scope || this, args || this) !== false){
25351             var cs = this.childNodes;
25352             for(var i = 0, len = cs.length; i < len; i++) {
25353                 cs[i].cascade(fn, scope, args);
25354             }
25355         }
25356     },
25357
25358     /**
25359      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25360      * function call will be the scope provided or the current node. The arguments to the function
25361      * will be the args provided or the current node. If the function returns false at any point,
25362      * the iteration stops.
25363      * @param {Function} fn The function to call
25364      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25365      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25366      */
25367     eachChild : function(fn, scope, args){
25368         var cs = this.childNodes;
25369         for(var i = 0, len = cs.length; i < len; i++) {
25370                 if(fn.call(scope || this, args || cs[i]) === false){
25371                     break;
25372                 }
25373         }
25374     },
25375
25376     /**
25377      * Finds the first child that has the attribute with the specified value.
25378      * @param {String} attribute The attribute name
25379      * @param {Mixed} value The value to search for
25380      * @return {Node} The found child or null if none was found
25381      */
25382     findChild : function(attribute, value){
25383         var cs = this.childNodes;
25384         for(var i = 0, len = cs.length; i < len; i++) {
25385                 if(cs[i].attributes[attribute] == value){
25386                     return cs[i];
25387                 }
25388         }
25389         return null;
25390     },
25391
25392     /**
25393      * Finds the first child by a custom function. The child matches if the function passed
25394      * returns true.
25395      * @param {Function} fn
25396      * @param {Object} scope (optional)
25397      * @return {Node} The found child or null if none was found
25398      */
25399     findChildBy : function(fn, scope){
25400         var cs = this.childNodes;
25401         for(var i = 0, len = cs.length; i < len; i++) {
25402                 if(fn.call(scope||cs[i], cs[i]) === true){
25403                     return cs[i];
25404                 }
25405         }
25406         return null;
25407     },
25408
25409     /**
25410      * Sorts this nodes children using the supplied sort function
25411      * @param {Function} fn
25412      * @param {Object} scope (optional)
25413      */
25414     sort : function(fn, scope){
25415         var cs = this.childNodes;
25416         var len = cs.length;
25417         if(len > 0){
25418             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25419             cs.sort(sortFn);
25420             for(var i = 0; i < len; i++){
25421                 var n = cs[i];
25422                 n.previousSibling = cs[i-1];
25423                 n.nextSibling = cs[i+1];
25424                 if(i == 0){
25425                     this.setFirstChild(n);
25426                 }
25427                 if(i == len-1){
25428                     this.setLastChild(n);
25429                 }
25430             }
25431         }
25432     },
25433
25434     /**
25435      * Returns true if this node is an ancestor (at any point) of the passed node.
25436      * @param {Node} node
25437      * @return {Boolean}
25438      */
25439     contains : function(node){
25440         return node.isAncestor(this);
25441     },
25442
25443     /**
25444      * Returns true if the passed node is an ancestor (at any point) of this node.
25445      * @param {Node} node
25446      * @return {Boolean}
25447      */
25448     isAncestor : function(node){
25449         var p = this.parentNode;
25450         while(p){
25451             if(p == node){
25452                 return true;
25453             }
25454             p = p.parentNode;
25455         }
25456         return false;
25457     },
25458
25459     toString : function(){
25460         return "[Node"+(this.id?" "+this.id:"")+"]";
25461     }
25462 });/*
25463  * Based on:
25464  * Ext JS Library 1.1.1
25465  * Copyright(c) 2006-2007, Ext JS, LLC.
25466  *
25467  * Originally Released Under LGPL - original licence link has changed is not relivant.
25468  *
25469  * Fork - LGPL
25470  * <script type="text/javascript">
25471  */
25472  (function(){ 
25473 /**
25474  * @class Roo.Layer
25475  * @extends Roo.Element
25476  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25477  * automatic maintaining of shadow/shim positions.
25478  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25479  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25480  * you can pass a string with a CSS class name. False turns off the shadow.
25481  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25482  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25483  * @cfg {String} cls CSS class to add to the element
25484  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25485  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25486  * @constructor
25487  * @param {Object} config An object with config options.
25488  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25489  */
25490
25491 Roo.Layer = function(config, existingEl){
25492     config = config || {};
25493     var dh = Roo.DomHelper;
25494     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25495     if(existingEl){
25496         this.dom = Roo.getDom(existingEl);
25497     }
25498     if(!this.dom){
25499         var o = config.dh || {tag: "div", cls: "x-layer"};
25500         this.dom = dh.append(pel, o);
25501     }
25502     if(config.cls){
25503         this.addClass(config.cls);
25504     }
25505     this.constrain = config.constrain !== false;
25506     this.visibilityMode = Roo.Element.VISIBILITY;
25507     if(config.id){
25508         this.id = this.dom.id = config.id;
25509     }else{
25510         this.id = Roo.id(this.dom);
25511     }
25512     this.zindex = config.zindex || this.getZIndex();
25513     this.position("absolute", this.zindex);
25514     if(config.shadow){
25515         this.shadowOffset = config.shadowOffset || 4;
25516         this.shadow = new Roo.Shadow({
25517             offset : this.shadowOffset,
25518             mode : config.shadow
25519         });
25520     }else{
25521         this.shadowOffset = 0;
25522     }
25523     this.useShim = config.shim !== false && Roo.useShims;
25524     this.useDisplay = config.useDisplay;
25525     this.hide();
25526 };
25527
25528 var supr = Roo.Element.prototype;
25529
25530 // shims are shared among layer to keep from having 100 iframes
25531 var shims = [];
25532
25533 Roo.extend(Roo.Layer, Roo.Element, {
25534
25535     getZIndex : function(){
25536         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25537     },
25538
25539     getShim : function(){
25540         if(!this.useShim){
25541             return null;
25542         }
25543         if(this.shim){
25544             return this.shim;
25545         }
25546         var shim = shims.shift();
25547         if(!shim){
25548             shim = this.createShim();
25549             shim.enableDisplayMode('block');
25550             shim.dom.style.display = 'none';
25551             shim.dom.style.visibility = 'visible';
25552         }
25553         var pn = this.dom.parentNode;
25554         if(shim.dom.parentNode != pn){
25555             pn.insertBefore(shim.dom, this.dom);
25556         }
25557         shim.setStyle('z-index', this.getZIndex()-2);
25558         this.shim = shim;
25559         return shim;
25560     },
25561
25562     hideShim : function(){
25563         if(this.shim){
25564             this.shim.setDisplayed(false);
25565             shims.push(this.shim);
25566             delete this.shim;
25567         }
25568     },
25569
25570     disableShadow : function(){
25571         if(this.shadow){
25572             this.shadowDisabled = true;
25573             this.shadow.hide();
25574             this.lastShadowOffset = this.shadowOffset;
25575             this.shadowOffset = 0;
25576         }
25577     },
25578
25579     enableShadow : function(show){
25580         if(this.shadow){
25581             this.shadowDisabled = false;
25582             this.shadowOffset = this.lastShadowOffset;
25583             delete this.lastShadowOffset;
25584             if(show){
25585                 this.sync(true);
25586             }
25587         }
25588     },
25589
25590     // private
25591     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25592     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25593     sync : function(doShow){
25594         var sw = this.shadow;
25595         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25596             var sh = this.getShim();
25597
25598             var w = this.getWidth(),
25599                 h = this.getHeight();
25600
25601             var l = this.getLeft(true),
25602                 t = this.getTop(true);
25603
25604             if(sw && !this.shadowDisabled){
25605                 if(doShow && !sw.isVisible()){
25606                     sw.show(this);
25607                 }else{
25608                     sw.realign(l, t, w, h);
25609                 }
25610                 if(sh){
25611                     if(doShow){
25612                        sh.show();
25613                     }
25614                     // fit the shim behind the shadow, so it is shimmed too
25615                     var a = sw.adjusts, s = sh.dom.style;
25616                     s.left = (Math.min(l, l+a.l))+"px";
25617                     s.top = (Math.min(t, t+a.t))+"px";
25618                     s.width = (w+a.w)+"px";
25619                     s.height = (h+a.h)+"px";
25620                 }
25621             }else if(sh){
25622                 if(doShow){
25623                    sh.show();
25624                 }
25625                 sh.setSize(w, h);
25626                 sh.setLeftTop(l, t);
25627             }
25628             
25629         }
25630     },
25631
25632     // private
25633     destroy : function(){
25634         this.hideShim();
25635         if(this.shadow){
25636             this.shadow.hide();
25637         }
25638         this.removeAllListeners();
25639         var pn = this.dom.parentNode;
25640         if(pn){
25641             pn.removeChild(this.dom);
25642         }
25643         Roo.Element.uncache(this.id);
25644     },
25645
25646     remove : function(){
25647         this.destroy();
25648     },
25649
25650     // private
25651     beginUpdate : function(){
25652         this.updating = true;
25653     },
25654
25655     // private
25656     endUpdate : function(){
25657         this.updating = false;
25658         this.sync(true);
25659     },
25660
25661     // private
25662     hideUnders : function(negOffset){
25663         if(this.shadow){
25664             this.shadow.hide();
25665         }
25666         this.hideShim();
25667     },
25668
25669     // private
25670     constrainXY : function(){
25671         if(this.constrain){
25672             var vw = Roo.lib.Dom.getViewWidth(),
25673                 vh = Roo.lib.Dom.getViewHeight();
25674             var s = Roo.get(document).getScroll();
25675
25676             var xy = this.getXY();
25677             var x = xy[0], y = xy[1];   
25678             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25679             // only move it if it needs it
25680             var moved = false;
25681             // first validate right/bottom
25682             if((x + w) > vw+s.left){
25683                 x = vw - w - this.shadowOffset;
25684                 moved = true;
25685             }
25686             if((y + h) > vh+s.top){
25687                 y = vh - h - this.shadowOffset;
25688                 moved = true;
25689             }
25690             // then make sure top/left isn't negative
25691             if(x < s.left){
25692                 x = s.left;
25693                 moved = true;
25694             }
25695             if(y < s.top){
25696                 y = s.top;
25697                 moved = true;
25698             }
25699             if(moved){
25700                 if(this.avoidY){
25701                     var ay = this.avoidY;
25702                     if(y <= ay && (y+h) >= ay){
25703                         y = ay-h-5;   
25704                     }
25705                 }
25706                 xy = [x, y];
25707                 this.storeXY(xy);
25708                 supr.setXY.call(this, xy);
25709                 this.sync();
25710             }
25711         }
25712     },
25713
25714     isVisible : function(){
25715         return this.visible;    
25716     },
25717
25718     // private
25719     showAction : function(){
25720         this.visible = true; // track visibility to prevent getStyle calls
25721         if(this.useDisplay === true){
25722             this.setDisplayed("");
25723         }else if(this.lastXY){
25724             supr.setXY.call(this, this.lastXY);
25725         }else if(this.lastLT){
25726             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25727         }
25728     },
25729
25730     // private
25731     hideAction : function(){
25732         this.visible = false;
25733         if(this.useDisplay === true){
25734             this.setDisplayed(false);
25735         }else{
25736             this.setLeftTop(-10000,-10000);
25737         }
25738     },
25739
25740     // overridden Element method
25741     setVisible : function(v, a, d, c, e){
25742         if(v){
25743             this.showAction();
25744         }
25745         if(a && v){
25746             var cb = function(){
25747                 this.sync(true);
25748                 if(c){
25749                     c();
25750                 }
25751             }.createDelegate(this);
25752             supr.setVisible.call(this, true, true, d, cb, e);
25753         }else{
25754             if(!v){
25755                 this.hideUnders(true);
25756             }
25757             var cb = c;
25758             if(a){
25759                 cb = function(){
25760                     this.hideAction();
25761                     if(c){
25762                         c();
25763                     }
25764                 }.createDelegate(this);
25765             }
25766             supr.setVisible.call(this, v, a, d, cb, e);
25767             if(v){
25768                 this.sync(true);
25769             }else if(!a){
25770                 this.hideAction();
25771             }
25772         }
25773     },
25774
25775     storeXY : function(xy){
25776         delete this.lastLT;
25777         this.lastXY = xy;
25778     },
25779
25780     storeLeftTop : function(left, top){
25781         delete this.lastXY;
25782         this.lastLT = [left, top];
25783     },
25784
25785     // private
25786     beforeFx : function(){
25787         this.beforeAction();
25788         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25789     },
25790
25791     // private
25792     afterFx : function(){
25793         Roo.Layer.superclass.afterFx.apply(this, arguments);
25794         this.sync(this.isVisible());
25795     },
25796
25797     // private
25798     beforeAction : function(){
25799         if(!this.updating && this.shadow){
25800             this.shadow.hide();
25801         }
25802     },
25803
25804     // overridden Element method
25805     setLeft : function(left){
25806         this.storeLeftTop(left, this.getTop(true));
25807         supr.setLeft.apply(this, arguments);
25808         this.sync();
25809     },
25810
25811     setTop : function(top){
25812         this.storeLeftTop(this.getLeft(true), top);
25813         supr.setTop.apply(this, arguments);
25814         this.sync();
25815     },
25816
25817     setLeftTop : function(left, top){
25818         this.storeLeftTop(left, top);
25819         supr.setLeftTop.apply(this, arguments);
25820         this.sync();
25821     },
25822
25823     setXY : function(xy, a, d, c, e){
25824         this.fixDisplay();
25825         this.beforeAction();
25826         this.storeXY(xy);
25827         var cb = this.createCB(c);
25828         supr.setXY.call(this, xy, a, d, cb, e);
25829         if(!a){
25830             cb();
25831         }
25832     },
25833
25834     // private
25835     createCB : function(c){
25836         var el = this;
25837         return function(){
25838             el.constrainXY();
25839             el.sync(true);
25840             if(c){
25841                 c();
25842             }
25843         };
25844     },
25845
25846     // overridden Element method
25847     setX : function(x, a, d, c, e){
25848         this.setXY([x, this.getY()], a, d, c, e);
25849     },
25850
25851     // overridden Element method
25852     setY : function(y, a, d, c, e){
25853         this.setXY([this.getX(), y], a, d, c, e);
25854     },
25855
25856     // overridden Element method
25857     setSize : function(w, h, a, d, c, e){
25858         this.beforeAction();
25859         var cb = this.createCB(c);
25860         supr.setSize.call(this, w, h, a, d, cb, e);
25861         if(!a){
25862             cb();
25863         }
25864     },
25865
25866     // overridden Element method
25867     setWidth : function(w, a, d, c, e){
25868         this.beforeAction();
25869         var cb = this.createCB(c);
25870         supr.setWidth.call(this, w, a, d, cb, e);
25871         if(!a){
25872             cb();
25873         }
25874     },
25875
25876     // overridden Element method
25877     setHeight : function(h, a, d, c, e){
25878         this.beforeAction();
25879         var cb = this.createCB(c);
25880         supr.setHeight.call(this, h, a, d, cb, e);
25881         if(!a){
25882             cb();
25883         }
25884     },
25885
25886     // overridden Element method
25887     setBounds : function(x, y, w, h, a, d, c, e){
25888         this.beforeAction();
25889         var cb = this.createCB(c);
25890         if(!a){
25891             this.storeXY([x, y]);
25892             supr.setXY.call(this, [x, y]);
25893             supr.setSize.call(this, w, h, a, d, cb, e);
25894             cb();
25895         }else{
25896             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25897         }
25898         return this;
25899     },
25900     
25901     /**
25902      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25903      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25904      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25905      * @param {Number} zindex The new z-index to set
25906      * @return {this} The Layer
25907      */
25908     setZIndex : function(zindex){
25909         this.zindex = zindex;
25910         this.setStyle("z-index", zindex + 2);
25911         if(this.shadow){
25912             this.shadow.setZIndex(zindex + 1);
25913         }
25914         if(this.shim){
25915             this.shim.setStyle("z-index", zindex);
25916         }
25917     }
25918 });
25919 })();/*
25920  * Based on:
25921  * Ext JS Library 1.1.1
25922  * Copyright(c) 2006-2007, Ext JS, LLC.
25923  *
25924  * Originally Released Under LGPL - original licence link has changed is not relivant.
25925  *
25926  * Fork - LGPL
25927  * <script type="text/javascript">
25928  */
25929
25930
25931 /**
25932  * @class Roo.Shadow
25933  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25934  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25935  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25936  * @constructor
25937  * Create a new Shadow
25938  * @param {Object} config The config object
25939  */
25940 Roo.Shadow = function(config){
25941     Roo.apply(this, config);
25942     if(typeof this.mode != "string"){
25943         this.mode = this.defaultMode;
25944     }
25945     var o = this.offset, a = {h: 0};
25946     var rad = Math.floor(this.offset/2);
25947     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25948         case "drop":
25949             a.w = 0;
25950             a.l = a.t = o;
25951             a.t -= 1;
25952             if(Roo.isIE){
25953                 a.l -= this.offset + rad;
25954                 a.t -= this.offset + rad;
25955                 a.w -= rad;
25956                 a.h -= rad;
25957                 a.t += 1;
25958             }
25959         break;
25960         case "sides":
25961             a.w = (o*2);
25962             a.l = -o;
25963             a.t = o-1;
25964             if(Roo.isIE){
25965                 a.l -= (this.offset - rad);
25966                 a.t -= this.offset + rad;
25967                 a.l += 1;
25968                 a.w -= (this.offset - rad)*2;
25969                 a.w -= rad + 1;
25970                 a.h -= 1;
25971             }
25972         break;
25973         case "frame":
25974             a.w = a.h = (o*2);
25975             a.l = a.t = -o;
25976             a.t += 1;
25977             a.h -= 2;
25978             if(Roo.isIE){
25979                 a.l -= (this.offset - rad);
25980                 a.t -= (this.offset - rad);
25981                 a.l += 1;
25982                 a.w -= (this.offset + rad + 1);
25983                 a.h -= (this.offset + rad);
25984                 a.h += 1;
25985             }
25986         break;
25987     };
25988
25989     this.adjusts = a;
25990 };
25991
25992 Roo.Shadow.prototype = {
25993     /**
25994      * @cfg {String} mode
25995      * The shadow display mode.  Supports the following options:<br />
25996      * sides: Shadow displays on both sides and bottom only<br />
25997      * frame: Shadow displays equally on all four sides<br />
25998      * drop: Traditional bottom-right drop shadow (default)
25999      */
26000     /**
26001      * @cfg {String} offset
26002      * The number of pixels to offset the shadow from the element (defaults to 4)
26003      */
26004     offset: 4,
26005
26006     // private
26007     defaultMode: "drop",
26008
26009     /**
26010      * Displays the shadow under the target element
26011      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26012      */
26013     show : function(target){
26014         target = Roo.get(target);
26015         if(!this.el){
26016             this.el = Roo.Shadow.Pool.pull();
26017             if(this.el.dom.nextSibling != target.dom){
26018                 this.el.insertBefore(target);
26019             }
26020         }
26021         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26022         if(Roo.isIE){
26023             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26024         }
26025         this.realign(
26026             target.getLeft(true),
26027             target.getTop(true),
26028             target.getWidth(),
26029             target.getHeight()
26030         );
26031         this.el.dom.style.display = "block";
26032     },
26033
26034     /**
26035      * Returns true if the shadow is visible, else false
26036      */
26037     isVisible : function(){
26038         return this.el ? true : false;  
26039     },
26040
26041     /**
26042      * Direct alignment when values are already available. Show must be called at least once before
26043      * calling this method to ensure it is initialized.
26044      * @param {Number} left The target element left position
26045      * @param {Number} top The target element top position
26046      * @param {Number} width The target element width
26047      * @param {Number} height The target element height
26048      */
26049     realign : function(l, t, w, h){
26050         if(!this.el){
26051             return;
26052         }
26053         var a = this.adjusts, d = this.el.dom, s = d.style;
26054         var iea = 0;
26055         s.left = (l+a.l)+"px";
26056         s.top = (t+a.t)+"px";
26057         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26058  
26059         if(s.width != sws || s.height != shs){
26060             s.width = sws;
26061             s.height = shs;
26062             if(!Roo.isIE){
26063                 var cn = d.childNodes;
26064                 var sww = Math.max(0, (sw-12))+"px";
26065                 cn[0].childNodes[1].style.width = sww;
26066                 cn[1].childNodes[1].style.width = sww;
26067                 cn[2].childNodes[1].style.width = sww;
26068                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26069             }
26070         }
26071     },
26072
26073     /**
26074      * Hides this shadow
26075      */
26076     hide : function(){
26077         if(this.el){
26078             this.el.dom.style.display = "none";
26079             Roo.Shadow.Pool.push(this.el);
26080             delete this.el;
26081         }
26082     },
26083
26084     /**
26085      * Adjust the z-index of this shadow
26086      * @param {Number} zindex The new z-index
26087      */
26088     setZIndex : function(z){
26089         this.zIndex = z;
26090         if(this.el){
26091             this.el.setStyle("z-index", z);
26092         }
26093     }
26094 };
26095
26096 // Private utility class that manages the internal Shadow cache
26097 Roo.Shadow.Pool = function(){
26098     var p = [];
26099     var markup = Roo.isIE ?
26100                  '<div class="x-ie-shadow"></div>' :
26101                  '<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>';
26102     return {
26103         pull : function(){
26104             var sh = p.shift();
26105             if(!sh){
26106                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26107                 sh.autoBoxAdjust = false;
26108             }
26109             return sh;
26110         },
26111
26112         push : function(sh){
26113             p.push(sh);
26114         }
26115     };
26116 }();/*
26117  * Based on:
26118  * Ext JS Library 1.1.1
26119  * Copyright(c) 2006-2007, Ext JS, LLC.
26120  *
26121  * Originally Released Under LGPL - original licence link has changed is not relivant.
26122  *
26123  * Fork - LGPL
26124  * <script type="text/javascript">
26125  */
26126
26127
26128 /**
26129  * @class Roo.SplitBar
26130  * @extends Roo.util.Observable
26131  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26132  * <br><br>
26133  * Usage:
26134  * <pre><code>
26135 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26136                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26137 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26138 split.minSize = 100;
26139 split.maxSize = 600;
26140 split.animate = true;
26141 split.on('moved', splitterMoved);
26142 </code></pre>
26143  * @constructor
26144  * Create a new SplitBar
26145  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26146  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26147  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26148  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26149                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26150                         position of the SplitBar).
26151  */
26152 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26153     
26154     /** @private */
26155     this.el = Roo.get(dragElement, true);
26156     this.el.dom.unselectable = "on";
26157     /** @private */
26158     this.resizingEl = Roo.get(resizingElement, true);
26159
26160     /**
26161      * @private
26162      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26163      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26164      * @type Number
26165      */
26166     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26167     
26168     /**
26169      * The minimum size of the resizing element. (Defaults to 0)
26170      * @type Number
26171      */
26172     this.minSize = 0;
26173     
26174     /**
26175      * The maximum size of the resizing element. (Defaults to 2000)
26176      * @type Number
26177      */
26178     this.maxSize = 2000;
26179     
26180     /**
26181      * Whether to animate the transition to the new size
26182      * @type Boolean
26183      */
26184     this.animate = false;
26185     
26186     /**
26187      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26188      * @type Boolean
26189      */
26190     this.useShim = false;
26191     
26192     /** @private */
26193     this.shim = null;
26194     
26195     if(!existingProxy){
26196         /** @private */
26197         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26198     }else{
26199         this.proxy = Roo.get(existingProxy).dom;
26200     }
26201     /** @private */
26202     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26203     
26204     /** @private */
26205     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26206     
26207     /** @private */
26208     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26209     
26210     /** @private */
26211     this.dragSpecs = {};
26212     
26213     /**
26214      * @private The adapter to use to positon and resize elements
26215      */
26216     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26217     this.adapter.init(this);
26218     
26219     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26220         /** @private */
26221         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26222         this.el.addClass("x-splitbar-h");
26223     }else{
26224         /** @private */
26225         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26226         this.el.addClass("x-splitbar-v");
26227     }
26228     
26229     this.addEvents({
26230         /**
26231          * @event resize
26232          * Fires when the splitter is moved (alias for {@link #event-moved})
26233          * @param {Roo.SplitBar} this
26234          * @param {Number} newSize the new width or height
26235          */
26236         "resize" : true,
26237         /**
26238          * @event moved
26239          * Fires when the splitter is moved
26240          * @param {Roo.SplitBar} this
26241          * @param {Number} newSize the new width or height
26242          */
26243         "moved" : true,
26244         /**
26245          * @event beforeresize
26246          * Fires before the splitter is dragged
26247          * @param {Roo.SplitBar} this
26248          */
26249         "beforeresize" : true,
26250
26251         "beforeapply" : true
26252     });
26253
26254     Roo.util.Observable.call(this);
26255 };
26256
26257 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26258     onStartProxyDrag : function(x, y){
26259         this.fireEvent("beforeresize", this);
26260         if(!this.overlay){
26261             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26262             o.unselectable();
26263             o.enableDisplayMode("block");
26264             // all splitbars share the same overlay
26265             Roo.SplitBar.prototype.overlay = o;
26266         }
26267         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26268         this.overlay.show();
26269         Roo.get(this.proxy).setDisplayed("block");
26270         var size = this.adapter.getElementSize(this);
26271         this.activeMinSize = this.getMinimumSize();;
26272         this.activeMaxSize = this.getMaximumSize();;
26273         var c1 = size - this.activeMinSize;
26274         var c2 = Math.max(this.activeMaxSize - size, 0);
26275         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26276             this.dd.resetConstraints();
26277             this.dd.setXConstraint(
26278                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26279                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26280             );
26281             this.dd.setYConstraint(0, 0);
26282         }else{
26283             this.dd.resetConstraints();
26284             this.dd.setXConstraint(0, 0);
26285             this.dd.setYConstraint(
26286                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26287                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26288             );
26289          }
26290         this.dragSpecs.startSize = size;
26291         this.dragSpecs.startPoint = [x, y];
26292         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26293     },
26294     
26295     /** 
26296      * @private Called after the drag operation by the DDProxy
26297      */
26298     onEndProxyDrag : function(e){
26299         Roo.get(this.proxy).setDisplayed(false);
26300         var endPoint = Roo.lib.Event.getXY(e);
26301         if(this.overlay){
26302             this.overlay.hide();
26303         }
26304         var newSize;
26305         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26306             newSize = this.dragSpecs.startSize + 
26307                 (this.placement == Roo.SplitBar.LEFT ?
26308                     endPoint[0] - this.dragSpecs.startPoint[0] :
26309                     this.dragSpecs.startPoint[0] - endPoint[0]
26310                 );
26311         }else{
26312             newSize = this.dragSpecs.startSize + 
26313                 (this.placement == Roo.SplitBar.TOP ?
26314                     endPoint[1] - this.dragSpecs.startPoint[1] :
26315                     this.dragSpecs.startPoint[1] - endPoint[1]
26316                 );
26317         }
26318         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26319         if(newSize != this.dragSpecs.startSize){
26320             if(this.fireEvent('beforeapply', this, newSize) !== false){
26321                 this.adapter.setElementSize(this, newSize);
26322                 this.fireEvent("moved", this, newSize);
26323                 this.fireEvent("resize", this, newSize);
26324             }
26325         }
26326     },
26327     
26328     /**
26329      * Get the adapter this SplitBar uses
26330      * @return The adapter object
26331      */
26332     getAdapter : function(){
26333         return this.adapter;
26334     },
26335     
26336     /**
26337      * Set the adapter this SplitBar uses
26338      * @param {Object} adapter A SplitBar adapter object
26339      */
26340     setAdapter : function(adapter){
26341         this.adapter = adapter;
26342         this.adapter.init(this);
26343     },
26344     
26345     /**
26346      * Gets the minimum size for the resizing element
26347      * @return {Number} The minimum size
26348      */
26349     getMinimumSize : function(){
26350         return this.minSize;
26351     },
26352     
26353     /**
26354      * Sets the minimum size for the resizing element
26355      * @param {Number} minSize The minimum size
26356      */
26357     setMinimumSize : function(minSize){
26358         this.minSize = minSize;
26359     },
26360     
26361     /**
26362      * Gets the maximum size for the resizing element
26363      * @return {Number} The maximum size
26364      */
26365     getMaximumSize : function(){
26366         return this.maxSize;
26367     },
26368     
26369     /**
26370      * Sets the maximum size for the resizing element
26371      * @param {Number} maxSize The maximum size
26372      */
26373     setMaximumSize : function(maxSize){
26374         this.maxSize = maxSize;
26375     },
26376     
26377     /**
26378      * Sets the initialize size for the resizing element
26379      * @param {Number} size The initial size
26380      */
26381     setCurrentSize : function(size){
26382         var oldAnimate = this.animate;
26383         this.animate = false;
26384         this.adapter.setElementSize(this, size);
26385         this.animate = oldAnimate;
26386     },
26387     
26388     /**
26389      * Destroy this splitbar. 
26390      * @param {Boolean} removeEl True to remove the element
26391      */
26392     destroy : function(removeEl){
26393         if(this.shim){
26394             this.shim.remove();
26395         }
26396         this.dd.unreg();
26397         this.proxy.parentNode.removeChild(this.proxy);
26398         if(removeEl){
26399             this.el.remove();
26400         }
26401     }
26402 });
26403
26404 /**
26405  * @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.
26406  */
26407 Roo.SplitBar.createProxy = function(dir){
26408     var proxy = new Roo.Element(document.createElement("div"));
26409     proxy.unselectable();
26410     var cls = 'x-splitbar-proxy';
26411     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26412     document.body.appendChild(proxy.dom);
26413     return proxy.dom;
26414 };
26415
26416 /** 
26417  * @class Roo.SplitBar.BasicLayoutAdapter
26418  * Default Adapter. It assumes the splitter and resizing element are not positioned
26419  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26420  */
26421 Roo.SplitBar.BasicLayoutAdapter = function(){
26422 };
26423
26424 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26425     // do nothing for now
26426     init : function(s){
26427     
26428     },
26429     /**
26430      * Called before drag operations to get the current size of the resizing element. 
26431      * @param {Roo.SplitBar} s The SplitBar using this adapter
26432      */
26433      getElementSize : function(s){
26434         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26435             return s.resizingEl.getWidth();
26436         }else{
26437             return s.resizingEl.getHeight();
26438         }
26439     },
26440     
26441     /**
26442      * Called after drag operations to set the size of the resizing element.
26443      * @param {Roo.SplitBar} s The SplitBar using this adapter
26444      * @param {Number} newSize The new size to set
26445      * @param {Function} onComplete A function to be invoked when resizing is complete
26446      */
26447     setElementSize : function(s, newSize, onComplete){
26448         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26449             if(!s.animate){
26450                 s.resizingEl.setWidth(newSize);
26451                 if(onComplete){
26452                     onComplete(s, newSize);
26453                 }
26454             }else{
26455                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26456             }
26457         }else{
26458             
26459             if(!s.animate){
26460                 s.resizingEl.setHeight(newSize);
26461                 if(onComplete){
26462                     onComplete(s, newSize);
26463                 }
26464             }else{
26465                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26466             }
26467         }
26468     }
26469 };
26470
26471 /** 
26472  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26473  * @extends Roo.SplitBar.BasicLayoutAdapter
26474  * Adapter that  moves the splitter element to align with the resized sizing element. 
26475  * Used with an absolute positioned SplitBar.
26476  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26477  * document.body, make sure you assign an id to the body element.
26478  */
26479 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26480     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26481     this.container = Roo.get(container);
26482 };
26483
26484 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26485     init : function(s){
26486         this.basic.init(s);
26487     },
26488     
26489     getElementSize : function(s){
26490         return this.basic.getElementSize(s);
26491     },
26492     
26493     setElementSize : function(s, newSize, onComplete){
26494         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26495     },
26496     
26497     moveSplitter : function(s){
26498         var yes = Roo.SplitBar;
26499         switch(s.placement){
26500             case yes.LEFT:
26501                 s.el.setX(s.resizingEl.getRight());
26502                 break;
26503             case yes.RIGHT:
26504                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26505                 break;
26506             case yes.TOP:
26507                 s.el.setY(s.resizingEl.getBottom());
26508                 break;
26509             case yes.BOTTOM:
26510                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26511                 break;
26512         }
26513     }
26514 };
26515
26516 /**
26517  * Orientation constant - Create a vertical SplitBar
26518  * @static
26519  * @type Number
26520  */
26521 Roo.SplitBar.VERTICAL = 1;
26522
26523 /**
26524  * Orientation constant - Create a horizontal SplitBar
26525  * @static
26526  * @type Number
26527  */
26528 Roo.SplitBar.HORIZONTAL = 2;
26529
26530 /**
26531  * Placement constant - The resizing element is to the left of the splitter element
26532  * @static
26533  * @type Number
26534  */
26535 Roo.SplitBar.LEFT = 1;
26536
26537 /**
26538  * Placement constant - The resizing element is to the right of the splitter element
26539  * @static
26540  * @type Number
26541  */
26542 Roo.SplitBar.RIGHT = 2;
26543
26544 /**
26545  * Placement constant - The resizing element is positioned above the splitter element
26546  * @static
26547  * @type Number
26548  */
26549 Roo.SplitBar.TOP = 3;
26550
26551 /**
26552  * Placement constant - The resizing element is positioned under splitter element
26553  * @static
26554  * @type Number
26555  */
26556 Roo.SplitBar.BOTTOM = 4;
26557 /*
26558  * Based on:
26559  * Ext JS Library 1.1.1
26560  * Copyright(c) 2006-2007, Ext JS, LLC.
26561  *
26562  * Originally Released Under LGPL - original licence link has changed is not relivant.
26563  *
26564  * Fork - LGPL
26565  * <script type="text/javascript">
26566  */
26567
26568 /**
26569  * @class Roo.View
26570  * @extends Roo.util.Observable
26571  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26572  * This class also supports single and multi selection modes. <br>
26573  * Create a data model bound view:
26574  <pre><code>
26575  var store = new Roo.data.Store(...);
26576
26577  var view = new Roo.View({
26578     el : "my-element",
26579     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26580  
26581     singleSelect: true,
26582     selectedClass: "ydataview-selected",
26583     store: store
26584  });
26585
26586  // listen for node click?
26587  view.on("click", function(vw, index, node, e){
26588  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26589  });
26590
26591  // load XML data
26592  dataModel.load("foobar.xml");
26593  </code></pre>
26594  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26595  * <br><br>
26596  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26597  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26598  * 
26599  * Note: old style constructor is still suported (container, template, config)
26600  * 
26601  * @constructor
26602  * Create a new View
26603  * @param {Object} config The config object
26604  * 
26605  */
26606 Roo.View = function(config, depreciated_tpl, depreciated_config){
26607     
26608     this.parent = false;
26609     
26610     if (typeof(depreciated_tpl) == 'undefined') {
26611         // new way.. - universal constructor.
26612         Roo.apply(this, config);
26613         this.el  = Roo.get(this.el);
26614     } else {
26615         // old format..
26616         this.el  = Roo.get(config);
26617         this.tpl = depreciated_tpl;
26618         Roo.apply(this, depreciated_config);
26619     }
26620     this.wrapEl  = this.el.wrap().wrap();
26621     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26622     
26623     
26624     if(typeof(this.tpl) == "string"){
26625         this.tpl = new Roo.Template(this.tpl);
26626     } else {
26627         // support xtype ctors..
26628         this.tpl = new Roo.factory(this.tpl, Roo);
26629     }
26630     
26631     
26632     this.tpl.compile();
26633     
26634     /** @private */
26635     this.addEvents({
26636         /**
26637          * @event beforeclick
26638          * Fires before a click is processed. Returns false to cancel the default action.
26639          * @param {Roo.View} this
26640          * @param {Number} index The index of the target node
26641          * @param {HTMLElement} node The target node
26642          * @param {Roo.EventObject} e The raw event object
26643          */
26644             "beforeclick" : true,
26645         /**
26646          * @event click
26647          * Fires when a template node is clicked.
26648          * @param {Roo.View} this
26649          * @param {Number} index The index of the target node
26650          * @param {HTMLElement} node The target node
26651          * @param {Roo.EventObject} e The raw event object
26652          */
26653             "click" : true,
26654         /**
26655          * @event dblclick
26656          * Fires when a template node is double clicked.
26657          * @param {Roo.View} this
26658          * @param {Number} index The index of the target node
26659          * @param {HTMLElement} node The target node
26660          * @param {Roo.EventObject} e The raw event object
26661          */
26662             "dblclick" : true,
26663         /**
26664          * @event contextmenu
26665          * Fires when a template node is right clicked.
26666          * @param {Roo.View} this
26667          * @param {Number} index The index of the target node
26668          * @param {HTMLElement} node The target node
26669          * @param {Roo.EventObject} e The raw event object
26670          */
26671             "contextmenu" : true,
26672         /**
26673          * @event selectionchange
26674          * Fires when the selected nodes change.
26675          * @param {Roo.View} this
26676          * @param {Array} selections Array of the selected nodes
26677          */
26678             "selectionchange" : true,
26679     
26680         /**
26681          * @event beforeselect
26682          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26683          * @param {Roo.View} this
26684          * @param {HTMLElement} node The node to be selected
26685          * @param {Array} selections Array of currently selected nodes
26686          */
26687             "beforeselect" : true,
26688         /**
26689          * @event preparedata
26690          * Fires on every row to render, to allow you to change the data.
26691          * @param {Roo.View} this
26692          * @param {Object} data to be rendered (change this)
26693          */
26694           "preparedata" : true
26695           
26696           
26697         });
26698
26699
26700
26701     this.el.on({
26702         "click": this.onClick,
26703         "dblclick": this.onDblClick,
26704         "contextmenu": this.onContextMenu,
26705         scope:this
26706     });
26707
26708     this.selections = [];
26709     this.nodes = [];
26710     this.cmp = new Roo.CompositeElementLite([]);
26711     if(this.store){
26712         this.store = Roo.factory(this.store, Roo.data);
26713         this.setStore(this.store, true);
26714     }
26715     
26716     if ( this.footer && this.footer.xtype) {
26717            
26718          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26719         
26720         this.footer.dataSource = this.store;
26721         this.footer.container = fctr;
26722         this.footer = Roo.factory(this.footer, Roo);
26723         fctr.insertFirst(this.el);
26724         
26725         // this is a bit insane - as the paging toolbar seems to detach the el..
26726 //        dom.parentNode.parentNode.parentNode
26727          // they get detached?
26728     }
26729     
26730     
26731     Roo.View.superclass.constructor.call(this);
26732     
26733     
26734 };
26735
26736 Roo.extend(Roo.View, Roo.util.Observable, {
26737     
26738      /**
26739      * @cfg {Roo.data.Store} store Data store to load data from.
26740      */
26741     store : false,
26742     
26743     /**
26744      * @cfg {String|Roo.Element} el The container element.
26745      */
26746     el : '',
26747     
26748     /**
26749      * @cfg {String|Roo.Template} tpl The template used by this View 
26750      */
26751     tpl : false,
26752     /**
26753      * @cfg {String} dataName the named area of the template to use as the data area
26754      *                          Works with domtemplates roo-name="name"
26755      */
26756     dataName: false,
26757     /**
26758      * @cfg {String} selectedClass The css class to add to selected nodes
26759      */
26760     selectedClass : "x-view-selected",
26761      /**
26762      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26763      */
26764     emptyText : "",
26765     
26766     /**
26767      * @cfg {String} text to display on mask (default Loading)
26768      */
26769     mask : false,
26770     /**
26771      * @cfg {Boolean} multiSelect Allow multiple selection
26772      */
26773     multiSelect : false,
26774     /**
26775      * @cfg {Boolean} singleSelect Allow single selection
26776      */
26777     singleSelect:  false,
26778     
26779     /**
26780      * @cfg {Boolean} toggleSelect - selecting 
26781      */
26782     toggleSelect : false,
26783     
26784     /**
26785      * @cfg {Boolean} tickable - selecting 
26786      */
26787     tickable : false,
26788     
26789     /**
26790      * Returns the element this view is bound to.
26791      * @return {Roo.Element}
26792      */
26793     getEl : function(){
26794         return this.wrapEl;
26795     },
26796     
26797     
26798
26799     /**
26800      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26801      */
26802     refresh : function(){
26803         //Roo.log('refresh');
26804         var t = this.tpl;
26805         
26806         // if we are using something like 'domtemplate', then
26807         // the what gets used is:
26808         // t.applySubtemplate(NAME, data, wrapping data..)
26809         // the outer template then get' applied with
26810         //     the store 'extra data'
26811         // and the body get's added to the
26812         //      roo-name="data" node?
26813         //      <span class='roo-tpl-{name}'></span> ?????
26814         
26815         
26816         
26817         this.clearSelections();
26818         this.el.update("");
26819         var html = [];
26820         var records = this.store.getRange();
26821         if(records.length < 1) {
26822             
26823             // is this valid??  = should it render a template??
26824             
26825             this.el.update(this.emptyText);
26826             return;
26827         }
26828         var el = this.el;
26829         if (this.dataName) {
26830             this.el.update(t.apply(this.store.meta)); //????
26831             el = this.el.child('.roo-tpl-' + this.dataName);
26832         }
26833         
26834         for(var i = 0, len = records.length; i < len; i++){
26835             var data = this.prepareData(records[i].data, i, records[i]);
26836             this.fireEvent("preparedata", this, data, i, records[i]);
26837             
26838             var d = Roo.apply({}, data);
26839             
26840             if(this.tickable){
26841                 Roo.apply(d, {'roo-id' : Roo.id()});
26842                 
26843                 var _this = this;
26844             
26845                 Roo.each(this.parent.item, function(item){
26846                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26847                         return;
26848                     }
26849                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26850                 });
26851             }
26852             
26853             html[html.length] = Roo.util.Format.trim(
26854                 this.dataName ?
26855                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26856                     t.apply(d)
26857             );
26858         }
26859         
26860         
26861         
26862         el.update(html.join(""));
26863         this.nodes = el.dom.childNodes;
26864         this.updateIndexes(0);
26865     },
26866     
26867
26868     /**
26869      * Function to override to reformat the data that is sent to
26870      * the template for each node.
26871      * DEPRICATED - use the preparedata event handler.
26872      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26873      * a JSON object for an UpdateManager bound view).
26874      */
26875     prepareData : function(data, index, record)
26876     {
26877         this.fireEvent("preparedata", this, data, index, record);
26878         return data;
26879     },
26880
26881     onUpdate : function(ds, record){
26882         // Roo.log('on update');   
26883         this.clearSelections();
26884         var index = this.store.indexOf(record);
26885         var n = this.nodes[index];
26886         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26887         n.parentNode.removeChild(n);
26888         this.updateIndexes(index, index);
26889     },
26890
26891     
26892     
26893 // --------- FIXME     
26894     onAdd : function(ds, records, index)
26895     {
26896         //Roo.log(['on Add', ds, records, index] );        
26897         this.clearSelections();
26898         if(this.nodes.length == 0){
26899             this.refresh();
26900             return;
26901         }
26902         var n = this.nodes[index];
26903         for(var i = 0, len = records.length; i < len; i++){
26904             var d = this.prepareData(records[i].data, i, records[i]);
26905             if(n){
26906                 this.tpl.insertBefore(n, d);
26907             }else{
26908                 
26909                 this.tpl.append(this.el, d);
26910             }
26911         }
26912         this.updateIndexes(index);
26913     },
26914
26915     onRemove : function(ds, record, index){
26916        // Roo.log('onRemove');
26917         this.clearSelections();
26918         var el = this.dataName  ?
26919             this.el.child('.roo-tpl-' + this.dataName) :
26920             this.el; 
26921         
26922         el.dom.removeChild(this.nodes[index]);
26923         this.updateIndexes(index);
26924     },
26925
26926     /**
26927      * Refresh an individual node.
26928      * @param {Number} index
26929      */
26930     refreshNode : function(index){
26931         this.onUpdate(this.store, this.store.getAt(index));
26932     },
26933
26934     updateIndexes : function(startIndex, endIndex){
26935         var ns = this.nodes;
26936         startIndex = startIndex || 0;
26937         endIndex = endIndex || ns.length - 1;
26938         for(var i = startIndex; i <= endIndex; i++){
26939             ns[i].nodeIndex = i;
26940         }
26941     },
26942
26943     /**
26944      * Changes the data store this view uses and refresh the view.
26945      * @param {Store} store
26946      */
26947     setStore : function(store, initial){
26948         if(!initial && this.store){
26949             this.store.un("datachanged", this.refresh);
26950             this.store.un("add", this.onAdd);
26951             this.store.un("remove", this.onRemove);
26952             this.store.un("update", this.onUpdate);
26953             this.store.un("clear", this.refresh);
26954             this.store.un("beforeload", this.onBeforeLoad);
26955             this.store.un("load", this.onLoad);
26956             this.store.un("loadexception", this.onLoad);
26957         }
26958         if(store){
26959           
26960             store.on("datachanged", this.refresh, this);
26961             store.on("add", this.onAdd, this);
26962             store.on("remove", this.onRemove, this);
26963             store.on("update", this.onUpdate, this);
26964             store.on("clear", this.refresh, this);
26965             store.on("beforeload", this.onBeforeLoad, this);
26966             store.on("load", this.onLoad, this);
26967             store.on("loadexception", this.onLoad, this);
26968         }
26969         
26970         if(store){
26971             this.refresh();
26972         }
26973     },
26974     /**
26975      * onbeforeLoad - masks the loading area.
26976      *
26977      */
26978     onBeforeLoad : function(store,opts)
26979     {
26980          //Roo.log('onBeforeLoad');   
26981         if (!opts.add) {
26982             this.el.update("");
26983         }
26984         this.el.mask(this.mask ? this.mask : "Loading" ); 
26985     },
26986     onLoad : function ()
26987     {
26988         this.el.unmask();
26989     },
26990     
26991
26992     /**
26993      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26994      * @param {HTMLElement} node
26995      * @return {HTMLElement} The template node
26996      */
26997     findItemFromChild : function(node){
26998         var el = this.dataName  ?
26999             this.el.child('.roo-tpl-' + this.dataName,true) :
27000             this.el.dom; 
27001         
27002         if(!node || node.parentNode == el){
27003                     return node;
27004             }
27005             var p = node.parentNode;
27006             while(p && p != el){
27007             if(p.parentNode == el){
27008                 return p;
27009             }
27010             p = p.parentNode;
27011         }
27012             return null;
27013     },
27014
27015     /** @ignore */
27016     onClick : function(e){
27017         var item = this.findItemFromChild(e.getTarget());
27018         if(item){
27019             var index = this.indexOf(item);
27020             if(this.onItemClick(item, index, e) !== false){
27021                 this.fireEvent("click", this, index, item, e);
27022             }
27023         }else{
27024             this.clearSelections();
27025         }
27026     },
27027
27028     /** @ignore */
27029     onContextMenu : function(e){
27030         var item = this.findItemFromChild(e.getTarget());
27031         if(item){
27032             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27033         }
27034     },
27035
27036     /** @ignore */
27037     onDblClick : function(e){
27038         var item = this.findItemFromChild(e.getTarget());
27039         if(item){
27040             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27041         }
27042     },
27043
27044     onItemClick : function(item, index, e)
27045     {
27046         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27047             return false;
27048         }
27049         if (this.toggleSelect) {
27050             var m = this.isSelected(item) ? 'unselect' : 'select';
27051             //Roo.log(m);
27052             var _t = this;
27053             _t[m](item, true, false);
27054             return true;
27055         }
27056         if(this.multiSelect || this.singleSelect){
27057             if(this.multiSelect && e.shiftKey && this.lastSelection){
27058                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27059             }else{
27060                 this.select(item, this.multiSelect && e.ctrlKey);
27061                 this.lastSelection = item;
27062             }
27063             
27064             if(!this.tickable){
27065                 e.preventDefault();
27066             }
27067             
27068         }
27069         return true;
27070     },
27071
27072     /**
27073      * Get the number of selected nodes.
27074      * @return {Number}
27075      */
27076     getSelectionCount : function(){
27077         return this.selections.length;
27078     },
27079
27080     /**
27081      * Get the currently selected nodes.
27082      * @return {Array} An array of HTMLElements
27083      */
27084     getSelectedNodes : function(){
27085         return this.selections;
27086     },
27087
27088     /**
27089      * Get the indexes of the selected nodes.
27090      * @return {Array}
27091      */
27092     getSelectedIndexes : function(){
27093         var indexes = [], s = this.selections;
27094         for(var i = 0, len = s.length; i < len; i++){
27095             indexes.push(s[i].nodeIndex);
27096         }
27097         return indexes;
27098     },
27099
27100     /**
27101      * Clear all selections
27102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27103      */
27104     clearSelections : function(suppressEvent){
27105         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27106             this.cmp.elements = this.selections;
27107             this.cmp.removeClass(this.selectedClass);
27108             this.selections = [];
27109             if(!suppressEvent){
27110                 this.fireEvent("selectionchange", this, this.selections);
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Returns true if the passed node is selected
27117      * @param {HTMLElement/Number} node The node or node index
27118      * @return {Boolean}
27119      */
27120     isSelected : function(node){
27121         var s = this.selections;
27122         if(s.length < 1){
27123             return false;
27124         }
27125         node = this.getNode(node);
27126         return s.indexOf(node) !== -1;
27127     },
27128
27129     /**
27130      * Selects nodes.
27131      * @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
27132      * @param {Boolean} keepExisting (optional) true to keep existing selections
27133      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27134      */
27135     select : function(nodeInfo, keepExisting, suppressEvent){
27136         if(nodeInfo instanceof Array){
27137             if(!keepExisting){
27138                 this.clearSelections(true);
27139             }
27140             for(var i = 0, len = nodeInfo.length; i < len; i++){
27141                 this.select(nodeInfo[i], true, true);
27142             }
27143             return;
27144         } 
27145         var node = this.getNode(nodeInfo);
27146         if(!node || this.isSelected(node)){
27147             return; // already selected.
27148         }
27149         if(!keepExisting){
27150             this.clearSelections(true);
27151         }
27152         
27153         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27154             Roo.fly(node).addClass(this.selectedClass);
27155             this.selections.push(node);
27156             if(!suppressEvent){
27157                 this.fireEvent("selectionchange", this, this.selections);
27158             }
27159         }
27160         
27161         
27162     },
27163       /**
27164      * Unselects nodes.
27165      * @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
27166      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27167      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27168      */
27169     unselect : function(nodeInfo, keepExisting, suppressEvent)
27170     {
27171         if(nodeInfo instanceof Array){
27172             Roo.each(this.selections, function(s) {
27173                 this.unselect(s, nodeInfo);
27174             }, this);
27175             return;
27176         }
27177         var node = this.getNode(nodeInfo);
27178         if(!node || !this.isSelected(node)){
27179             //Roo.log("not selected");
27180             return; // not selected.
27181         }
27182         // fireevent???
27183         var ns = [];
27184         Roo.each(this.selections, function(s) {
27185             if (s == node ) {
27186                 Roo.fly(node).removeClass(this.selectedClass);
27187
27188                 return;
27189             }
27190             ns.push(s);
27191         },this);
27192         
27193         this.selections= ns;
27194         this.fireEvent("selectionchange", this, this.selections);
27195     },
27196
27197     /**
27198      * Gets a template node.
27199      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27200      * @return {HTMLElement} The node or null if it wasn't found
27201      */
27202     getNode : function(nodeInfo){
27203         if(typeof nodeInfo == "string"){
27204             return document.getElementById(nodeInfo);
27205         }else if(typeof nodeInfo == "number"){
27206             return this.nodes[nodeInfo];
27207         }
27208         return nodeInfo;
27209     },
27210
27211     /**
27212      * Gets a range template nodes.
27213      * @param {Number} startIndex
27214      * @param {Number} endIndex
27215      * @return {Array} An array of nodes
27216      */
27217     getNodes : function(start, end){
27218         var ns = this.nodes;
27219         start = start || 0;
27220         end = typeof end == "undefined" ? ns.length - 1 : end;
27221         var nodes = [];
27222         if(start <= end){
27223             for(var i = start; i <= end; i++){
27224                 nodes.push(ns[i]);
27225             }
27226         } else{
27227             for(var i = start; i >= end; i--){
27228                 nodes.push(ns[i]);
27229             }
27230         }
27231         return nodes;
27232     },
27233
27234     /**
27235      * Finds the index of the passed node
27236      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27237      * @return {Number} The index of the node or -1
27238      */
27239     indexOf : function(node){
27240         node = this.getNode(node);
27241         if(typeof node.nodeIndex == "number"){
27242             return node.nodeIndex;
27243         }
27244         var ns = this.nodes;
27245         for(var i = 0, len = ns.length; i < len; i++){
27246             if(ns[i] == node){
27247                 return i;
27248             }
27249         }
27250         return -1;
27251     }
27252 });
27253 /*
27254  * Based on:
27255  * Ext JS Library 1.1.1
27256  * Copyright(c) 2006-2007, Ext JS, LLC.
27257  *
27258  * Originally Released Under LGPL - original licence link has changed is not relivant.
27259  *
27260  * Fork - LGPL
27261  * <script type="text/javascript">
27262  */
27263
27264 /**
27265  * @class Roo.JsonView
27266  * @extends Roo.View
27267  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27268 <pre><code>
27269 var view = new Roo.JsonView({
27270     container: "my-element",
27271     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27272     multiSelect: true, 
27273     jsonRoot: "data" 
27274 });
27275
27276 // listen for node click?
27277 view.on("click", function(vw, index, node, e){
27278     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27279 });
27280
27281 // direct load of JSON data
27282 view.load("foobar.php");
27283
27284 // Example from my blog list
27285 var tpl = new Roo.Template(
27286     '&lt;div class="entry"&gt;' +
27287     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27288     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27289     "&lt;/div&gt;&lt;hr /&gt;"
27290 );
27291
27292 var moreView = new Roo.JsonView({
27293     container :  "entry-list", 
27294     template : tpl,
27295     jsonRoot: "posts"
27296 });
27297 moreView.on("beforerender", this.sortEntries, this);
27298 moreView.load({
27299     url: "/blog/get-posts.php",
27300     params: "allposts=true",
27301     text: "Loading Blog Entries..."
27302 });
27303 </code></pre>
27304
27305 * Note: old code is supported with arguments : (container, template, config)
27306
27307
27308  * @constructor
27309  * Create a new JsonView
27310  * 
27311  * @param {Object} config The config object
27312  * 
27313  */
27314 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27315     
27316     
27317     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27318
27319     var um = this.el.getUpdateManager();
27320     um.setRenderer(this);
27321     um.on("update", this.onLoad, this);
27322     um.on("failure", this.onLoadException, this);
27323
27324     /**
27325      * @event beforerender
27326      * Fires before rendering of the downloaded JSON data.
27327      * @param {Roo.JsonView} this
27328      * @param {Object} data The JSON data loaded
27329      */
27330     /**
27331      * @event load
27332      * Fires when data is loaded.
27333      * @param {Roo.JsonView} this
27334      * @param {Object} data The JSON data loaded
27335      * @param {Object} response The raw Connect response object
27336      */
27337     /**
27338      * @event loadexception
27339      * Fires when loading fails.
27340      * @param {Roo.JsonView} this
27341      * @param {Object} response The raw Connect response object
27342      */
27343     this.addEvents({
27344         'beforerender' : true,
27345         'load' : true,
27346         'loadexception' : true
27347     });
27348 };
27349 Roo.extend(Roo.JsonView, Roo.View, {
27350     /**
27351      * @type {String} The root property in the loaded JSON object that contains the data
27352      */
27353     jsonRoot : "",
27354
27355     /**
27356      * Refreshes the view.
27357      */
27358     refresh : function(){
27359         this.clearSelections();
27360         this.el.update("");
27361         var html = [];
27362         var o = this.jsonData;
27363         if(o && o.length > 0){
27364             for(var i = 0, len = o.length; i < len; i++){
27365                 var data = this.prepareData(o[i], i, o);
27366                 html[html.length] = this.tpl.apply(data);
27367             }
27368         }else{
27369             html.push(this.emptyText);
27370         }
27371         this.el.update(html.join(""));
27372         this.nodes = this.el.dom.childNodes;
27373         this.updateIndexes(0);
27374     },
27375
27376     /**
27377      * 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.
27378      * @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:
27379      <pre><code>
27380      view.load({
27381          url: "your-url.php",
27382          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27383          callback: yourFunction,
27384          scope: yourObject, //(optional scope)
27385          discardUrl: false,
27386          nocache: false,
27387          text: "Loading...",
27388          timeout: 30,
27389          scripts: false
27390      });
27391      </code></pre>
27392      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27393      * 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.
27394      * @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}
27395      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27396      * @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.
27397      */
27398     load : function(){
27399         var um = this.el.getUpdateManager();
27400         um.update.apply(um, arguments);
27401     },
27402
27403     // note - render is a standard framework call...
27404     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27405     render : function(el, response){
27406         
27407         this.clearSelections();
27408         this.el.update("");
27409         var o;
27410         try{
27411             if (response != '') {
27412                 o = Roo.util.JSON.decode(response.responseText);
27413                 if(this.jsonRoot){
27414                     
27415                     o = o[this.jsonRoot];
27416                 }
27417             }
27418         } catch(e){
27419         }
27420         /**
27421          * The current JSON data or null
27422          */
27423         this.jsonData = o;
27424         this.beforeRender();
27425         this.refresh();
27426     },
27427
27428 /**
27429  * Get the number of records in the current JSON dataset
27430  * @return {Number}
27431  */
27432     getCount : function(){
27433         return this.jsonData ? this.jsonData.length : 0;
27434     },
27435
27436 /**
27437  * Returns the JSON object for the specified node(s)
27438  * @param {HTMLElement/Array} node The node or an array of nodes
27439  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27440  * you get the JSON object for the node
27441  */
27442     getNodeData : function(node){
27443         if(node instanceof Array){
27444             var data = [];
27445             for(var i = 0, len = node.length; i < len; i++){
27446                 data.push(this.getNodeData(node[i]));
27447             }
27448             return data;
27449         }
27450         return this.jsonData[this.indexOf(node)] || null;
27451     },
27452
27453     beforeRender : function(){
27454         this.snapshot = this.jsonData;
27455         if(this.sortInfo){
27456             this.sort.apply(this, this.sortInfo);
27457         }
27458         this.fireEvent("beforerender", this, this.jsonData);
27459     },
27460
27461     onLoad : function(el, o){
27462         this.fireEvent("load", this, this.jsonData, o);
27463     },
27464
27465     onLoadException : function(el, o){
27466         this.fireEvent("loadexception", this, o);
27467     },
27468
27469 /**
27470  * Filter the data by a specific property.
27471  * @param {String} property A property on your JSON objects
27472  * @param {String/RegExp} value Either string that the property values
27473  * should start with, or a RegExp to test against the property
27474  */
27475     filter : function(property, value){
27476         if(this.jsonData){
27477             var data = [];
27478             var ss = this.snapshot;
27479             if(typeof value == "string"){
27480                 var vlen = value.length;
27481                 if(vlen == 0){
27482                     this.clearFilter();
27483                     return;
27484                 }
27485                 value = value.toLowerCase();
27486                 for(var i = 0, len = ss.length; i < len; i++){
27487                     var o = ss[i];
27488                     if(o[property].substr(0, vlen).toLowerCase() == value){
27489                         data.push(o);
27490                     }
27491                 }
27492             } else if(value.exec){ // regex?
27493                 for(var i = 0, len = ss.length; i < len; i++){
27494                     var o = ss[i];
27495                     if(value.test(o[property])){
27496                         data.push(o);
27497                     }
27498                 }
27499             } else{
27500                 return;
27501             }
27502             this.jsonData = data;
27503             this.refresh();
27504         }
27505     },
27506
27507 /**
27508  * Filter by a function. The passed function will be called with each
27509  * object in the current dataset. If the function returns true the value is kept,
27510  * otherwise it is filtered.
27511  * @param {Function} fn
27512  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27513  */
27514     filterBy : function(fn, scope){
27515         if(this.jsonData){
27516             var data = [];
27517             var ss = this.snapshot;
27518             for(var i = 0, len = ss.length; i < len; i++){
27519                 var o = ss[i];
27520                 if(fn.call(scope || this, o)){
27521                     data.push(o);
27522                 }
27523             }
27524             this.jsonData = data;
27525             this.refresh();
27526         }
27527     },
27528
27529 /**
27530  * Clears the current filter.
27531  */
27532     clearFilter : function(){
27533         if(this.snapshot && this.jsonData != this.snapshot){
27534             this.jsonData = this.snapshot;
27535             this.refresh();
27536         }
27537     },
27538
27539
27540 /**
27541  * Sorts the data for this view and refreshes it.
27542  * @param {String} property A property on your JSON objects to sort on
27543  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27544  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27545  */
27546     sort : function(property, dir, sortType){
27547         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27548         if(this.jsonData){
27549             var p = property;
27550             var dsc = dir && dir.toLowerCase() == "desc";
27551             var f = function(o1, o2){
27552                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27553                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27554                 ;
27555                 if(v1 < v2){
27556                     return dsc ? +1 : -1;
27557                 } else if(v1 > v2){
27558                     return dsc ? -1 : +1;
27559                 } else{
27560                     return 0;
27561                 }
27562             };
27563             this.jsonData.sort(f);
27564             this.refresh();
27565             if(this.jsonData != this.snapshot){
27566                 this.snapshot.sort(f);
27567             }
27568         }
27569     }
27570 });/*
27571  * Based on:
27572  * Ext JS Library 1.1.1
27573  * Copyright(c) 2006-2007, Ext JS, LLC.
27574  *
27575  * Originally Released Under LGPL - original licence link has changed is not relivant.
27576  *
27577  * Fork - LGPL
27578  * <script type="text/javascript">
27579  */
27580  
27581
27582 /**
27583  * @class Roo.ColorPalette
27584  * @extends Roo.Component
27585  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27586  * Here's an example of typical usage:
27587  * <pre><code>
27588 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27589 cp.render('my-div');
27590
27591 cp.on('select', function(palette, selColor){
27592     // do something with selColor
27593 });
27594 </code></pre>
27595  * @constructor
27596  * Create a new ColorPalette
27597  * @param {Object} config The config object
27598  */
27599 Roo.ColorPalette = function(config){
27600     Roo.ColorPalette.superclass.constructor.call(this, config);
27601     this.addEvents({
27602         /**
27603              * @event select
27604              * Fires when a color is selected
27605              * @param {ColorPalette} this
27606              * @param {String} color The 6-digit color hex code (without the # symbol)
27607              */
27608         select: true
27609     });
27610
27611     if(this.handler){
27612         this.on("select", this.handler, this.scope, true);
27613     }
27614 };
27615 Roo.extend(Roo.ColorPalette, Roo.Component, {
27616     /**
27617      * @cfg {String} itemCls
27618      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27619      */
27620     itemCls : "x-color-palette",
27621     /**
27622      * @cfg {String} value
27623      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27624      * the hex codes are case-sensitive.
27625      */
27626     value : null,
27627     clickEvent:'click',
27628     // private
27629     ctype: "Roo.ColorPalette",
27630
27631     /**
27632      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27633      */
27634     allowReselect : false,
27635
27636     /**
27637      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27638      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27639      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27640      * of colors with the width setting until the box is symmetrical.</p>
27641      * <p>You can override individual colors if needed:</p>
27642      * <pre><code>
27643 var cp = new Roo.ColorPalette();
27644 cp.colors[0] = "FF0000";  // change the first box to red
27645 </code></pre>
27646
27647 Or you can provide a custom array of your own for complete control:
27648 <pre><code>
27649 var cp = new Roo.ColorPalette();
27650 cp.colors = ["000000", "993300", "333300"];
27651 </code></pre>
27652      * @type Array
27653      */
27654     colors : [
27655         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27656         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27657         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27658         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27659         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27660     ],
27661
27662     // private
27663     onRender : function(container, position){
27664         var t = new Roo.MasterTemplate(
27665             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27666         );
27667         var c = this.colors;
27668         for(var i = 0, len = c.length; i < len; i++){
27669             t.add([c[i]]);
27670         }
27671         var el = document.createElement("div");
27672         el.className = this.itemCls;
27673         t.overwrite(el);
27674         container.dom.insertBefore(el, position);
27675         this.el = Roo.get(el);
27676         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27677         if(this.clickEvent != 'click'){
27678             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27679         }
27680     },
27681
27682     // private
27683     afterRender : function(){
27684         Roo.ColorPalette.superclass.afterRender.call(this);
27685         if(this.value){
27686             var s = this.value;
27687             this.value = null;
27688             this.select(s);
27689         }
27690     },
27691
27692     // private
27693     handleClick : function(e, t){
27694         e.preventDefault();
27695         if(!this.disabled){
27696             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27697             this.select(c.toUpperCase());
27698         }
27699     },
27700
27701     /**
27702      * Selects the specified color in the palette (fires the select event)
27703      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27704      */
27705     select : function(color){
27706         color = color.replace("#", "");
27707         if(color != this.value || this.allowReselect){
27708             var el = this.el;
27709             if(this.value){
27710                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27711             }
27712             el.child("a.color-"+color).addClass("x-color-palette-sel");
27713             this.value = color;
27714             this.fireEvent("select", this, color);
27715         }
27716     }
27717 });/*
27718  * Based on:
27719  * Ext JS Library 1.1.1
27720  * Copyright(c) 2006-2007, Ext JS, LLC.
27721  *
27722  * Originally Released Under LGPL - original licence link has changed is not relivant.
27723  *
27724  * Fork - LGPL
27725  * <script type="text/javascript">
27726  */
27727  
27728 /**
27729  * @class Roo.DatePicker
27730  * @extends Roo.Component
27731  * Simple date picker class.
27732  * @constructor
27733  * Create a new DatePicker
27734  * @param {Object} config The config object
27735  */
27736 Roo.DatePicker = function(config){
27737     Roo.DatePicker.superclass.constructor.call(this, config);
27738
27739     this.value = config && config.value ?
27740                  config.value.clearTime() : new Date().clearTime();
27741
27742     this.addEvents({
27743         /**
27744              * @event select
27745              * Fires when a date is selected
27746              * @param {DatePicker} this
27747              * @param {Date} date The selected date
27748              */
27749         'select': true,
27750         /**
27751              * @event monthchange
27752              * Fires when the displayed month changes 
27753              * @param {DatePicker} this
27754              * @param {Date} date The selected month
27755              */
27756         'monthchange': true
27757     });
27758
27759     if(this.handler){
27760         this.on("select", this.handler,  this.scope || this);
27761     }
27762     // build the disabledDatesRE
27763     if(!this.disabledDatesRE && this.disabledDates){
27764         var dd = this.disabledDates;
27765         var re = "(?:";
27766         for(var i = 0; i < dd.length; i++){
27767             re += dd[i];
27768             if(i != dd.length-1) {
27769                 re += "|";
27770             }
27771         }
27772         this.disabledDatesRE = new RegExp(re + ")");
27773     }
27774 };
27775
27776 Roo.extend(Roo.DatePicker, Roo.Component, {
27777     /**
27778      * @cfg {String} todayText
27779      * The text to display on the button that selects the current date (defaults to "Today")
27780      */
27781     todayText : "Today",
27782     /**
27783      * @cfg {String} okText
27784      * The text to display on the ok button
27785      */
27786     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27787     /**
27788      * @cfg {String} cancelText
27789      * The text to display on the cancel button
27790      */
27791     cancelText : "Cancel",
27792     /**
27793      * @cfg {String} todayTip
27794      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27795      */
27796     todayTip : "{0} (Spacebar)",
27797     /**
27798      * @cfg {Date} minDate
27799      * Minimum allowable date (JavaScript date object, defaults to null)
27800      */
27801     minDate : null,
27802     /**
27803      * @cfg {Date} maxDate
27804      * Maximum allowable date (JavaScript date object, defaults to null)
27805      */
27806     maxDate : null,
27807     /**
27808      * @cfg {String} minText
27809      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27810      */
27811     minText : "This date is before the minimum date",
27812     /**
27813      * @cfg {String} maxText
27814      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27815      */
27816     maxText : "This date is after the maximum date",
27817     /**
27818      * @cfg {String} format
27819      * The default date format string which can be overriden for localization support.  The format must be
27820      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27821      */
27822     format : "m/d/y",
27823     /**
27824      * @cfg {Array} disabledDays
27825      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27826      */
27827     disabledDays : null,
27828     /**
27829      * @cfg {String} disabledDaysText
27830      * The tooltip to display when the date falls on a disabled day (defaults to "")
27831      */
27832     disabledDaysText : "",
27833     /**
27834      * @cfg {RegExp} disabledDatesRE
27835      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27836      */
27837     disabledDatesRE : null,
27838     /**
27839      * @cfg {String} disabledDatesText
27840      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27841      */
27842     disabledDatesText : "",
27843     /**
27844      * @cfg {Boolean} constrainToViewport
27845      * True to constrain the date picker to the viewport (defaults to true)
27846      */
27847     constrainToViewport : true,
27848     /**
27849      * @cfg {Array} monthNames
27850      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27851      */
27852     monthNames : Date.monthNames,
27853     /**
27854      * @cfg {Array} dayNames
27855      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27856      */
27857     dayNames : Date.dayNames,
27858     /**
27859      * @cfg {String} nextText
27860      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27861      */
27862     nextText: 'Next Month (Control+Right)',
27863     /**
27864      * @cfg {String} prevText
27865      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27866      */
27867     prevText: 'Previous Month (Control+Left)',
27868     /**
27869      * @cfg {String} monthYearText
27870      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27871      */
27872     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27873     /**
27874      * @cfg {Number} startDay
27875      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27876      */
27877     startDay : 0,
27878     /**
27879      * @cfg {Bool} showClear
27880      * Show a clear button (usefull for date form elements that can be blank.)
27881      */
27882     
27883     showClear: false,
27884     
27885     /**
27886      * Sets the value of the date field
27887      * @param {Date} value The date to set
27888      */
27889     setValue : function(value){
27890         var old = this.value;
27891         
27892         if (typeof(value) == 'string') {
27893          
27894             value = Date.parseDate(value, this.format);
27895         }
27896         if (!value) {
27897             value = new Date();
27898         }
27899         
27900         this.value = value.clearTime(true);
27901         if(this.el){
27902             this.update(this.value);
27903         }
27904     },
27905
27906     /**
27907      * Gets the current selected value of the date field
27908      * @return {Date} The selected date
27909      */
27910     getValue : function(){
27911         return this.value;
27912     },
27913
27914     // private
27915     focus : function(){
27916         if(this.el){
27917             this.update(this.activeDate);
27918         }
27919     },
27920
27921     // privateval
27922     onRender : function(container, position){
27923         
27924         var m = [
27925              '<table cellspacing="0">',
27926                 '<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>',
27927                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27928         var dn = this.dayNames;
27929         for(var i = 0; i < 7; i++){
27930             var d = this.startDay+i;
27931             if(d > 6){
27932                 d = d-7;
27933             }
27934             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27935         }
27936         m[m.length] = "</tr></thead><tbody><tr>";
27937         for(var i = 0; i < 42; i++) {
27938             if(i % 7 == 0 && i != 0){
27939                 m[m.length] = "</tr><tr>";
27940             }
27941             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27942         }
27943         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27944             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27945
27946         var el = document.createElement("div");
27947         el.className = "x-date-picker";
27948         el.innerHTML = m.join("");
27949
27950         container.dom.insertBefore(el, position);
27951
27952         this.el = Roo.get(el);
27953         this.eventEl = Roo.get(el.firstChild);
27954
27955         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27956             handler: this.showPrevMonth,
27957             scope: this,
27958             preventDefault:true,
27959             stopDefault:true
27960         });
27961
27962         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27963             handler: this.showNextMonth,
27964             scope: this,
27965             preventDefault:true,
27966             stopDefault:true
27967         });
27968
27969         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27970
27971         this.monthPicker = this.el.down('div.x-date-mp');
27972         this.monthPicker.enableDisplayMode('block');
27973         
27974         var kn = new Roo.KeyNav(this.eventEl, {
27975             "left" : function(e){
27976                 e.ctrlKey ?
27977                     this.showPrevMonth() :
27978                     this.update(this.activeDate.add("d", -1));
27979             },
27980
27981             "right" : function(e){
27982                 e.ctrlKey ?
27983                     this.showNextMonth() :
27984                     this.update(this.activeDate.add("d", 1));
27985             },
27986
27987             "up" : function(e){
27988                 e.ctrlKey ?
27989                     this.showNextYear() :
27990                     this.update(this.activeDate.add("d", -7));
27991             },
27992
27993             "down" : function(e){
27994                 e.ctrlKey ?
27995                     this.showPrevYear() :
27996                     this.update(this.activeDate.add("d", 7));
27997             },
27998
27999             "pageUp" : function(e){
28000                 this.showNextMonth();
28001             },
28002
28003             "pageDown" : function(e){
28004                 this.showPrevMonth();
28005             },
28006
28007             "enter" : function(e){
28008                 e.stopPropagation();
28009                 return true;
28010             },
28011
28012             scope : this
28013         });
28014
28015         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28016
28017         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28018
28019         this.el.unselectable();
28020         
28021         this.cells = this.el.select("table.x-date-inner tbody td");
28022         this.textNodes = this.el.query("table.x-date-inner tbody span");
28023
28024         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28025             text: "&#160;",
28026             tooltip: this.monthYearText
28027         });
28028
28029         this.mbtn.on('click', this.showMonthPicker, this);
28030         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28031
28032
28033         var today = (new Date()).dateFormat(this.format);
28034         
28035         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28036         if (this.showClear) {
28037             baseTb.add( new Roo.Toolbar.Fill());
28038         }
28039         baseTb.add({
28040             text: String.format(this.todayText, today),
28041             tooltip: String.format(this.todayTip, today),
28042             handler: this.selectToday,
28043             scope: this
28044         });
28045         
28046         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28047             
28048         //});
28049         if (this.showClear) {
28050             
28051             baseTb.add( new Roo.Toolbar.Fill());
28052             baseTb.add({
28053                 text: '&#160;',
28054                 cls: 'x-btn-icon x-btn-clear',
28055                 handler: function() {
28056                     //this.value = '';
28057                     this.fireEvent("select", this, '');
28058                 },
28059                 scope: this
28060             });
28061         }
28062         
28063         
28064         if(Roo.isIE){
28065             this.el.repaint();
28066         }
28067         this.update(this.value);
28068     },
28069
28070     createMonthPicker : function(){
28071         if(!this.monthPicker.dom.firstChild){
28072             var buf = ['<table border="0" cellspacing="0">'];
28073             for(var i = 0; i < 6; i++){
28074                 buf.push(
28075                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28076                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28077                     i == 0 ?
28078                     '<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>' :
28079                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28080                 );
28081             }
28082             buf.push(
28083                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28084                     this.okText,
28085                     '</button><button type="button" class="x-date-mp-cancel">',
28086                     this.cancelText,
28087                     '</button></td></tr>',
28088                 '</table>'
28089             );
28090             this.monthPicker.update(buf.join(''));
28091             this.monthPicker.on('click', this.onMonthClick, this);
28092             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28093
28094             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28095             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28096
28097             this.mpMonths.each(function(m, a, i){
28098                 i += 1;
28099                 if((i%2) == 0){
28100                     m.dom.xmonth = 5 + Math.round(i * .5);
28101                 }else{
28102                     m.dom.xmonth = Math.round((i-1) * .5);
28103                 }
28104             });
28105         }
28106     },
28107
28108     showMonthPicker : function(){
28109         this.createMonthPicker();
28110         var size = this.el.getSize();
28111         this.monthPicker.setSize(size);
28112         this.monthPicker.child('table').setSize(size);
28113
28114         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28115         this.updateMPMonth(this.mpSelMonth);
28116         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28117         this.updateMPYear(this.mpSelYear);
28118
28119         this.monthPicker.slideIn('t', {duration:.2});
28120     },
28121
28122     updateMPYear : function(y){
28123         this.mpyear = y;
28124         var ys = this.mpYears.elements;
28125         for(var i = 1; i <= 10; i++){
28126             var td = ys[i-1], y2;
28127             if((i%2) == 0){
28128                 y2 = y + Math.round(i * .5);
28129                 td.firstChild.innerHTML = y2;
28130                 td.xyear = y2;
28131             }else{
28132                 y2 = y - (5-Math.round(i * .5));
28133                 td.firstChild.innerHTML = y2;
28134                 td.xyear = y2;
28135             }
28136             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28137         }
28138     },
28139
28140     updateMPMonth : function(sm){
28141         this.mpMonths.each(function(m, a, i){
28142             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28143         });
28144     },
28145
28146     selectMPMonth: function(m){
28147         
28148     },
28149
28150     onMonthClick : function(e, t){
28151         e.stopEvent();
28152         var el = new Roo.Element(t), pn;
28153         if(el.is('button.x-date-mp-cancel')){
28154             this.hideMonthPicker();
28155         }
28156         else if(el.is('button.x-date-mp-ok')){
28157             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28158             this.hideMonthPicker();
28159         }
28160         else if(pn = el.up('td.x-date-mp-month', 2)){
28161             this.mpMonths.removeClass('x-date-mp-sel');
28162             pn.addClass('x-date-mp-sel');
28163             this.mpSelMonth = pn.dom.xmonth;
28164         }
28165         else if(pn = el.up('td.x-date-mp-year', 2)){
28166             this.mpYears.removeClass('x-date-mp-sel');
28167             pn.addClass('x-date-mp-sel');
28168             this.mpSelYear = pn.dom.xyear;
28169         }
28170         else if(el.is('a.x-date-mp-prev')){
28171             this.updateMPYear(this.mpyear-10);
28172         }
28173         else if(el.is('a.x-date-mp-next')){
28174             this.updateMPYear(this.mpyear+10);
28175         }
28176     },
28177
28178     onMonthDblClick : function(e, t){
28179         e.stopEvent();
28180         var el = new Roo.Element(t), pn;
28181         if(pn = el.up('td.x-date-mp-month', 2)){
28182             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28183             this.hideMonthPicker();
28184         }
28185         else if(pn = el.up('td.x-date-mp-year', 2)){
28186             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28187             this.hideMonthPicker();
28188         }
28189     },
28190
28191     hideMonthPicker : function(disableAnim){
28192         if(this.monthPicker){
28193             if(disableAnim === true){
28194                 this.monthPicker.hide();
28195             }else{
28196                 this.monthPicker.slideOut('t', {duration:.2});
28197             }
28198         }
28199     },
28200
28201     // private
28202     showPrevMonth : function(e){
28203         this.update(this.activeDate.add("mo", -1));
28204     },
28205
28206     // private
28207     showNextMonth : function(e){
28208         this.update(this.activeDate.add("mo", 1));
28209     },
28210
28211     // private
28212     showPrevYear : function(){
28213         this.update(this.activeDate.add("y", -1));
28214     },
28215
28216     // private
28217     showNextYear : function(){
28218         this.update(this.activeDate.add("y", 1));
28219     },
28220
28221     // private
28222     handleMouseWheel : function(e){
28223         var delta = e.getWheelDelta();
28224         if(delta > 0){
28225             this.showPrevMonth();
28226             e.stopEvent();
28227         } else if(delta < 0){
28228             this.showNextMonth();
28229             e.stopEvent();
28230         }
28231     },
28232
28233     // private
28234     handleDateClick : function(e, t){
28235         e.stopEvent();
28236         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28237             this.setValue(new Date(t.dateValue));
28238             this.fireEvent("select", this, this.value);
28239         }
28240     },
28241
28242     // private
28243     selectToday : function(){
28244         this.setValue(new Date().clearTime());
28245         this.fireEvent("select", this, this.value);
28246     },
28247
28248     // private
28249     update : function(date)
28250     {
28251         var vd = this.activeDate;
28252         this.activeDate = date;
28253         if(vd && this.el){
28254             var t = date.getTime();
28255             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28256                 this.cells.removeClass("x-date-selected");
28257                 this.cells.each(function(c){
28258                    if(c.dom.firstChild.dateValue == t){
28259                        c.addClass("x-date-selected");
28260                        setTimeout(function(){
28261                             try{c.dom.firstChild.focus();}catch(e){}
28262                        }, 50);
28263                        return false;
28264                    }
28265                 });
28266                 return;
28267             }
28268         }
28269         
28270         var days = date.getDaysInMonth();
28271         var firstOfMonth = date.getFirstDateOfMonth();
28272         var startingPos = firstOfMonth.getDay()-this.startDay;
28273
28274         if(startingPos <= this.startDay){
28275             startingPos += 7;
28276         }
28277
28278         var pm = date.add("mo", -1);
28279         var prevStart = pm.getDaysInMonth()-startingPos;
28280
28281         var cells = this.cells.elements;
28282         var textEls = this.textNodes;
28283         days += startingPos;
28284
28285         // convert everything to numbers so it's fast
28286         var day = 86400000;
28287         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28288         var today = new Date().clearTime().getTime();
28289         var sel = date.clearTime().getTime();
28290         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28291         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28292         var ddMatch = this.disabledDatesRE;
28293         var ddText = this.disabledDatesText;
28294         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28295         var ddaysText = this.disabledDaysText;
28296         var format = this.format;
28297
28298         var setCellClass = function(cal, cell){
28299             cell.title = "";
28300             var t = d.getTime();
28301             cell.firstChild.dateValue = t;
28302             if(t == today){
28303                 cell.className += " x-date-today";
28304                 cell.title = cal.todayText;
28305             }
28306             if(t == sel){
28307                 cell.className += " x-date-selected";
28308                 setTimeout(function(){
28309                     try{cell.firstChild.focus();}catch(e){}
28310                 }, 50);
28311             }
28312             // disabling
28313             if(t < min) {
28314                 cell.className = " x-date-disabled";
28315                 cell.title = cal.minText;
28316                 return;
28317             }
28318             if(t > max) {
28319                 cell.className = " x-date-disabled";
28320                 cell.title = cal.maxText;
28321                 return;
28322             }
28323             if(ddays){
28324                 if(ddays.indexOf(d.getDay()) != -1){
28325                     cell.title = ddaysText;
28326                     cell.className = " x-date-disabled";
28327                 }
28328             }
28329             if(ddMatch && format){
28330                 var fvalue = d.dateFormat(format);
28331                 if(ddMatch.test(fvalue)){
28332                     cell.title = ddText.replace("%0", fvalue);
28333                     cell.className = " x-date-disabled";
28334                 }
28335             }
28336         };
28337
28338         var i = 0;
28339         for(; i < startingPos; i++) {
28340             textEls[i].innerHTML = (++prevStart);
28341             d.setDate(d.getDate()+1);
28342             cells[i].className = "x-date-prevday";
28343             setCellClass(this, cells[i]);
28344         }
28345         for(; i < days; i++){
28346             intDay = i - startingPos + 1;
28347             textEls[i].innerHTML = (intDay);
28348             d.setDate(d.getDate()+1);
28349             cells[i].className = "x-date-active";
28350             setCellClass(this, cells[i]);
28351         }
28352         var extraDays = 0;
28353         for(; i < 42; i++) {
28354              textEls[i].innerHTML = (++extraDays);
28355              d.setDate(d.getDate()+1);
28356              cells[i].className = "x-date-nextday";
28357              setCellClass(this, cells[i]);
28358         }
28359
28360         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28361         this.fireEvent('monthchange', this, date);
28362         
28363         if(!this.internalRender){
28364             var main = this.el.dom.firstChild;
28365             var w = main.offsetWidth;
28366             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28367             Roo.fly(main).setWidth(w);
28368             this.internalRender = true;
28369             // opera does not respect the auto grow header center column
28370             // then, after it gets a width opera refuses to recalculate
28371             // without a second pass
28372             if(Roo.isOpera && !this.secondPass){
28373                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28374                 this.secondPass = true;
28375                 this.update.defer(10, this, [date]);
28376             }
28377         }
28378         
28379         
28380     }
28381 });        /*
28382  * Based on:
28383  * Ext JS Library 1.1.1
28384  * Copyright(c) 2006-2007, Ext JS, LLC.
28385  *
28386  * Originally Released Under LGPL - original licence link has changed is not relivant.
28387  *
28388  * Fork - LGPL
28389  * <script type="text/javascript">
28390  */
28391 /**
28392  * @class Roo.TabPanel
28393  * @extends Roo.util.Observable
28394  * A lightweight tab container.
28395  * <br><br>
28396  * Usage:
28397  * <pre><code>
28398 // basic tabs 1, built from existing content
28399 var tabs = new Roo.TabPanel("tabs1");
28400 tabs.addTab("script", "View Script");
28401 tabs.addTab("markup", "View Markup");
28402 tabs.activate("script");
28403
28404 // more advanced tabs, built from javascript
28405 var jtabs = new Roo.TabPanel("jtabs");
28406 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28407
28408 // set up the UpdateManager
28409 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28410 var updater = tab2.getUpdateManager();
28411 updater.setDefaultUrl("ajax1.htm");
28412 tab2.on('activate', updater.refresh, updater, true);
28413
28414 // Use setUrl for Ajax loading
28415 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28416 tab3.setUrl("ajax2.htm", null, true);
28417
28418 // Disabled tab
28419 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28420 tab4.disable();
28421
28422 jtabs.activate("jtabs-1");
28423  * </code></pre>
28424  * @constructor
28425  * Create a new TabPanel.
28426  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28427  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28428  */
28429 Roo.TabPanel = function(container, config){
28430     /**
28431     * The container element for this TabPanel.
28432     * @type Roo.Element
28433     */
28434     this.el = Roo.get(container, true);
28435     if(config){
28436         if(typeof config == "boolean"){
28437             this.tabPosition = config ? "bottom" : "top";
28438         }else{
28439             Roo.apply(this, config);
28440         }
28441     }
28442     if(this.tabPosition == "bottom"){
28443         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28444         this.el.addClass("x-tabs-bottom");
28445     }
28446     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28447     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28448     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28449     if(Roo.isIE){
28450         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28451     }
28452     if(this.tabPosition != "bottom"){
28453         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28454          * @type Roo.Element
28455          */
28456         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28457         this.el.addClass("x-tabs-top");
28458     }
28459     this.items = [];
28460
28461     this.bodyEl.setStyle("position", "relative");
28462
28463     this.active = null;
28464     this.activateDelegate = this.activate.createDelegate(this);
28465
28466     this.addEvents({
28467         /**
28468          * @event tabchange
28469          * Fires when the active tab changes
28470          * @param {Roo.TabPanel} this
28471          * @param {Roo.TabPanelItem} activePanel The new active tab
28472          */
28473         "tabchange": true,
28474         /**
28475          * @event beforetabchange
28476          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28477          * @param {Roo.TabPanel} this
28478          * @param {Object} e Set cancel to true on this object to cancel the tab change
28479          * @param {Roo.TabPanelItem} tab The tab being changed to
28480          */
28481         "beforetabchange" : true
28482     });
28483
28484     Roo.EventManager.onWindowResize(this.onResize, this);
28485     this.cpad = this.el.getPadding("lr");
28486     this.hiddenCount = 0;
28487
28488
28489     // toolbar on the tabbar support...
28490     if (this.toolbar) {
28491         var tcfg = this.toolbar;
28492         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28493         this.toolbar = new Roo.Toolbar(tcfg);
28494         if (Roo.isSafari) {
28495             var tbl = tcfg.container.child('table', true);
28496             tbl.setAttribute('width', '100%');
28497         }
28498         
28499     }
28500    
28501
28502
28503     Roo.TabPanel.superclass.constructor.call(this);
28504 };
28505
28506 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28507     /*
28508      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28509      */
28510     tabPosition : "top",
28511     /*
28512      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28513      */
28514     currentTabWidth : 0,
28515     /*
28516      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28517      */
28518     minTabWidth : 40,
28519     /*
28520      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28521      */
28522     maxTabWidth : 250,
28523     /*
28524      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28525      */
28526     preferredTabWidth : 175,
28527     /*
28528      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28529      */
28530     resizeTabs : false,
28531     /*
28532      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28533      */
28534     monitorResize : true,
28535     /*
28536      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28537      */
28538     toolbar : false,
28539
28540     /**
28541      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28542      * @param {String} id The id of the div to use <b>or create</b>
28543      * @param {String} text The text for the tab
28544      * @param {String} content (optional) Content to put in the TabPanelItem body
28545      * @param {Boolean} closable (optional) True to create a close icon on the tab
28546      * @return {Roo.TabPanelItem} The created TabPanelItem
28547      */
28548     addTab : function(id, text, content, closable){
28549         var item = new Roo.TabPanelItem(this, id, text, closable);
28550         this.addTabItem(item);
28551         if(content){
28552             item.setContent(content);
28553         }
28554         return item;
28555     },
28556
28557     /**
28558      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28559      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28560      * @return {Roo.TabPanelItem}
28561      */
28562     getTab : function(id){
28563         return this.items[id];
28564     },
28565
28566     /**
28567      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28568      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28569      */
28570     hideTab : function(id){
28571         var t = this.items[id];
28572         if(!t.isHidden()){
28573            t.setHidden(true);
28574            this.hiddenCount++;
28575            this.autoSizeTabs();
28576         }
28577     },
28578
28579     /**
28580      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28581      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28582      */
28583     unhideTab : function(id){
28584         var t = this.items[id];
28585         if(t.isHidden()){
28586            t.setHidden(false);
28587            this.hiddenCount--;
28588            this.autoSizeTabs();
28589         }
28590     },
28591
28592     /**
28593      * Adds an existing {@link Roo.TabPanelItem}.
28594      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28595      */
28596     addTabItem : function(item){
28597         this.items[item.id] = item;
28598         this.items.push(item);
28599         if(this.resizeTabs){
28600            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28601            this.autoSizeTabs();
28602         }else{
28603             item.autoSize();
28604         }
28605     },
28606
28607     /**
28608      * Removes a {@link Roo.TabPanelItem}.
28609      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28610      */
28611     removeTab : function(id){
28612         var items = this.items;
28613         var tab = items[id];
28614         if(!tab) { return; }
28615         var index = items.indexOf(tab);
28616         if(this.active == tab && items.length > 1){
28617             var newTab = this.getNextAvailable(index);
28618             if(newTab) {
28619                 newTab.activate();
28620             }
28621         }
28622         this.stripEl.dom.removeChild(tab.pnode.dom);
28623         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28624             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28625         }
28626         items.splice(index, 1);
28627         delete this.items[tab.id];
28628         tab.fireEvent("close", tab);
28629         tab.purgeListeners();
28630         this.autoSizeTabs();
28631     },
28632
28633     getNextAvailable : function(start){
28634         var items = this.items;
28635         var index = start;
28636         // look for a next tab that will slide over to
28637         // replace the one being removed
28638         while(index < items.length){
28639             var item = items[++index];
28640             if(item && !item.isHidden()){
28641                 return item;
28642             }
28643         }
28644         // if one isn't found select the previous tab (on the left)
28645         index = start;
28646         while(index >= 0){
28647             var item = items[--index];
28648             if(item && !item.isHidden()){
28649                 return item;
28650             }
28651         }
28652         return null;
28653     },
28654
28655     /**
28656      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28657      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28658      */
28659     disableTab : function(id){
28660         var tab = this.items[id];
28661         if(tab && this.active != tab){
28662             tab.disable();
28663         }
28664     },
28665
28666     /**
28667      * Enables a {@link Roo.TabPanelItem} that is disabled.
28668      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28669      */
28670     enableTab : function(id){
28671         var tab = this.items[id];
28672         tab.enable();
28673     },
28674
28675     /**
28676      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28677      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28678      * @return {Roo.TabPanelItem} The TabPanelItem.
28679      */
28680     activate : function(id){
28681         var tab = this.items[id];
28682         if(!tab){
28683             return null;
28684         }
28685         if(tab == this.active || tab.disabled){
28686             return tab;
28687         }
28688         var e = {};
28689         this.fireEvent("beforetabchange", this, e, tab);
28690         if(e.cancel !== true && !tab.disabled){
28691             if(this.active){
28692                 this.active.hide();
28693             }
28694             this.active = this.items[id];
28695             this.active.show();
28696             this.fireEvent("tabchange", this, this.active);
28697         }
28698         return tab;
28699     },
28700
28701     /**
28702      * Gets the active {@link Roo.TabPanelItem}.
28703      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28704      */
28705     getActiveTab : function(){
28706         return this.active;
28707     },
28708
28709     /**
28710      * Updates the tab body element to fit the height of the container element
28711      * for overflow scrolling
28712      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28713      */
28714     syncHeight : function(targetHeight){
28715         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28716         var bm = this.bodyEl.getMargins();
28717         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28718         this.bodyEl.setHeight(newHeight);
28719         return newHeight;
28720     },
28721
28722     onResize : function(){
28723         if(this.monitorResize){
28724             this.autoSizeTabs();
28725         }
28726     },
28727
28728     /**
28729      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28730      */
28731     beginUpdate : function(){
28732         this.updating = true;
28733     },
28734
28735     /**
28736      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28737      */
28738     endUpdate : function(){
28739         this.updating = false;
28740         this.autoSizeTabs();
28741     },
28742
28743     /**
28744      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28745      */
28746     autoSizeTabs : function(){
28747         var count = this.items.length;
28748         var vcount = count - this.hiddenCount;
28749         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28750             return;
28751         }
28752         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28753         var availWidth = Math.floor(w / vcount);
28754         var b = this.stripBody;
28755         if(b.getWidth() > w){
28756             var tabs = this.items;
28757             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28758             if(availWidth < this.minTabWidth){
28759                 /*if(!this.sleft){    // incomplete scrolling code
28760                     this.createScrollButtons();
28761                 }
28762                 this.showScroll();
28763                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28764             }
28765         }else{
28766             if(this.currentTabWidth < this.preferredTabWidth){
28767                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28768             }
28769         }
28770     },
28771
28772     /**
28773      * Returns the number of tabs in this TabPanel.
28774      * @return {Number}
28775      */
28776      getCount : function(){
28777          return this.items.length;
28778      },
28779
28780     /**
28781      * Resizes all the tabs to the passed width
28782      * @param {Number} The new width
28783      */
28784     setTabWidth : function(width){
28785         this.currentTabWidth = width;
28786         for(var i = 0, len = this.items.length; i < len; i++) {
28787                 if(!this.items[i].isHidden()) {
28788                 this.items[i].setWidth(width);
28789             }
28790         }
28791     },
28792
28793     /**
28794      * Destroys this TabPanel
28795      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28796      */
28797     destroy : function(removeEl){
28798         Roo.EventManager.removeResizeListener(this.onResize, this);
28799         for(var i = 0, len = this.items.length; i < len; i++){
28800             this.items[i].purgeListeners();
28801         }
28802         if(removeEl === true){
28803             this.el.update("");
28804             this.el.remove();
28805         }
28806     }
28807 });
28808
28809 /**
28810  * @class Roo.TabPanelItem
28811  * @extends Roo.util.Observable
28812  * Represents an individual item (tab plus body) in a TabPanel.
28813  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28814  * @param {String} id The id of this TabPanelItem
28815  * @param {String} text The text for the tab of this TabPanelItem
28816  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28817  */
28818 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28819     /**
28820      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28821      * @type Roo.TabPanel
28822      */
28823     this.tabPanel = tabPanel;
28824     /**
28825      * The id for this TabPanelItem
28826      * @type String
28827      */
28828     this.id = id;
28829     /** @private */
28830     this.disabled = false;
28831     /** @private */
28832     this.text = text;
28833     /** @private */
28834     this.loaded = false;
28835     this.closable = closable;
28836
28837     /**
28838      * The body element for this TabPanelItem.
28839      * @type Roo.Element
28840      */
28841     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28842     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28843     this.bodyEl.setStyle("display", "block");
28844     this.bodyEl.setStyle("zoom", "1");
28845     this.hideAction();
28846
28847     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28848     /** @private */
28849     this.el = Roo.get(els.el, true);
28850     this.inner = Roo.get(els.inner, true);
28851     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28852     this.pnode = Roo.get(els.el.parentNode, true);
28853     this.el.on("mousedown", this.onTabMouseDown, this);
28854     this.el.on("click", this.onTabClick, this);
28855     /** @private */
28856     if(closable){
28857         var c = Roo.get(els.close, true);
28858         c.dom.title = this.closeText;
28859         c.addClassOnOver("close-over");
28860         c.on("click", this.closeClick, this);
28861      }
28862
28863     this.addEvents({
28864          /**
28865          * @event activate
28866          * Fires when this tab becomes the active tab.
28867          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28868          * @param {Roo.TabPanelItem} this
28869          */
28870         "activate": true,
28871         /**
28872          * @event beforeclose
28873          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28874          * @param {Roo.TabPanelItem} this
28875          * @param {Object} e Set cancel to true on this object to cancel the close.
28876          */
28877         "beforeclose": true,
28878         /**
28879          * @event close
28880          * Fires when this tab is closed.
28881          * @param {Roo.TabPanelItem} this
28882          */
28883          "close": true,
28884         /**
28885          * @event deactivate
28886          * Fires when this tab is no longer the active tab.
28887          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28888          * @param {Roo.TabPanelItem} this
28889          */
28890          "deactivate" : true
28891     });
28892     this.hidden = false;
28893
28894     Roo.TabPanelItem.superclass.constructor.call(this);
28895 };
28896
28897 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28898     purgeListeners : function(){
28899        Roo.util.Observable.prototype.purgeListeners.call(this);
28900        this.el.removeAllListeners();
28901     },
28902     /**
28903      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28904      */
28905     show : function(){
28906         this.pnode.addClass("on");
28907         this.showAction();
28908         if(Roo.isOpera){
28909             this.tabPanel.stripWrap.repaint();
28910         }
28911         this.fireEvent("activate", this.tabPanel, this);
28912     },
28913
28914     /**
28915      * Returns true if this tab is the active tab.
28916      * @return {Boolean}
28917      */
28918     isActive : function(){
28919         return this.tabPanel.getActiveTab() == this;
28920     },
28921
28922     /**
28923      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28924      */
28925     hide : function(){
28926         this.pnode.removeClass("on");
28927         this.hideAction();
28928         this.fireEvent("deactivate", this.tabPanel, this);
28929     },
28930
28931     hideAction : function(){
28932         this.bodyEl.hide();
28933         this.bodyEl.setStyle("position", "absolute");
28934         this.bodyEl.setLeft("-20000px");
28935         this.bodyEl.setTop("-20000px");
28936     },
28937
28938     showAction : function(){
28939         this.bodyEl.setStyle("position", "relative");
28940         this.bodyEl.setTop("");
28941         this.bodyEl.setLeft("");
28942         this.bodyEl.show();
28943     },
28944
28945     /**
28946      * Set the tooltip for the tab.
28947      * @param {String} tooltip The tab's tooltip
28948      */
28949     setTooltip : function(text){
28950         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28951             this.textEl.dom.qtip = text;
28952             this.textEl.dom.removeAttribute('title');
28953         }else{
28954             this.textEl.dom.title = text;
28955         }
28956     },
28957
28958     onTabClick : function(e){
28959         e.preventDefault();
28960         this.tabPanel.activate(this.id);
28961     },
28962
28963     onTabMouseDown : function(e){
28964         e.preventDefault();
28965         this.tabPanel.activate(this.id);
28966     },
28967
28968     getWidth : function(){
28969         return this.inner.getWidth();
28970     },
28971
28972     setWidth : function(width){
28973         var iwidth = width - this.pnode.getPadding("lr");
28974         this.inner.setWidth(iwidth);
28975         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28976         this.pnode.setWidth(width);
28977     },
28978
28979     /**
28980      * Show or hide the tab
28981      * @param {Boolean} hidden True to hide or false to show.
28982      */
28983     setHidden : function(hidden){
28984         this.hidden = hidden;
28985         this.pnode.setStyle("display", hidden ? "none" : "");
28986     },
28987
28988     /**
28989      * Returns true if this tab is "hidden"
28990      * @return {Boolean}
28991      */
28992     isHidden : function(){
28993         return this.hidden;
28994     },
28995
28996     /**
28997      * Returns the text for this tab
28998      * @return {String}
28999      */
29000     getText : function(){
29001         return this.text;
29002     },
29003
29004     autoSize : function(){
29005         //this.el.beginMeasure();
29006         this.textEl.setWidth(1);
29007         /*
29008          *  #2804 [new] Tabs in Roojs
29009          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29010          */
29011         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29012         //this.el.endMeasure();
29013     },
29014
29015     /**
29016      * Sets the text for the tab (Note: this also sets the tooltip text)
29017      * @param {String} text The tab's text and tooltip
29018      */
29019     setText : function(text){
29020         this.text = text;
29021         this.textEl.update(text);
29022         this.setTooltip(text);
29023         if(!this.tabPanel.resizeTabs){
29024             this.autoSize();
29025         }
29026     },
29027     /**
29028      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29029      */
29030     activate : function(){
29031         this.tabPanel.activate(this.id);
29032     },
29033
29034     /**
29035      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29036      */
29037     disable : function(){
29038         if(this.tabPanel.active != this){
29039             this.disabled = true;
29040             this.pnode.addClass("disabled");
29041         }
29042     },
29043
29044     /**
29045      * Enables this TabPanelItem if it was previously disabled.
29046      */
29047     enable : function(){
29048         this.disabled = false;
29049         this.pnode.removeClass("disabled");
29050     },
29051
29052     /**
29053      * Sets the content for this TabPanelItem.
29054      * @param {String} content The content
29055      * @param {Boolean} loadScripts true to look for and load scripts
29056      */
29057     setContent : function(content, loadScripts){
29058         this.bodyEl.update(content, loadScripts);
29059     },
29060
29061     /**
29062      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29063      * @return {Roo.UpdateManager} The UpdateManager
29064      */
29065     getUpdateManager : function(){
29066         return this.bodyEl.getUpdateManager();
29067     },
29068
29069     /**
29070      * Set a URL to be used to load the content for this TabPanelItem.
29071      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29072      * @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)
29073      * @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)
29074      * @return {Roo.UpdateManager} The UpdateManager
29075      */
29076     setUrl : function(url, params, loadOnce){
29077         if(this.refreshDelegate){
29078             this.un('activate', this.refreshDelegate);
29079         }
29080         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29081         this.on("activate", this.refreshDelegate);
29082         return this.bodyEl.getUpdateManager();
29083     },
29084
29085     /** @private */
29086     _handleRefresh : function(url, params, loadOnce){
29087         if(!loadOnce || !this.loaded){
29088             var updater = this.bodyEl.getUpdateManager();
29089             updater.update(url, params, this._setLoaded.createDelegate(this));
29090         }
29091     },
29092
29093     /**
29094      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29095      *   Will fail silently if the setUrl method has not been called.
29096      *   This does not activate the panel, just updates its content.
29097      */
29098     refresh : function(){
29099         if(this.refreshDelegate){
29100            this.loaded = false;
29101            this.refreshDelegate();
29102         }
29103     },
29104
29105     /** @private */
29106     _setLoaded : function(){
29107         this.loaded = true;
29108     },
29109
29110     /** @private */
29111     closeClick : function(e){
29112         var o = {};
29113         e.stopEvent();
29114         this.fireEvent("beforeclose", this, o);
29115         if(o.cancel !== true){
29116             this.tabPanel.removeTab(this.id);
29117         }
29118     },
29119     /**
29120      * The text displayed in the tooltip for the close icon.
29121      * @type String
29122      */
29123     closeText : "Close this tab"
29124 });
29125
29126 /** @private */
29127 Roo.TabPanel.prototype.createStrip = function(container){
29128     var strip = document.createElement("div");
29129     strip.className = "x-tabs-wrap";
29130     container.appendChild(strip);
29131     return strip;
29132 };
29133 /** @private */
29134 Roo.TabPanel.prototype.createStripList = function(strip){
29135     // div wrapper for retard IE
29136     // returns the "tr" element.
29137     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29138         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29139         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29140     return strip.firstChild.firstChild.firstChild.firstChild;
29141 };
29142 /** @private */
29143 Roo.TabPanel.prototype.createBody = function(container){
29144     var body = document.createElement("div");
29145     Roo.id(body, "tab-body");
29146     Roo.fly(body).addClass("x-tabs-body");
29147     container.appendChild(body);
29148     return body;
29149 };
29150 /** @private */
29151 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29152     var body = Roo.getDom(id);
29153     if(!body){
29154         body = document.createElement("div");
29155         body.id = id;
29156     }
29157     Roo.fly(body).addClass("x-tabs-item-body");
29158     bodyEl.insertBefore(body, bodyEl.firstChild);
29159     return body;
29160 };
29161 /** @private */
29162 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29163     var td = document.createElement("td");
29164     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29165     //stripEl.appendChild(td);
29166     if(closable){
29167         td.className = "x-tabs-closable";
29168         if(!this.closeTpl){
29169             this.closeTpl = new Roo.Template(
29170                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29171                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29172                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29173             );
29174         }
29175         var el = this.closeTpl.overwrite(td, {"text": text});
29176         var close = el.getElementsByTagName("div")[0];
29177         var inner = el.getElementsByTagName("em")[0];
29178         return {"el": el, "close": close, "inner": inner};
29179     } else {
29180         if(!this.tabTpl){
29181             this.tabTpl = new Roo.Template(
29182                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29183                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29184             );
29185         }
29186         var el = this.tabTpl.overwrite(td, {"text": text});
29187         var inner = el.getElementsByTagName("em")[0];
29188         return {"el": el, "inner": inner};
29189     }
29190 };/*
29191  * Based on:
29192  * Ext JS Library 1.1.1
29193  * Copyright(c) 2006-2007, Ext JS, LLC.
29194  *
29195  * Originally Released Under LGPL - original licence link has changed is not relivant.
29196  *
29197  * Fork - LGPL
29198  * <script type="text/javascript">
29199  */
29200
29201 /**
29202  * @class Roo.Button
29203  * @extends Roo.util.Observable
29204  * Simple Button class
29205  * @cfg {String} text The button text
29206  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29207  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29208  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29209  * @cfg {Object} scope The scope of the handler
29210  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29211  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29212  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29213  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29214  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29215  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29216    applies if enableToggle = true)
29217  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29218  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29219   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29220  * @constructor
29221  * Create a new button
29222  * @param {Object} config The config object
29223  */
29224 Roo.Button = function(renderTo, config)
29225 {
29226     if (!config) {
29227         config = renderTo;
29228         renderTo = config.renderTo || false;
29229     }
29230     
29231     Roo.apply(this, config);
29232     this.addEvents({
29233         /**
29234              * @event click
29235              * Fires when this button is clicked
29236              * @param {Button} this
29237              * @param {EventObject} e The click event
29238              */
29239             "click" : true,
29240         /**
29241              * @event toggle
29242              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29243              * @param {Button} this
29244              * @param {Boolean} pressed
29245              */
29246             "toggle" : true,
29247         /**
29248              * @event mouseover
29249              * Fires when the mouse hovers over the button
29250              * @param {Button} this
29251              * @param {Event} e The event object
29252              */
29253         'mouseover' : true,
29254         /**
29255              * @event mouseout
29256              * Fires when the mouse exits the button
29257              * @param {Button} this
29258              * @param {Event} e The event object
29259              */
29260         'mouseout': true,
29261          /**
29262              * @event render
29263              * Fires when the button is rendered
29264              * @param {Button} this
29265              */
29266         'render': true
29267     });
29268     if(this.menu){
29269         this.menu = Roo.menu.MenuMgr.get(this.menu);
29270     }
29271     // register listeners first!!  - so render can be captured..
29272     Roo.util.Observable.call(this);
29273     if(renderTo){
29274         this.render(renderTo);
29275     }
29276     
29277   
29278 };
29279
29280 Roo.extend(Roo.Button, Roo.util.Observable, {
29281     /**
29282      * 
29283      */
29284     
29285     /**
29286      * Read-only. True if this button is hidden
29287      * @type Boolean
29288      */
29289     hidden : false,
29290     /**
29291      * Read-only. True if this button is disabled
29292      * @type Boolean
29293      */
29294     disabled : false,
29295     /**
29296      * Read-only. True if this button is pressed (only if enableToggle = true)
29297      * @type Boolean
29298      */
29299     pressed : false,
29300
29301     /**
29302      * @cfg {Number} tabIndex 
29303      * The DOM tabIndex for this button (defaults to undefined)
29304      */
29305     tabIndex : undefined,
29306
29307     /**
29308      * @cfg {Boolean} enableToggle
29309      * True to enable pressed/not pressed toggling (defaults to false)
29310      */
29311     enableToggle: false,
29312     /**
29313      * @cfg {Mixed} menu
29314      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29315      */
29316     menu : undefined,
29317     /**
29318      * @cfg {String} menuAlign
29319      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29320      */
29321     menuAlign : "tl-bl?",
29322
29323     /**
29324      * @cfg {String} iconCls
29325      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29326      */
29327     iconCls : undefined,
29328     /**
29329      * @cfg {String} type
29330      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29331      */
29332     type : 'button',
29333
29334     // private
29335     menuClassTarget: 'tr',
29336
29337     /**
29338      * @cfg {String} clickEvent
29339      * The type of event to map to the button's event handler (defaults to 'click')
29340      */
29341     clickEvent : 'click',
29342
29343     /**
29344      * @cfg {Boolean} handleMouseEvents
29345      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29346      */
29347     handleMouseEvents : true,
29348
29349     /**
29350      * @cfg {String} tooltipType
29351      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29352      */
29353     tooltipType : 'qtip',
29354
29355     /**
29356      * @cfg {String} cls
29357      * A CSS class to apply to the button's main element.
29358      */
29359     
29360     /**
29361      * @cfg {Roo.Template} template (Optional)
29362      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29363      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29364      * require code modifications if required elements (e.g. a button) aren't present.
29365      */
29366
29367     // private
29368     render : function(renderTo){
29369         var btn;
29370         if(this.hideParent){
29371             this.parentEl = Roo.get(renderTo);
29372         }
29373         if(!this.dhconfig){
29374             if(!this.template){
29375                 if(!Roo.Button.buttonTemplate){
29376                     // hideous table template
29377                     Roo.Button.buttonTemplate = new Roo.Template(
29378                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29379                         '<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>',
29380                         "</tr></tbody></table>");
29381                 }
29382                 this.template = Roo.Button.buttonTemplate;
29383             }
29384             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29385             var btnEl = btn.child("button:first");
29386             btnEl.on('focus', this.onFocus, this);
29387             btnEl.on('blur', this.onBlur, this);
29388             if(this.cls){
29389                 btn.addClass(this.cls);
29390             }
29391             if(this.icon){
29392                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29393             }
29394             if(this.iconCls){
29395                 btnEl.addClass(this.iconCls);
29396                 if(!this.cls){
29397                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29398                 }
29399             }
29400             if(this.tabIndex !== undefined){
29401                 btnEl.dom.tabIndex = this.tabIndex;
29402             }
29403             if(this.tooltip){
29404                 if(typeof this.tooltip == 'object'){
29405                     Roo.QuickTips.tips(Roo.apply({
29406                           target: btnEl.id
29407                     }, this.tooltip));
29408                 } else {
29409                     btnEl.dom[this.tooltipType] = this.tooltip;
29410                 }
29411             }
29412         }else{
29413             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29414         }
29415         this.el = btn;
29416         if(this.id){
29417             this.el.dom.id = this.el.id = this.id;
29418         }
29419         if(this.menu){
29420             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29421             this.menu.on("show", this.onMenuShow, this);
29422             this.menu.on("hide", this.onMenuHide, this);
29423         }
29424         btn.addClass("x-btn");
29425         if(Roo.isIE && !Roo.isIE7){
29426             this.autoWidth.defer(1, this);
29427         }else{
29428             this.autoWidth();
29429         }
29430         if(this.handleMouseEvents){
29431             btn.on("mouseover", this.onMouseOver, this);
29432             btn.on("mouseout", this.onMouseOut, this);
29433             btn.on("mousedown", this.onMouseDown, this);
29434         }
29435         btn.on(this.clickEvent, this.onClick, this);
29436         //btn.on("mouseup", this.onMouseUp, this);
29437         if(this.hidden){
29438             this.hide();
29439         }
29440         if(this.disabled){
29441             this.disable();
29442         }
29443         Roo.ButtonToggleMgr.register(this);
29444         if(this.pressed){
29445             this.el.addClass("x-btn-pressed");
29446         }
29447         if(this.repeat){
29448             var repeater = new Roo.util.ClickRepeater(btn,
29449                 typeof this.repeat == "object" ? this.repeat : {}
29450             );
29451             repeater.on("click", this.onClick,  this);
29452         }
29453         
29454         this.fireEvent('render', this);
29455         
29456     },
29457     /**
29458      * Returns the button's underlying element
29459      * @return {Roo.Element} The element
29460      */
29461     getEl : function(){
29462         return this.el;  
29463     },
29464     
29465     /**
29466      * Destroys this Button and removes any listeners.
29467      */
29468     destroy : function(){
29469         Roo.ButtonToggleMgr.unregister(this);
29470         this.el.removeAllListeners();
29471         this.purgeListeners();
29472         this.el.remove();
29473     },
29474
29475     // private
29476     autoWidth : function(){
29477         if(this.el){
29478             this.el.setWidth("auto");
29479             if(Roo.isIE7 && Roo.isStrict){
29480                 var ib = this.el.child('button');
29481                 if(ib && ib.getWidth() > 20){
29482                     ib.clip();
29483                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29484                 }
29485             }
29486             if(this.minWidth){
29487                 if(this.hidden){
29488                     this.el.beginMeasure();
29489                 }
29490                 if(this.el.getWidth() < this.minWidth){
29491                     this.el.setWidth(this.minWidth);
29492                 }
29493                 if(this.hidden){
29494                     this.el.endMeasure();
29495                 }
29496             }
29497         }
29498     },
29499
29500     /**
29501      * Assigns this button's click handler
29502      * @param {Function} handler The function to call when the button is clicked
29503      * @param {Object} scope (optional) Scope for the function passed in
29504      */
29505     setHandler : function(handler, scope){
29506         this.handler = handler;
29507         this.scope = scope;  
29508     },
29509     
29510     /**
29511      * Sets this button's text
29512      * @param {String} text The button text
29513      */
29514     setText : function(text){
29515         this.text = text;
29516         if(this.el){
29517             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29518         }
29519         this.autoWidth();
29520     },
29521     
29522     /**
29523      * Gets the text for this button
29524      * @return {String} The button text
29525      */
29526     getText : function(){
29527         return this.text;  
29528     },
29529     
29530     /**
29531      * Show this button
29532      */
29533     show: function(){
29534         this.hidden = false;
29535         if(this.el){
29536             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29537         }
29538     },
29539     
29540     /**
29541      * Hide this button
29542      */
29543     hide: function(){
29544         this.hidden = true;
29545         if(this.el){
29546             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29547         }
29548     },
29549     
29550     /**
29551      * Convenience function for boolean show/hide
29552      * @param {Boolean} visible True to show, false to hide
29553      */
29554     setVisible: function(visible){
29555         if(visible) {
29556             this.show();
29557         }else{
29558             this.hide();
29559         }
29560     },
29561     
29562     /**
29563      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29564      * @param {Boolean} state (optional) Force a particular state
29565      */
29566     toggle : function(state){
29567         state = state === undefined ? !this.pressed : state;
29568         if(state != this.pressed){
29569             if(state){
29570                 this.el.addClass("x-btn-pressed");
29571                 this.pressed = true;
29572                 this.fireEvent("toggle", this, true);
29573             }else{
29574                 this.el.removeClass("x-btn-pressed");
29575                 this.pressed = false;
29576                 this.fireEvent("toggle", this, false);
29577             }
29578             if(this.toggleHandler){
29579                 this.toggleHandler.call(this.scope || this, this, state);
29580             }
29581         }
29582     },
29583     
29584     /**
29585      * Focus the button
29586      */
29587     focus : function(){
29588         this.el.child('button:first').focus();
29589     },
29590     
29591     /**
29592      * Disable this button
29593      */
29594     disable : function(){
29595         if(this.el){
29596             this.el.addClass("x-btn-disabled");
29597         }
29598         this.disabled = true;
29599     },
29600     
29601     /**
29602      * Enable this button
29603      */
29604     enable : function(){
29605         if(this.el){
29606             this.el.removeClass("x-btn-disabled");
29607         }
29608         this.disabled = false;
29609     },
29610
29611     /**
29612      * Convenience function for boolean enable/disable
29613      * @param {Boolean} enabled True to enable, false to disable
29614      */
29615     setDisabled : function(v){
29616         this[v !== true ? "enable" : "disable"]();
29617     },
29618
29619     // private
29620     onClick : function(e)
29621     {
29622         if(e){
29623             e.preventDefault();
29624         }
29625         if(e.button != 0){
29626             return;
29627         }
29628         if(!this.disabled){
29629             if(this.enableToggle){
29630                 this.toggle();
29631             }
29632             if(this.menu && !this.menu.isVisible()){
29633                 this.menu.show(this.el, this.menuAlign);
29634             }
29635             this.fireEvent("click", this, e);
29636             if(this.handler){
29637                 this.el.removeClass("x-btn-over");
29638                 this.handler.call(this.scope || this, this, e);
29639             }
29640         }
29641     },
29642     // private
29643     onMouseOver : function(e){
29644         if(!this.disabled){
29645             this.el.addClass("x-btn-over");
29646             this.fireEvent('mouseover', this, e);
29647         }
29648     },
29649     // private
29650     onMouseOut : function(e){
29651         if(!e.within(this.el,  true)){
29652             this.el.removeClass("x-btn-over");
29653             this.fireEvent('mouseout', this, e);
29654         }
29655     },
29656     // private
29657     onFocus : function(e){
29658         if(!this.disabled){
29659             this.el.addClass("x-btn-focus");
29660         }
29661     },
29662     // private
29663     onBlur : function(e){
29664         this.el.removeClass("x-btn-focus");
29665     },
29666     // private
29667     onMouseDown : function(e){
29668         if(!this.disabled && e.button == 0){
29669             this.el.addClass("x-btn-click");
29670             Roo.get(document).on('mouseup', this.onMouseUp, this);
29671         }
29672     },
29673     // private
29674     onMouseUp : function(e){
29675         if(e.button == 0){
29676             this.el.removeClass("x-btn-click");
29677             Roo.get(document).un('mouseup', this.onMouseUp, this);
29678         }
29679     },
29680     // private
29681     onMenuShow : function(e){
29682         this.el.addClass("x-btn-menu-active");
29683     },
29684     // private
29685     onMenuHide : function(e){
29686         this.el.removeClass("x-btn-menu-active");
29687     }   
29688 });
29689
29690 // Private utility class used by Button
29691 Roo.ButtonToggleMgr = function(){
29692    var groups = {};
29693    
29694    function toggleGroup(btn, state){
29695        if(state){
29696            var g = groups[btn.toggleGroup];
29697            for(var i = 0, l = g.length; i < l; i++){
29698                if(g[i] != btn){
29699                    g[i].toggle(false);
29700                }
29701            }
29702        }
29703    }
29704    
29705    return {
29706        register : function(btn){
29707            if(!btn.toggleGroup){
29708                return;
29709            }
29710            var g = groups[btn.toggleGroup];
29711            if(!g){
29712                g = groups[btn.toggleGroup] = [];
29713            }
29714            g.push(btn);
29715            btn.on("toggle", toggleGroup);
29716        },
29717        
29718        unregister : function(btn){
29719            if(!btn.toggleGroup){
29720                return;
29721            }
29722            var g = groups[btn.toggleGroup];
29723            if(g){
29724                g.remove(btn);
29725                btn.un("toggle", toggleGroup);
29726            }
29727        }
29728    };
29729 }();/*
29730  * Based on:
29731  * Ext JS Library 1.1.1
29732  * Copyright(c) 2006-2007, Ext JS, LLC.
29733  *
29734  * Originally Released Under LGPL - original licence link has changed is not relivant.
29735  *
29736  * Fork - LGPL
29737  * <script type="text/javascript">
29738  */
29739  
29740 /**
29741  * @class Roo.SplitButton
29742  * @extends Roo.Button
29743  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29744  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29745  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29746  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29747  * @cfg {String} arrowTooltip The title attribute of the arrow
29748  * @constructor
29749  * Create a new menu button
29750  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29751  * @param {Object} config The config object
29752  */
29753 Roo.SplitButton = function(renderTo, config){
29754     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29755     /**
29756      * @event arrowclick
29757      * Fires when this button's arrow is clicked
29758      * @param {SplitButton} this
29759      * @param {EventObject} e The click event
29760      */
29761     this.addEvents({"arrowclick":true});
29762 };
29763
29764 Roo.extend(Roo.SplitButton, Roo.Button, {
29765     render : function(renderTo){
29766         // this is one sweet looking template!
29767         var tpl = new Roo.Template(
29768             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29769             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29770             '<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>',
29771             "</tbody></table></td><td>",
29772             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29773             '<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>',
29774             "</tbody></table></td></tr></table>"
29775         );
29776         var btn = tpl.append(renderTo, [this.text, this.type], true);
29777         var btnEl = btn.child("button");
29778         if(this.cls){
29779             btn.addClass(this.cls);
29780         }
29781         if(this.icon){
29782             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29783         }
29784         if(this.iconCls){
29785             btnEl.addClass(this.iconCls);
29786             if(!this.cls){
29787                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29788             }
29789         }
29790         this.el = btn;
29791         if(this.handleMouseEvents){
29792             btn.on("mouseover", this.onMouseOver, this);
29793             btn.on("mouseout", this.onMouseOut, this);
29794             btn.on("mousedown", this.onMouseDown, this);
29795             btn.on("mouseup", this.onMouseUp, this);
29796         }
29797         btn.on(this.clickEvent, this.onClick, this);
29798         if(this.tooltip){
29799             if(typeof this.tooltip == 'object'){
29800                 Roo.QuickTips.tips(Roo.apply({
29801                       target: btnEl.id
29802                 }, this.tooltip));
29803             } else {
29804                 btnEl.dom[this.tooltipType] = this.tooltip;
29805             }
29806         }
29807         if(this.arrowTooltip){
29808             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29809         }
29810         if(this.hidden){
29811             this.hide();
29812         }
29813         if(this.disabled){
29814             this.disable();
29815         }
29816         if(this.pressed){
29817             this.el.addClass("x-btn-pressed");
29818         }
29819         if(Roo.isIE && !Roo.isIE7){
29820             this.autoWidth.defer(1, this);
29821         }else{
29822             this.autoWidth();
29823         }
29824         if(this.menu){
29825             this.menu.on("show", this.onMenuShow, this);
29826             this.menu.on("hide", this.onMenuHide, this);
29827         }
29828         this.fireEvent('render', this);
29829     },
29830
29831     // private
29832     autoWidth : function(){
29833         if(this.el){
29834             var tbl = this.el.child("table:first");
29835             var tbl2 = this.el.child("table:last");
29836             this.el.setWidth("auto");
29837             tbl.setWidth("auto");
29838             if(Roo.isIE7 && Roo.isStrict){
29839                 var ib = this.el.child('button:first');
29840                 if(ib && ib.getWidth() > 20){
29841                     ib.clip();
29842                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29843                 }
29844             }
29845             if(this.minWidth){
29846                 if(this.hidden){
29847                     this.el.beginMeasure();
29848                 }
29849                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29850                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29851                 }
29852                 if(this.hidden){
29853                     this.el.endMeasure();
29854                 }
29855             }
29856             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29857         } 
29858     },
29859     /**
29860      * Sets this button's click handler
29861      * @param {Function} handler The function to call when the button is clicked
29862      * @param {Object} scope (optional) Scope for the function passed above
29863      */
29864     setHandler : function(handler, scope){
29865         this.handler = handler;
29866         this.scope = scope;  
29867     },
29868     
29869     /**
29870      * Sets this button's arrow click handler
29871      * @param {Function} handler The function to call when the arrow is clicked
29872      * @param {Object} scope (optional) Scope for the function passed above
29873      */
29874     setArrowHandler : function(handler, scope){
29875         this.arrowHandler = handler;
29876         this.scope = scope;  
29877     },
29878     
29879     /**
29880      * Focus the button
29881      */
29882     focus : function(){
29883         if(this.el){
29884             this.el.child("button:first").focus();
29885         }
29886     },
29887
29888     // private
29889     onClick : function(e){
29890         e.preventDefault();
29891         if(!this.disabled){
29892             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29893                 if(this.menu && !this.menu.isVisible()){
29894                     this.menu.show(this.el, this.menuAlign);
29895                 }
29896                 this.fireEvent("arrowclick", this, e);
29897                 if(this.arrowHandler){
29898                     this.arrowHandler.call(this.scope || this, this, e);
29899                 }
29900             }else{
29901                 this.fireEvent("click", this, e);
29902                 if(this.handler){
29903                     this.handler.call(this.scope || this, this, e);
29904                 }
29905             }
29906         }
29907     },
29908     // private
29909     onMouseDown : function(e){
29910         if(!this.disabled){
29911             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29912         }
29913     },
29914     // private
29915     onMouseUp : function(e){
29916         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29917     }   
29918 });
29919
29920
29921 // backwards compat
29922 Roo.MenuButton = Roo.SplitButton;/*
29923  * Based on:
29924  * Ext JS Library 1.1.1
29925  * Copyright(c) 2006-2007, Ext JS, LLC.
29926  *
29927  * Originally Released Under LGPL - original licence link has changed is not relivant.
29928  *
29929  * Fork - LGPL
29930  * <script type="text/javascript">
29931  */
29932
29933 /**
29934  * @class Roo.Toolbar
29935  * Basic Toolbar class.
29936  * @constructor
29937  * Creates a new Toolbar
29938  * @param {Object} container The config object
29939  */ 
29940 Roo.Toolbar = function(container, buttons, config)
29941 {
29942     /// old consturctor format still supported..
29943     if(container instanceof Array){ // omit the container for later rendering
29944         buttons = container;
29945         config = buttons;
29946         container = null;
29947     }
29948     if (typeof(container) == 'object' && container.xtype) {
29949         config = container;
29950         container = config.container;
29951         buttons = config.buttons || []; // not really - use items!!
29952     }
29953     var xitems = [];
29954     if (config && config.items) {
29955         xitems = config.items;
29956         delete config.items;
29957     }
29958     Roo.apply(this, config);
29959     this.buttons = buttons;
29960     
29961     if(container){
29962         this.render(container);
29963     }
29964     this.xitems = xitems;
29965     Roo.each(xitems, function(b) {
29966         this.add(b);
29967     }, this);
29968     
29969 };
29970
29971 Roo.Toolbar.prototype = {
29972     /**
29973      * @cfg {Array} items
29974      * array of button configs or elements to add (will be converted to a MixedCollection)
29975      */
29976     
29977     /**
29978      * @cfg {String/HTMLElement/Element} container
29979      * The id or element that will contain the toolbar
29980      */
29981     // private
29982     render : function(ct){
29983         this.el = Roo.get(ct);
29984         if(this.cls){
29985             this.el.addClass(this.cls);
29986         }
29987         // using a table allows for vertical alignment
29988         // 100% width is needed by Safari...
29989         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29990         this.tr = this.el.child("tr", true);
29991         var autoId = 0;
29992         this.items = new Roo.util.MixedCollection(false, function(o){
29993             return o.id || ("item" + (++autoId));
29994         });
29995         if(this.buttons){
29996             this.add.apply(this, this.buttons);
29997             delete this.buttons;
29998         }
29999     },
30000
30001     /**
30002      * Adds element(s) to the toolbar -- this function takes a variable number of 
30003      * arguments of mixed type and adds them to the toolbar.
30004      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30005      * <ul>
30006      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30007      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30008      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30009      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30010      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30011      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30012      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30013      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30014      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30015      * </ul>
30016      * @param {Mixed} arg2
30017      * @param {Mixed} etc.
30018      */
30019     add : function(){
30020         var a = arguments, l = a.length;
30021         for(var i = 0; i < l; i++){
30022             this._add(a[i]);
30023         }
30024     },
30025     // private..
30026     _add : function(el) {
30027         
30028         if (el.xtype) {
30029             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30030         }
30031         
30032         if (el.applyTo){ // some kind of form field
30033             return this.addField(el);
30034         } 
30035         if (el.render){ // some kind of Toolbar.Item
30036             return this.addItem(el);
30037         }
30038         if (typeof el == "string"){ // string
30039             if(el == "separator" || el == "-"){
30040                 return this.addSeparator();
30041             }
30042             if (el == " "){
30043                 return this.addSpacer();
30044             }
30045             if(el == "->"){
30046                 return this.addFill();
30047             }
30048             return this.addText(el);
30049             
30050         }
30051         if(el.tagName){ // element
30052             return this.addElement(el);
30053         }
30054         if(typeof el == "object"){ // must be button config?
30055             return this.addButton(el);
30056         }
30057         // and now what?!?!
30058         return false;
30059         
30060     },
30061     
30062     /**
30063      * Add an Xtype element
30064      * @param {Object} xtype Xtype Object
30065      * @return {Object} created Object
30066      */
30067     addxtype : function(e){
30068         return this.add(e);  
30069     },
30070     
30071     /**
30072      * Returns the Element for this toolbar.
30073      * @return {Roo.Element}
30074      */
30075     getEl : function(){
30076         return this.el;  
30077     },
30078     
30079     /**
30080      * Adds a separator
30081      * @return {Roo.Toolbar.Item} The separator item
30082      */
30083     addSeparator : function(){
30084         return this.addItem(new Roo.Toolbar.Separator());
30085     },
30086
30087     /**
30088      * Adds a spacer element
30089      * @return {Roo.Toolbar.Spacer} The spacer item
30090      */
30091     addSpacer : function(){
30092         return this.addItem(new Roo.Toolbar.Spacer());
30093     },
30094
30095     /**
30096      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30097      * @return {Roo.Toolbar.Fill} The fill item
30098      */
30099     addFill : function(){
30100         return this.addItem(new Roo.Toolbar.Fill());
30101     },
30102
30103     /**
30104      * Adds any standard HTML element to the toolbar
30105      * @param {String/HTMLElement/Element} el The element or id of the element to add
30106      * @return {Roo.Toolbar.Item} The element's item
30107      */
30108     addElement : function(el){
30109         return this.addItem(new Roo.Toolbar.Item(el));
30110     },
30111     /**
30112      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30113      * @type Roo.util.MixedCollection  
30114      */
30115     items : false,
30116      
30117     /**
30118      * Adds any Toolbar.Item or subclass
30119      * @param {Roo.Toolbar.Item} item
30120      * @return {Roo.Toolbar.Item} The item
30121      */
30122     addItem : function(item){
30123         var td = this.nextBlock();
30124         item.render(td);
30125         this.items.add(item);
30126         return item;
30127     },
30128     
30129     /**
30130      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30131      * @param {Object/Array} config A button config or array of configs
30132      * @return {Roo.Toolbar.Button/Array}
30133      */
30134     addButton : function(config){
30135         if(config instanceof Array){
30136             var buttons = [];
30137             for(var i = 0, len = config.length; i < len; i++) {
30138                 buttons.push(this.addButton(config[i]));
30139             }
30140             return buttons;
30141         }
30142         var b = config;
30143         if(!(config instanceof Roo.Toolbar.Button)){
30144             b = config.split ?
30145                 new Roo.Toolbar.SplitButton(config) :
30146                 new Roo.Toolbar.Button(config);
30147         }
30148         var td = this.nextBlock();
30149         b.render(td);
30150         this.items.add(b);
30151         return b;
30152     },
30153     
30154     /**
30155      * Adds text to the toolbar
30156      * @param {String} text The text to add
30157      * @return {Roo.Toolbar.Item} The element's item
30158      */
30159     addText : function(text){
30160         return this.addItem(new Roo.Toolbar.TextItem(text));
30161     },
30162     
30163     /**
30164      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30165      * @param {Number} index The index where the item is to be inserted
30166      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30167      * @return {Roo.Toolbar.Button/Item}
30168      */
30169     insertButton : function(index, item){
30170         if(item instanceof Array){
30171             var buttons = [];
30172             for(var i = 0, len = item.length; i < len; i++) {
30173                buttons.push(this.insertButton(index + i, item[i]));
30174             }
30175             return buttons;
30176         }
30177         if (!(item instanceof Roo.Toolbar.Button)){
30178            item = new Roo.Toolbar.Button(item);
30179         }
30180         var td = document.createElement("td");
30181         this.tr.insertBefore(td, this.tr.childNodes[index]);
30182         item.render(td);
30183         this.items.insert(index, item);
30184         return item;
30185     },
30186     
30187     /**
30188      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30189      * @param {Object} config
30190      * @return {Roo.Toolbar.Item} The element's item
30191      */
30192     addDom : function(config, returnEl){
30193         var td = this.nextBlock();
30194         Roo.DomHelper.overwrite(td, config);
30195         var ti = new Roo.Toolbar.Item(td.firstChild);
30196         ti.render(td);
30197         this.items.add(ti);
30198         return ti;
30199     },
30200
30201     /**
30202      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30203      * @type Roo.util.MixedCollection  
30204      */
30205     fields : false,
30206     
30207     /**
30208      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30209      * Note: the field should not have been rendered yet. For a field that has already been
30210      * rendered, use {@link #addElement}.
30211      * @param {Roo.form.Field} field
30212      * @return {Roo.ToolbarItem}
30213      */
30214      
30215       
30216     addField : function(field) {
30217         if (!this.fields) {
30218             var autoId = 0;
30219             this.fields = new Roo.util.MixedCollection(false, function(o){
30220                 return o.id || ("item" + (++autoId));
30221             });
30222
30223         }
30224         
30225         var td = this.nextBlock();
30226         field.render(td);
30227         var ti = new Roo.Toolbar.Item(td.firstChild);
30228         ti.render(td);
30229         this.items.add(ti);
30230         this.fields.add(field);
30231         return ti;
30232     },
30233     /**
30234      * Hide the toolbar
30235      * @method hide
30236      */
30237      
30238       
30239     hide : function()
30240     {
30241         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30242         this.el.child('div').hide();
30243     },
30244     /**
30245      * Show the toolbar
30246      * @method show
30247      */
30248     show : function()
30249     {
30250         this.el.child('div').show();
30251     },
30252       
30253     // private
30254     nextBlock : function(){
30255         var td = document.createElement("td");
30256         this.tr.appendChild(td);
30257         return td;
30258     },
30259
30260     // private
30261     destroy : function(){
30262         if(this.items){ // rendered?
30263             Roo.destroy.apply(Roo, this.items.items);
30264         }
30265         if(this.fields){ // rendered?
30266             Roo.destroy.apply(Roo, this.fields.items);
30267         }
30268         Roo.Element.uncache(this.el, this.tr);
30269     }
30270 };
30271
30272 /**
30273  * @class Roo.Toolbar.Item
30274  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30275  * @constructor
30276  * Creates a new Item
30277  * @param {HTMLElement} el 
30278  */
30279 Roo.Toolbar.Item = function(el){
30280     var cfg = {};
30281     if (typeof (el.xtype) != 'undefined') {
30282         cfg = el;
30283         el = cfg.el;
30284     }
30285     
30286     this.el = Roo.getDom(el);
30287     this.id = Roo.id(this.el);
30288     this.hidden = false;
30289     
30290     this.addEvents({
30291          /**
30292              * @event render
30293              * Fires when the button is rendered
30294              * @param {Button} this
30295              */
30296         'render': true
30297     });
30298     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30299 };
30300 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30301 //Roo.Toolbar.Item.prototype = {
30302     
30303     /**
30304      * Get this item's HTML Element
30305      * @return {HTMLElement}
30306      */
30307     getEl : function(){
30308        return this.el;  
30309     },
30310
30311     // private
30312     render : function(td){
30313         
30314          this.td = td;
30315         td.appendChild(this.el);
30316         
30317         this.fireEvent('render', this);
30318     },
30319     
30320     /**
30321      * Removes and destroys this item.
30322      */
30323     destroy : function(){
30324         this.td.parentNode.removeChild(this.td);
30325     },
30326     
30327     /**
30328      * Shows this item.
30329      */
30330     show: function(){
30331         this.hidden = false;
30332         this.td.style.display = "";
30333     },
30334     
30335     /**
30336      * Hides this item.
30337      */
30338     hide: function(){
30339         this.hidden = true;
30340         this.td.style.display = "none";
30341     },
30342     
30343     /**
30344      * Convenience function for boolean show/hide.
30345      * @param {Boolean} visible true to show/false to hide
30346      */
30347     setVisible: function(visible){
30348         if(visible) {
30349             this.show();
30350         }else{
30351             this.hide();
30352         }
30353     },
30354     
30355     /**
30356      * Try to focus this item.
30357      */
30358     focus : function(){
30359         Roo.fly(this.el).focus();
30360     },
30361     
30362     /**
30363      * Disables this item.
30364      */
30365     disable : function(){
30366         Roo.fly(this.td).addClass("x-item-disabled");
30367         this.disabled = true;
30368         this.el.disabled = true;
30369     },
30370     
30371     /**
30372      * Enables this item.
30373      */
30374     enable : function(){
30375         Roo.fly(this.td).removeClass("x-item-disabled");
30376         this.disabled = false;
30377         this.el.disabled = false;
30378     }
30379 });
30380
30381
30382 /**
30383  * @class Roo.Toolbar.Separator
30384  * @extends Roo.Toolbar.Item
30385  * A simple toolbar separator class
30386  * @constructor
30387  * Creates a new Separator
30388  */
30389 Roo.Toolbar.Separator = function(cfg){
30390     
30391     var s = document.createElement("span");
30392     s.className = "ytb-sep";
30393     if (cfg) {
30394         cfg.el = s;
30395     }
30396     
30397     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30398 };
30399 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30400     enable:Roo.emptyFn,
30401     disable:Roo.emptyFn,
30402     focus:Roo.emptyFn
30403 });
30404
30405 /**
30406  * @class Roo.Toolbar.Spacer
30407  * @extends Roo.Toolbar.Item
30408  * A simple element that adds extra horizontal space to a toolbar.
30409  * @constructor
30410  * Creates a new Spacer
30411  */
30412 Roo.Toolbar.Spacer = function(cfg){
30413     var s = document.createElement("div");
30414     s.className = "ytb-spacer";
30415     if (cfg) {
30416         cfg.el = s;
30417     }
30418     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30419 };
30420 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30421     enable:Roo.emptyFn,
30422     disable:Roo.emptyFn,
30423     focus:Roo.emptyFn
30424 });
30425
30426 /**
30427  * @class Roo.Toolbar.Fill
30428  * @extends Roo.Toolbar.Spacer
30429  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30430  * @constructor
30431  * Creates a new Spacer
30432  */
30433 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30434     // private
30435     render : function(td){
30436         td.style.width = '100%';
30437         Roo.Toolbar.Fill.superclass.render.call(this, td);
30438     }
30439 });
30440
30441 /**
30442  * @class Roo.Toolbar.TextItem
30443  * @extends Roo.Toolbar.Item
30444  * A simple class that renders text directly into a toolbar.
30445  * @constructor
30446  * Creates a new TextItem
30447  * @param {String} text
30448  */
30449 Roo.Toolbar.TextItem = function(cfg){
30450     var  text = cfg || "";
30451     if (typeof(cfg) == 'object') {
30452         text = cfg.text || "";
30453     }  else {
30454         cfg = null;
30455     }
30456     var s = document.createElement("span");
30457     s.className = "ytb-text";
30458     s.innerHTML = text;
30459     if (cfg) {
30460         cfg.el  = s;
30461     }
30462     
30463     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30464 };
30465 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30466     
30467      
30468     enable:Roo.emptyFn,
30469     disable:Roo.emptyFn,
30470     focus:Roo.emptyFn
30471 });
30472
30473 /**
30474  * @class Roo.Toolbar.Button
30475  * @extends Roo.Button
30476  * A button that renders into a toolbar.
30477  * @constructor
30478  * Creates a new Button
30479  * @param {Object} config A standard {@link Roo.Button} config object
30480  */
30481 Roo.Toolbar.Button = function(config){
30482     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30483 };
30484 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30485     render : function(td){
30486         this.td = td;
30487         Roo.Toolbar.Button.superclass.render.call(this, td);
30488     },
30489     
30490     /**
30491      * Removes and destroys this button
30492      */
30493     destroy : function(){
30494         Roo.Toolbar.Button.superclass.destroy.call(this);
30495         this.td.parentNode.removeChild(this.td);
30496     },
30497     
30498     /**
30499      * Shows this button
30500      */
30501     show: function(){
30502         this.hidden = false;
30503         this.td.style.display = "";
30504     },
30505     
30506     /**
30507      * Hides this button
30508      */
30509     hide: function(){
30510         this.hidden = true;
30511         this.td.style.display = "none";
30512     },
30513
30514     /**
30515      * Disables this item
30516      */
30517     disable : function(){
30518         Roo.fly(this.td).addClass("x-item-disabled");
30519         this.disabled = true;
30520     },
30521
30522     /**
30523      * Enables this item
30524      */
30525     enable : function(){
30526         Roo.fly(this.td).removeClass("x-item-disabled");
30527         this.disabled = false;
30528     }
30529 });
30530 // backwards compat
30531 Roo.ToolbarButton = Roo.Toolbar.Button;
30532
30533 /**
30534  * @class Roo.Toolbar.SplitButton
30535  * @extends Roo.SplitButton
30536  * A menu button that renders into a toolbar.
30537  * @constructor
30538  * Creates a new SplitButton
30539  * @param {Object} config A standard {@link Roo.SplitButton} config object
30540  */
30541 Roo.Toolbar.SplitButton = function(config){
30542     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30543 };
30544 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30545     render : function(td){
30546         this.td = td;
30547         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30548     },
30549     
30550     /**
30551      * Removes and destroys this button
30552      */
30553     destroy : function(){
30554         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30555         this.td.parentNode.removeChild(this.td);
30556     },
30557     
30558     /**
30559      * Shows this button
30560      */
30561     show: function(){
30562         this.hidden = false;
30563         this.td.style.display = "";
30564     },
30565     
30566     /**
30567      * Hides this button
30568      */
30569     hide: function(){
30570         this.hidden = true;
30571         this.td.style.display = "none";
30572     }
30573 });
30574
30575 // backwards compat
30576 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30577  * Based on:
30578  * Ext JS Library 1.1.1
30579  * Copyright(c) 2006-2007, Ext JS, LLC.
30580  *
30581  * Originally Released Under LGPL - original licence link has changed is not relivant.
30582  *
30583  * Fork - LGPL
30584  * <script type="text/javascript">
30585  */
30586  
30587 /**
30588  * @class Roo.PagingToolbar
30589  * @extends Roo.Toolbar
30590  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30591  * @constructor
30592  * Create a new PagingToolbar
30593  * @param {Object} config The config object
30594  */
30595 Roo.PagingToolbar = function(el, ds, config)
30596 {
30597     // old args format still supported... - xtype is prefered..
30598     if (typeof(el) == 'object' && el.xtype) {
30599         // created from xtype...
30600         config = el;
30601         ds = el.dataSource;
30602         el = config.container;
30603     }
30604     var items = [];
30605     if (config.items) {
30606         items = config.items;
30607         config.items = [];
30608     }
30609     
30610     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30611     this.ds = ds;
30612     this.cursor = 0;
30613     this.renderButtons(this.el);
30614     this.bind(ds);
30615     
30616     // supprot items array.
30617    
30618     Roo.each(items, function(e) {
30619         this.add(Roo.factory(e));
30620     },this);
30621     
30622 };
30623
30624 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30625     /**
30626      * @cfg {Roo.data.Store} dataSource
30627      * The underlying data store providing the paged data
30628      */
30629     /**
30630      * @cfg {String/HTMLElement/Element} container
30631      * container The id or element that will contain the toolbar
30632      */
30633     /**
30634      * @cfg {Boolean} displayInfo
30635      * True to display the displayMsg (defaults to false)
30636      */
30637     /**
30638      * @cfg {Number} pageSize
30639      * The number of records to display per page (defaults to 20)
30640      */
30641     pageSize: 20,
30642     /**
30643      * @cfg {String} displayMsg
30644      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30645      */
30646     displayMsg : 'Displaying {0} - {1} of {2}',
30647     /**
30648      * @cfg {String} emptyMsg
30649      * The message to display when no records are found (defaults to "No data to display")
30650      */
30651     emptyMsg : 'No data to display',
30652     /**
30653      * Customizable piece of the default paging text (defaults to "Page")
30654      * @type String
30655      */
30656     beforePageText : "Page",
30657     /**
30658      * Customizable piece of the default paging text (defaults to "of %0")
30659      * @type String
30660      */
30661     afterPageText : "of {0}",
30662     /**
30663      * Customizable piece of the default paging text (defaults to "First Page")
30664      * @type String
30665      */
30666     firstText : "First Page",
30667     /**
30668      * Customizable piece of the default paging text (defaults to "Previous Page")
30669      * @type String
30670      */
30671     prevText : "Previous Page",
30672     /**
30673      * Customizable piece of the default paging text (defaults to "Next Page")
30674      * @type String
30675      */
30676     nextText : "Next Page",
30677     /**
30678      * Customizable piece of the default paging text (defaults to "Last Page")
30679      * @type String
30680      */
30681     lastText : "Last Page",
30682     /**
30683      * Customizable piece of the default paging text (defaults to "Refresh")
30684      * @type String
30685      */
30686     refreshText : "Refresh",
30687
30688     // private
30689     renderButtons : function(el){
30690         Roo.PagingToolbar.superclass.render.call(this, el);
30691         this.first = this.addButton({
30692             tooltip: this.firstText,
30693             cls: "x-btn-icon x-grid-page-first",
30694             disabled: true,
30695             handler: this.onClick.createDelegate(this, ["first"])
30696         });
30697         this.prev = this.addButton({
30698             tooltip: this.prevText,
30699             cls: "x-btn-icon x-grid-page-prev",
30700             disabled: true,
30701             handler: this.onClick.createDelegate(this, ["prev"])
30702         });
30703         //this.addSeparator();
30704         this.add(this.beforePageText);
30705         this.field = Roo.get(this.addDom({
30706            tag: "input",
30707            type: "text",
30708            size: "3",
30709            value: "1",
30710            cls: "x-grid-page-number"
30711         }).el);
30712         this.field.on("keydown", this.onPagingKeydown, this);
30713         this.field.on("focus", function(){this.dom.select();});
30714         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30715         this.field.setHeight(18);
30716         //this.addSeparator();
30717         this.next = this.addButton({
30718             tooltip: this.nextText,
30719             cls: "x-btn-icon x-grid-page-next",
30720             disabled: true,
30721             handler: this.onClick.createDelegate(this, ["next"])
30722         });
30723         this.last = this.addButton({
30724             tooltip: this.lastText,
30725             cls: "x-btn-icon x-grid-page-last",
30726             disabled: true,
30727             handler: this.onClick.createDelegate(this, ["last"])
30728         });
30729         //this.addSeparator();
30730         this.loading = this.addButton({
30731             tooltip: this.refreshText,
30732             cls: "x-btn-icon x-grid-loading",
30733             handler: this.onClick.createDelegate(this, ["refresh"])
30734         });
30735
30736         if(this.displayInfo){
30737             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30738         }
30739     },
30740
30741     // private
30742     updateInfo : function(){
30743         if(this.displayEl){
30744             var count = this.ds.getCount();
30745             var msg = count == 0 ?
30746                 this.emptyMsg :
30747                 String.format(
30748                     this.displayMsg,
30749                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30750                 );
30751             this.displayEl.update(msg);
30752         }
30753     },
30754
30755     // private
30756     onLoad : function(ds, r, o){
30757        this.cursor = o.params ? o.params.start : 0;
30758        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30759
30760        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30761        this.field.dom.value = ap;
30762        this.first.setDisabled(ap == 1);
30763        this.prev.setDisabled(ap == 1);
30764        this.next.setDisabled(ap == ps);
30765        this.last.setDisabled(ap == ps);
30766        this.loading.enable();
30767        this.updateInfo();
30768     },
30769
30770     // private
30771     getPageData : function(){
30772         var total = this.ds.getTotalCount();
30773         return {
30774             total : total,
30775             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30776             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30777         };
30778     },
30779
30780     // private
30781     onLoadError : function(){
30782         this.loading.enable();
30783     },
30784
30785     // private
30786     onPagingKeydown : function(e){
30787         var k = e.getKey();
30788         var d = this.getPageData();
30789         if(k == e.RETURN){
30790             var v = this.field.dom.value, pageNum;
30791             if(!v || isNaN(pageNum = parseInt(v, 10))){
30792                 this.field.dom.value = d.activePage;
30793                 return;
30794             }
30795             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30796             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30797             e.stopEvent();
30798         }
30799         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))
30800         {
30801           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30802           this.field.dom.value = pageNum;
30803           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30804           e.stopEvent();
30805         }
30806         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30807         {
30808           var v = this.field.dom.value, pageNum; 
30809           var increment = (e.shiftKey) ? 10 : 1;
30810           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30811             increment *= -1;
30812           }
30813           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30814             this.field.dom.value = d.activePage;
30815             return;
30816           }
30817           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30818           {
30819             this.field.dom.value = parseInt(v, 10) + increment;
30820             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30821             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30822           }
30823           e.stopEvent();
30824         }
30825     },
30826
30827     // private
30828     beforeLoad : function(){
30829         if(this.loading){
30830             this.loading.disable();
30831         }
30832     },
30833
30834     // private
30835     onClick : function(which){
30836         var ds = this.ds;
30837         switch(which){
30838             case "first":
30839                 ds.load({params:{start: 0, limit: this.pageSize}});
30840             break;
30841             case "prev":
30842                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30843             break;
30844             case "next":
30845                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30846             break;
30847             case "last":
30848                 var total = ds.getTotalCount();
30849                 var extra = total % this.pageSize;
30850                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30851                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30852             break;
30853             case "refresh":
30854                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30855             break;
30856         }
30857     },
30858
30859     /**
30860      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30861      * @param {Roo.data.Store} store The data store to unbind
30862      */
30863     unbind : function(ds){
30864         ds.un("beforeload", this.beforeLoad, this);
30865         ds.un("load", this.onLoad, this);
30866         ds.un("loadexception", this.onLoadError, this);
30867         ds.un("remove", this.updateInfo, this);
30868         ds.un("add", this.updateInfo, this);
30869         this.ds = undefined;
30870     },
30871
30872     /**
30873      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30874      * @param {Roo.data.Store} store The data store to bind
30875      */
30876     bind : function(ds){
30877         ds.on("beforeload", this.beforeLoad, this);
30878         ds.on("load", this.onLoad, this);
30879         ds.on("loadexception", this.onLoadError, this);
30880         ds.on("remove", this.updateInfo, this);
30881         ds.on("add", this.updateInfo, this);
30882         this.ds = ds;
30883     }
30884 });/*
30885  * Based on:
30886  * Ext JS Library 1.1.1
30887  * Copyright(c) 2006-2007, Ext JS, LLC.
30888  *
30889  * Originally Released Under LGPL - original licence link has changed is not relivant.
30890  *
30891  * Fork - LGPL
30892  * <script type="text/javascript">
30893  */
30894
30895 /**
30896  * @class Roo.Resizable
30897  * @extends Roo.util.Observable
30898  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30899  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30900  * 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
30901  * the element will be wrapped for you automatically.</p>
30902  * <p>Here is the list of valid resize handles:</p>
30903  * <pre>
30904 Value   Description
30905 ------  -------------------
30906  'n'     north
30907  's'     south
30908  'e'     east
30909  'w'     west
30910  'nw'    northwest
30911  'sw'    southwest
30912  'se'    southeast
30913  'ne'    northeast
30914  'hd'    horizontal drag
30915  'all'   all
30916 </pre>
30917  * <p>Here's an example showing the creation of a typical Resizable:</p>
30918  * <pre><code>
30919 var resizer = new Roo.Resizable("element-id", {
30920     handles: 'all',
30921     minWidth: 200,
30922     minHeight: 100,
30923     maxWidth: 500,
30924     maxHeight: 400,
30925     pinned: true
30926 });
30927 resizer.on("resize", myHandler);
30928 </code></pre>
30929  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30930  * resizer.east.setDisplayed(false);</p>
30931  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30932  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30933  * resize operation's new size (defaults to [0, 0])
30934  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30935  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30936  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30937  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30938  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30939  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30940  * @cfg {Number} width The width of the element in pixels (defaults to null)
30941  * @cfg {Number} height The height of the element in pixels (defaults to null)
30942  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30943  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30944  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30945  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30946  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30947  * in favor of the handles config option (defaults to false)
30948  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30949  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30950  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30951  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30952  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30953  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30954  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30955  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30956  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30957  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30958  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30959  * @constructor
30960  * Create a new resizable component
30961  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30962  * @param {Object} config configuration options
30963   */
30964 Roo.Resizable = function(el, config)
30965 {
30966     this.el = Roo.get(el);
30967
30968     if(config && config.wrap){
30969         config.resizeChild = this.el;
30970         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30971         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30972         this.el.setStyle("overflow", "hidden");
30973         this.el.setPositioning(config.resizeChild.getPositioning());
30974         config.resizeChild.clearPositioning();
30975         if(!config.width || !config.height){
30976             var csize = config.resizeChild.getSize();
30977             this.el.setSize(csize.width, csize.height);
30978         }
30979         if(config.pinned && !config.adjustments){
30980             config.adjustments = "auto";
30981         }
30982     }
30983
30984     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30985     this.proxy.unselectable();
30986     this.proxy.enableDisplayMode('block');
30987
30988     Roo.apply(this, config);
30989
30990     if(this.pinned){
30991         this.disableTrackOver = true;
30992         this.el.addClass("x-resizable-pinned");
30993     }
30994     // if the element isn't positioned, make it relative
30995     var position = this.el.getStyle("position");
30996     if(position != "absolute" && position != "fixed"){
30997         this.el.setStyle("position", "relative");
30998     }
30999     if(!this.handles){ // no handles passed, must be legacy style
31000         this.handles = 's,e,se';
31001         if(this.multiDirectional){
31002             this.handles += ',n,w';
31003         }
31004     }
31005     if(this.handles == "all"){
31006         this.handles = "n s e w ne nw se sw";
31007     }
31008     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31009     var ps = Roo.Resizable.positions;
31010     for(var i = 0, len = hs.length; i < len; i++){
31011         if(hs[i] && ps[hs[i]]){
31012             var pos = ps[hs[i]];
31013             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31014         }
31015     }
31016     // legacy
31017     this.corner = this.southeast;
31018     
31019     // updateBox = the box can move..
31020     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31021         this.updateBox = true;
31022     }
31023
31024     this.activeHandle = null;
31025
31026     if(this.resizeChild){
31027         if(typeof this.resizeChild == "boolean"){
31028             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31029         }else{
31030             this.resizeChild = Roo.get(this.resizeChild, true);
31031         }
31032     }
31033     
31034     if(this.adjustments == "auto"){
31035         var rc = this.resizeChild;
31036         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31037         if(rc && (hw || hn)){
31038             rc.position("relative");
31039             rc.setLeft(hw ? hw.el.getWidth() : 0);
31040             rc.setTop(hn ? hn.el.getHeight() : 0);
31041         }
31042         this.adjustments = [
31043             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31044             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31045         ];
31046     }
31047
31048     if(this.draggable){
31049         this.dd = this.dynamic ?
31050             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31051         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31052     }
31053
31054     // public events
31055     this.addEvents({
31056         /**
31057          * @event beforeresize
31058          * Fired before resize is allowed. Set enabled to false to cancel resize.
31059          * @param {Roo.Resizable} this
31060          * @param {Roo.EventObject} e The mousedown event
31061          */
31062         "beforeresize" : true,
31063         /**
31064          * @event resizing
31065          * Fired a resizing.
31066          * @param {Roo.Resizable} this
31067          * @param {Number} x The new x position
31068          * @param {Number} y The new y position
31069          * @param {Number} w The new w width
31070          * @param {Number} h The new h hight
31071          * @param {Roo.EventObject} e The mouseup event
31072          */
31073         "resizing" : true,
31074         /**
31075          * @event resize
31076          * Fired after a resize.
31077          * @param {Roo.Resizable} this
31078          * @param {Number} width The new width
31079          * @param {Number} height The new height
31080          * @param {Roo.EventObject} e The mouseup event
31081          */
31082         "resize" : true
31083     });
31084
31085     if(this.width !== null && this.height !== null){
31086         this.resizeTo(this.width, this.height);
31087     }else{
31088         this.updateChildSize();
31089     }
31090     if(Roo.isIE){
31091         this.el.dom.style.zoom = 1;
31092     }
31093     Roo.Resizable.superclass.constructor.call(this);
31094 };
31095
31096 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31097         resizeChild : false,
31098         adjustments : [0, 0],
31099         minWidth : 5,
31100         minHeight : 5,
31101         maxWidth : 10000,
31102         maxHeight : 10000,
31103         enabled : true,
31104         animate : false,
31105         duration : .35,
31106         dynamic : false,
31107         handles : false,
31108         multiDirectional : false,
31109         disableTrackOver : false,
31110         easing : 'easeOutStrong',
31111         widthIncrement : 0,
31112         heightIncrement : 0,
31113         pinned : false,
31114         width : null,
31115         height : null,
31116         preserveRatio : false,
31117         transparent: false,
31118         minX: 0,
31119         minY: 0,
31120         draggable: false,
31121
31122         /**
31123          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31124          */
31125         constrainTo: undefined,
31126         /**
31127          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31128          */
31129         resizeRegion: undefined,
31130
31131
31132     /**
31133      * Perform a manual resize
31134      * @param {Number} width
31135      * @param {Number} height
31136      */
31137     resizeTo : function(width, height){
31138         this.el.setSize(width, height);
31139         this.updateChildSize();
31140         this.fireEvent("resize", this, width, height, null);
31141     },
31142
31143     // private
31144     startSizing : function(e, handle){
31145         this.fireEvent("beforeresize", this, e);
31146         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31147
31148             if(!this.overlay){
31149                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31150                 this.overlay.unselectable();
31151                 this.overlay.enableDisplayMode("block");
31152                 this.overlay.on("mousemove", this.onMouseMove, this);
31153                 this.overlay.on("mouseup", this.onMouseUp, this);
31154             }
31155             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31156
31157             this.resizing = true;
31158             this.startBox = this.el.getBox();
31159             this.startPoint = e.getXY();
31160             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31161                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31162
31163             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31164             this.overlay.show();
31165
31166             if(this.constrainTo) {
31167                 var ct = Roo.get(this.constrainTo);
31168                 this.resizeRegion = ct.getRegion().adjust(
31169                     ct.getFrameWidth('t'),
31170                     ct.getFrameWidth('l'),
31171                     -ct.getFrameWidth('b'),
31172                     -ct.getFrameWidth('r')
31173                 );
31174             }
31175
31176             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31177             this.proxy.show();
31178             this.proxy.setBox(this.startBox);
31179             if(!this.dynamic){
31180                 this.proxy.setStyle('visibility', 'visible');
31181             }
31182         }
31183     },
31184
31185     // private
31186     onMouseDown : function(handle, e){
31187         if(this.enabled){
31188             e.stopEvent();
31189             this.activeHandle = handle;
31190             this.startSizing(e, handle);
31191         }
31192     },
31193
31194     // private
31195     onMouseUp : function(e){
31196         var size = this.resizeElement();
31197         this.resizing = false;
31198         this.handleOut();
31199         this.overlay.hide();
31200         this.proxy.hide();
31201         this.fireEvent("resize", this, size.width, size.height, e);
31202     },
31203
31204     // private
31205     updateChildSize : function(){
31206         
31207         if(this.resizeChild){
31208             var el = this.el;
31209             var child = this.resizeChild;
31210             var adj = this.adjustments;
31211             if(el.dom.offsetWidth){
31212                 var b = el.getSize(true);
31213                 child.setSize(b.width+adj[0], b.height+adj[1]);
31214             }
31215             // Second call here for IE
31216             // The first call enables instant resizing and
31217             // the second call corrects scroll bars if they
31218             // exist
31219             if(Roo.isIE){
31220                 setTimeout(function(){
31221                     if(el.dom.offsetWidth){
31222                         var b = el.getSize(true);
31223                         child.setSize(b.width+adj[0], b.height+adj[1]);
31224                     }
31225                 }, 10);
31226             }
31227         }
31228     },
31229
31230     // private
31231     snap : function(value, inc, min){
31232         if(!inc || !value) {
31233             return value;
31234         }
31235         var newValue = value;
31236         var m = value % inc;
31237         if(m > 0){
31238             if(m > (inc/2)){
31239                 newValue = value + (inc-m);
31240             }else{
31241                 newValue = value - m;
31242             }
31243         }
31244         return Math.max(min, newValue);
31245     },
31246
31247     // private
31248     resizeElement : function(){
31249         var box = this.proxy.getBox();
31250         if(this.updateBox){
31251             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31252         }else{
31253             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31254         }
31255         this.updateChildSize();
31256         if(!this.dynamic){
31257             this.proxy.hide();
31258         }
31259         return box;
31260     },
31261
31262     // private
31263     constrain : function(v, diff, m, mx){
31264         if(v - diff < m){
31265             diff = v - m;
31266         }else if(v - diff > mx){
31267             diff = mx - v;
31268         }
31269         return diff;
31270     },
31271
31272     // private
31273     onMouseMove : function(e){
31274         
31275         if(this.enabled){
31276             try{// try catch so if something goes wrong the user doesn't get hung
31277
31278             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31279                 return;
31280             }
31281
31282             //var curXY = this.startPoint;
31283             var curSize = this.curSize || this.startBox;
31284             var x = this.startBox.x, y = this.startBox.y;
31285             var ox = x, oy = y;
31286             var w = curSize.width, h = curSize.height;
31287             var ow = w, oh = h;
31288             var mw = this.minWidth, mh = this.minHeight;
31289             var mxw = this.maxWidth, mxh = this.maxHeight;
31290             var wi = this.widthIncrement;
31291             var hi = this.heightIncrement;
31292
31293             var eventXY = e.getXY();
31294             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31295             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31296
31297             var pos = this.activeHandle.position;
31298
31299             switch(pos){
31300                 case "east":
31301                     w += diffX;
31302                     w = Math.min(Math.max(mw, w), mxw);
31303                     break;
31304              
31305                 case "south":
31306                     h += diffY;
31307                     h = Math.min(Math.max(mh, h), mxh);
31308                     break;
31309                 case "southeast":
31310                     w += diffX;
31311                     h += diffY;
31312                     w = Math.min(Math.max(mw, w), mxw);
31313                     h = Math.min(Math.max(mh, h), mxh);
31314                     break;
31315                 case "north":
31316                     diffY = this.constrain(h, diffY, mh, mxh);
31317                     y += diffY;
31318                     h -= diffY;
31319                     break;
31320                 case "hdrag":
31321                     
31322                     if (wi) {
31323                         var adiffX = Math.abs(diffX);
31324                         var sub = (adiffX % wi); // how much 
31325                         if (sub > (wi/2)) { // far enough to snap
31326                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31327                         } else {
31328                             // remove difference.. 
31329                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31330                         }
31331                     }
31332                     x += diffX;
31333                     x = Math.max(this.minX, x);
31334                     break;
31335                 case "west":
31336                     diffX = this.constrain(w, diffX, mw, mxw);
31337                     x += diffX;
31338                     w -= diffX;
31339                     break;
31340                 case "northeast":
31341                     w += diffX;
31342                     w = Math.min(Math.max(mw, w), mxw);
31343                     diffY = this.constrain(h, diffY, mh, mxh);
31344                     y += diffY;
31345                     h -= diffY;
31346                     break;
31347                 case "northwest":
31348                     diffX = this.constrain(w, diffX, mw, mxw);
31349                     diffY = this.constrain(h, diffY, mh, mxh);
31350                     y += diffY;
31351                     h -= diffY;
31352                     x += diffX;
31353                     w -= diffX;
31354                     break;
31355                case "southwest":
31356                     diffX = this.constrain(w, diffX, mw, mxw);
31357                     h += diffY;
31358                     h = Math.min(Math.max(mh, h), mxh);
31359                     x += diffX;
31360                     w -= diffX;
31361                     break;
31362             }
31363
31364             var sw = this.snap(w, wi, mw);
31365             var sh = this.snap(h, hi, mh);
31366             if(sw != w || sh != h){
31367                 switch(pos){
31368                     case "northeast":
31369                         y -= sh - h;
31370                     break;
31371                     case "north":
31372                         y -= sh - h;
31373                         break;
31374                     case "southwest":
31375                         x -= sw - w;
31376                     break;
31377                     case "west":
31378                         x -= sw - w;
31379                         break;
31380                     case "northwest":
31381                         x -= sw - w;
31382                         y -= sh - h;
31383                     break;
31384                 }
31385                 w = sw;
31386                 h = sh;
31387             }
31388
31389             if(this.preserveRatio){
31390                 switch(pos){
31391                     case "southeast":
31392                     case "east":
31393                         h = oh * (w/ow);
31394                         h = Math.min(Math.max(mh, h), mxh);
31395                         w = ow * (h/oh);
31396                        break;
31397                     case "south":
31398                         w = ow * (h/oh);
31399                         w = Math.min(Math.max(mw, w), mxw);
31400                         h = oh * (w/ow);
31401                         break;
31402                     case "northeast":
31403                         w = ow * (h/oh);
31404                         w = Math.min(Math.max(mw, w), mxw);
31405                         h = oh * (w/ow);
31406                     break;
31407                     case "north":
31408                         var tw = w;
31409                         w = ow * (h/oh);
31410                         w = Math.min(Math.max(mw, w), mxw);
31411                         h = oh * (w/ow);
31412                         x += (tw - w) / 2;
31413                         break;
31414                     case "southwest":
31415                         h = oh * (w/ow);
31416                         h = Math.min(Math.max(mh, h), mxh);
31417                         var tw = w;
31418                         w = ow * (h/oh);
31419                         x += tw - w;
31420                         break;
31421                     case "west":
31422                         var th = h;
31423                         h = oh * (w/ow);
31424                         h = Math.min(Math.max(mh, h), mxh);
31425                         y += (th - h) / 2;
31426                         var tw = w;
31427                         w = ow * (h/oh);
31428                         x += tw - w;
31429                        break;
31430                     case "northwest":
31431                         var tw = w;
31432                         var th = h;
31433                         h = oh * (w/ow);
31434                         h = Math.min(Math.max(mh, h), mxh);
31435                         w = ow * (h/oh);
31436                         y += th - h;
31437                         x += tw - w;
31438                        break;
31439
31440                 }
31441             }
31442             if (pos == 'hdrag') {
31443                 w = ow;
31444             }
31445             this.proxy.setBounds(x, y, w, h);
31446             if(this.dynamic){
31447                 this.resizeElement();
31448             }
31449             }catch(e){}
31450         }
31451         this.fireEvent("resizing", this, x, y, w, h, e);
31452     },
31453
31454     // private
31455     handleOver : function(){
31456         if(this.enabled){
31457             this.el.addClass("x-resizable-over");
31458         }
31459     },
31460
31461     // private
31462     handleOut : function(){
31463         if(!this.resizing){
31464             this.el.removeClass("x-resizable-over");
31465         }
31466     },
31467
31468     /**
31469      * Returns the element this component is bound to.
31470      * @return {Roo.Element}
31471      */
31472     getEl : function(){
31473         return this.el;
31474     },
31475
31476     /**
31477      * Returns the resizeChild element (or null).
31478      * @return {Roo.Element}
31479      */
31480     getResizeChild : function(){
31481         return this.resizeChild;
31482     },
31483     groupHandler : function()
31484     {
31485         
31486     },
31487     /**
31488      * Destroys this resizable. If the element was wrapped and
31489      * removeEl is not true then the element remains.
31490      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31491      */
31492     destroy : function(removeEl){
31493         this.proxy.remove();
31494         if(this.overlay){
31495             this.overlay.removeAllListeners();
31496             this.overlay.remove();
31497         }
31498         var ps = Roo.Resizable.positions;
31499         for(var k in ps){
31500             if(typeof ps[k] != "function" && this[ps[k]]){
31501                 var h = this[ps[k]];
31502                 h.el.removeAllListeners();
31503                 h.el.remove();
31504             }
31505         }
31506         if(removeEl){
31507             this.el.update("");
31508             this.el.remove();
31509         }
31510     }
31511 });
31512
31513 // private
31514 // hash to map config positions to true positions
31515 Roo.Resizable.positions = {
31516     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31517     hd: "hdrag"
31518 };
31519
31520 // private
31521 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31522     if(!this.tpl){
31523         // only initialize the template if resizable is used
31524         var tpl = Roo.DomHelper.createTemplate(
31525             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31526         );
31527         tpl.compile();
31528         Roo.Resizable.Handle.prototype.tpl = tpl;
31529     }
31530     this.position = pos;
31531     this.rz = rz;
31532     // show north drag fro topdra
31533     var handlepos = pos == 'hdrag' ? 'north' : pos;
31534     
31535     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31536     if (pos == 'hdrag') {
31537         this.el.setStyle('cursor', 'pointer');
31538     }
31539     this.el.unselectable();
31540     if(transparent){
31541         this.el.setOpacity(0);
31542     }
31543     this.el.on("mousedown", this.onMouseDown, this);
31544     if(!disableTrackOver){
31545         this.el.on("mouseover", this.onMouseOver, this);
31546         this.el.on("mouseout", this.onMouseOut, this);
31547     }
31548 };
31549
31550 // private
31551 Roo.Resizable.Handle.prototype = {
31552     afterResize : function(rz){
31553         Roo.log('after?');
31554         // do nothing
31555     },
31556     // private
31557     onMouseDown : function(e){
31558         this.rz.onMouseDown(this, e);
31559     },
31560     // private
31561     onMouseOver : function(e){
31562         this.rz.handleOver(this, e);
31563     },
31564     // private
31565     onMouseOut : function(e){
31566         this.rz.handleOut(this, e);
31567     }
31568 };/*
31569  * Based on:
31570  * Ext JS Library 1.1.1
31571  * Copyright(c) 2006-2007, Ext JS, LLC.
31572  *
31573  * Originally Released Under LGPL - original licence link has changed is not relivant.
31574  *
31575  * Fork - LGPL
31576  * <script type="text/javascript">
31577  */
31578
31579 /**
31580  * @class Roo.Editor
31581  * @extends Roo.Component
31582  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31583  * @constructor
31584  * Create a new Editor
31585  * @param {Roo.form.Field} field The Field object (or descendant)
31586  * @param {Object} config The config object
31587  */
31588 Roo.Editor = function(field, config){
31589     Roo.Editor.superclass.constructor.call(this, config);
31590     this.field = field;
31591     this.addEvents({
31592         /**
31593              * @event beforestartedit
31594              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31595              * false from the handler of this event.
31596              * @param {Editor} this
31597              * @param {Roo.Element} boundEl The underlying element bound to this editor
31598              * @param {Mixed} value The field value being set
31599              */
31600         "beforestartedit" : true,
31601         /**
31602              * @event startedit
31603              * Fires when this editor is displayed
31604              * @param {Roo.Element} boundEl The underlying element bound to this editor
31605              * @param {Mixed} value The starting field value
31606              */
31607         "startedit" : true,
31608         /**
31609              * @event beforecomplete
31610              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31611              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31612              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31613              * event will not fire since no edit actually occurred.
31614              * @param {Editor} this
31615              * @param {Mixed} value The current field value
31616              * @param {Mixed} startValue The original field value
31617              */
31618         "beforecomplete" : true,
31619         /**
31620              * @event complete
31621              * Fires after editing is complete and any changed value has been written to the underlying field.
31622              * @param {Editor} this
31623              * @param {Mixed} value The current field value
31624              * @param {Mixed} startValue The original field value
31625              */
31626         "complete" : true,
31627         /**
31628          * @event specialkey
31629          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31630          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31631          * @param {Roo.form.Field} this
31632          * @param {Roo.EventObject} e The event object
31633          */
31634         "specialkey" : true
31635     });
31636 };
31637
31638 Roo.extend(Roo.Editor, Roo.Component, {
31639     /**
31640      * @cfg {Boolean/String} autosize
31641      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31642      * or "height" to adopt the height only (defaults to false)
31643      */
31644     /**
31645      * @cfg {Boolean} revertInvalid
31646      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31647      * validation fails (defaults to true)
31648      */
31649     /**
31650      * @cfg {Boolean} ignoreNoChange
31651      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31652      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31653      * will never be ignored.
31654      */
31655     /**
31656      * @cfg {Boolean} hideEl
31657      * False to keep the bound element visible while the editor is displayed (defaults to true)
31658      */
31659     /**
31660      * @cfg {Mixed} value
31661      * The data value of the underlying field (defaults to "")
31662      */
31663     value : "",
31664     /**
31665      * @cfg {String} alignment
31666      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31667      */
31668     alignment: "c-c?",
31669     /**
31670      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31671      * for bottom-right shadow (defaults to "frame")
31672      */
31673     shadow : "frame",
31674     /**
31675      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31676      */
31677     constrain : false,
31678     /**
31679      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31680      */
31681     completeOnEnter : false,
31682     /**
31683      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31684      */
31685     cancelOnEsc : false,
31686     /**
31687      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31688      */
31689     updateEl : false,
31690
31691     // private
31692     onRender : function(ct, position){
31693         this.el = new Roo.Layer({
31694             shadow: this.shadow,
31695             cls: "x-editor",
31696             parentEl : ct,
31697             shim : this.shim,
31698             shadowOffset:4,
31699             id: this.id,
31700             constrain: this.constrain
31701         });
31702         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31703         if(this.field.msgTarget != 'title'){
31704             this.field.msgTarget = 'qtip';
31705         }
31706         this.field.render(this.el);
31707         if(Roo.isGecko){
31708             this.field.el.dom.setAttribute('autocomplete', 'off');
31709         }
31710         this.field.on("specialkey", this.onSpecialKey, this);
31711         if(this.swallowKeys){
31712             this.field.el.swallowEvent(['keydown','keypress']);
31713         }
31714         this.field.show();
31715         this.field.on("blur", this.onBlur, this);
31716         if(this.field.grow){
31717             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31718         }
31719     },
31720
31721     onSpecialKey : function(field, e)
31722     {
31723         //Roo.log('editor onSpecialKey');
31724         if(this.completeOnEnter && e.getKey() == e.ENTER){
31725             e.stopEvent();
31726             this.completeEdit();
31727             return;
31728         }
31729         // do not fire special key otherwise it might hide close the editor...
31730         if(e.getKey() == e.ENTER){    
31731             return;
31732         }
31733         if(this.cancelOnEsc && e.getKey() == e.ESC){
31734             this.cancelEdit();
31735             return;
31736         } 
31737         this.fireEvent('specialkey', field, e);
31738     
31739     },
31740
31741     /**
31742      * Starts the editing process and shows the editor.
31743      * @param {String/HTMLElement/Element} el The element to edit
31744      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31745       * to the innerHTML of el.
31746      */
31747     startEdit : function(el, value){
31748         if(this.editing){
31749             this.completeEdit();
31750         }
31751         this.boundEl = Roo.get(el);
31752         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31753         if(!this.rendered){
31754             this.render(this.parentEl || document.body);
31755         }
31756         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31757             return;
31758         }
31759         this.startValue = v;
31760         this.field.setValue(v);
31761         if(this.autoSize){
31762             var sz = this.boundEl.getSize();
31763             switch(this.autoSize){
31764                 case "width":
31765                 this.setSize(sz.width,  "");
31766                 break;
31767                 case "height":
31768                 this.setSize("",  sz.height);
31769                 break;
31770                 default:
31771                 this.setSize(sz.width,  sz.height);
31772             }
31773         }
31774         this.el.alignTo(this.boundEl, this.alignment);
31775         this.editing = true;
31776         if(Roo.QuickTips){
31777             Roo.QuickTips.disable();
31778         }
31779         this.show();
31780     },
31781
31782     /**
31783      * Sets the height and width of this editor.
31784      * @param {Number} width The new width
31785      * @param {Number} height The new height
31786      */
31787     setSize : function(w, h){
31788         this.field.setSize(w, h);
31789         if(this.el){
31790             this.el.sync();
31791         }
31792     },
31793
31794     /**
31795      * Realigns the editor to the bound field based on the current alignment config value.
31796      */
31797     realign : function(){
31798         this.el.alignTo(this.boundEl, this.alignment);
31799     },
31800
31801     /**
31802      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31803      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31804      */
31805     completeEdit : function(remainVisible){
31806         if(!this.editing){
31807             return;
31808         }
31809         var v = this.getValue();
31810         if(this.revertInvalid !== false && !this.field.isValid()){
31811             v = this.startValue;
31812             this.cancelEdit(true);
31813         }
31814         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31815             this.editing = false;
31816             this.hide();
31817             return;
31818         }
31819         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31820             this.editing = false;
31821             if(this.updateEl && this.boundEl){
31822                 this.boundEl.update(v);
31823             }
31824             if(remainVisible !== true){
31825                 this.hide();
31826             }
31827             this.fireEvent("complete", this, v, this.startValue);
31828         }
31829     },
31830
31831     // private
31832     onShow : function(){
31833         this.el.show();
31834         if(this.hideEl !== false){
31835             this.boundEl.hide();
31836         }
31837         this.field.show();
31838         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31839             this.fixIEFocus = true;
31840             this.deferredFocus.defer(50, this);
31841         }else{
31842             this.field.focus();
31843         }
31844         this.fireEvent("startedit", this.boundEl, this.startValue);
31845     },
31846
31847     deferredFocus : function(){
31848         if(this.editing){
31849             this.field.focus();
31850         }
31851     },
31852
31853     /**
31854      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31855      * reverted to the original starting value.
31856      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31857      * cancel (defaults to false)
31858      */
31859     cancelEdit : function(remainVisible){
31860         if(this.editing){
31861             this.setValue(this.startValue);
31862             if(remainVisible !== true){
31863                 this.hide();
31864             }
31865         }
31866     },
31867
31868     // private
31869     onBlur : function(){
31870         if(this.allowBlur !== true && this.editing){
31871             this.completeEdit();
31872         }
31873     },
31874
31875     // private
31876     onHide : function(){
31877         if(this.editing){
31878             this.completeEdit();
31879             return;
31880         }
31881         this.field.blur();
31882         if(this.field.collapse){
31883             this.field.collapse();
31884         }
31885         this.el.hide();
31886         if(this.hideEl !== false){
31887             this.boundEl.show();
31888         }
31889         if(Roo.QuickTips){
31890             Roo.QuickTips.enable();
31891         }
31892     },
31893
31894     /**
31895      * Sets the data value of the editor
31896      * @param {Mixed} value Any valid value supported by the underlying field
31897      */
31898     setValue : function(v){
31899         this.field.setValue(v);
31900     },
31901
31902     /**
31903      * Gets the data value of the editor
31904      * @return {Mixed} The data value
31905      */
31906     getValue : function(){
31907         return this.field.getValue();
31908     }
31909 });/*
31910  * Based on:
31911  * Ext JS Library 1.1.1
31912  * Copyright(c) 2006-2007, Ext JS, LLC.
31913  *
31914  * Originally Released Under LGPL - original licence link has changed is not relivant.
31915  *
31916  * Fork - LGPL
31917  * <script type="text/javascript">
31918  */
31919  
31920 /**
31921  * @class Roo.BasicDialog
31922  * @extends Roo.util.Observable
31923  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31924  * <pre><code>
31925 var dlg = new Roo.BasicDialog("my-dlg", {
31926     height: 200,
31927     width: 300,
31928     minHeight: 100,
31929     minWidth: 150,
31930     modal: true,
31931     proxyDrag: true,
31932     shadow: true
31933 });
31934 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31935 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31936 dlg.addButton('Cancel', dlg.hide, dlg);
31937 dlg.show();
31938 </code></pre>
31939   <b>A Dialog should always be a direct child of the body element.</b>
31940  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31941  * @cfg {String} title Default text to display in the title bar (defaults to null)
31942  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31943  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31944  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31945  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31946  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31947  * (defaults to null with no animation)
31948  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31949  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31950  * property for valid values (defaults to 'all')
31951  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31952  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31953  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31954  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31955  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31956  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31957  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31958  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31959  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31960  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31961  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31962  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31963  * draggable = true (defaults to false)
31964  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31965  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31966  * shadow (defaults to false)
31967  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31968  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31969  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31970  * @cfg {Array} buttons Array of buttons
31971  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31972  * @constructor
31973  * Create a new BasicDialog.
31974  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31975  * @param {Object} config Configuration options
31976  */
31977 Roo.BasicDialog = function(el, config){
31978     this.el = Roo.get(el);
31979     var dh = Roo.DomHelper;
31980     if(!this.el && config && config.autoCreate){
31981         if(typeof config.autoCreate == "object"){
31982             if(!config.autoCreate.id){
31983                 config.autoCreate.id = el;
31984             }
31985             this.el = dh.append(document.body,
31986                         config.autoCreate, true);
31987         }else{
31988             this.el = dh.append(document.body,
31989                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31990         }
31991     }
31992     el = this.el;
31993     el.setDisplayed(true);
31994     el.hide = this.hideAction;
31995     this.id = el.id;
31996     el.addClass("x-dlg");
31997
31998     Roo.apply(this, config);
31999
32000     this.proxy = el.createProxy("x-dlg-proxy");
32001     this.proxy.hide = this.hideAction;
32002     this.proxy.setOpacity(.5);
32003     this.proxy.hide();
32004
32005     if(config.width){
32006         el.setWidth(config.width);
32007     }
32008     if(config.height){
32009         el.setHeight(config.height);
32010     }
32011     this.size = el.getSize();
32012     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32013         this.xy = [config.x,config.y];
32014     }else{
32015         this.xy = el.getCenterXY(true);
32016     }
32017     /** The header element @type Roo.Element */
32018     this.header = el.child("> .x-dlg-hd");
32019     /** The body element @type Roo.Element */
32020     this.body = el.child("> .x-dlg-bd");
32021     /** The footer element @type Roo.Element */
32022     this.footer = el.child("> .x-dlg-ft");
32023
32024     if(!this.header){
32025         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32026     }
32027     if(!this.body){
32028         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32029     }
32030
32031     this.header.unselectable();
32032     if(this.title){
32033         this.header.update(this.title);
32034     }
32035     // this element allows the dialog to be focused for keyboard event
32036     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32037     this.focusEl.swallowEvent("click", true);
32038
32039     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32040
32041     // wrap the body and footer for special rendering
32042     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32043     if(this.footer){
32044         this.bwrap.dom.appendChild(this.footer.dom);
32045     }
32046
32047     this.bg = this.el.createChild({
32048         tag: "div", cls:"x-dlg-bg",
32049         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32050     });
32051     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32052
32053
32054     if(this.autoScroll !== false && !this.autoTabs){
32055         this.body.setStyle("overflow", "auto");
32056     }
32057
32058     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32059
32060     if(this.closable !== false){
32061         this.el.addClass("x-dlg-closable");
32062         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32063         this.close.on("click", this.closeClick, this);
32064         this.close.addClassOnOver("x-dlg-close-over");
32065     }
32066     if(this.collapsible !== false){
32067         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32068         this.collapseBtn.on("click", this.collapseClick, this);
32069         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32070         this.header.on("dblclick", this.collapseClick, this);
32071     }
32072     if(this.resizable !== false){
32073         this.el.addClass("x-dlg-resizable");
32074         this.resizer = new Roo.Resizable(el, {
32075             minWidth: this.minWidth || 80,
32076             minHeight:this.minHeight || 80,
32077             handles: this.resizeHandles || "all",
32078             pinned: true
32079         });
32080         this.resizer.on("beforeresize", this.beforeResize, this);
32081         this.resizer.on("resize", this.onResize, this);
32082     }
32083     if(this.draggable !== false){
32084         el.addClass("x-dlg-draggable");
32085         if (!this.proxyDrag) {
32086             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32087         }
32088         else {
32089             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32090         }
32091         dd.setHandleElId(this.header.id);
32092         dd.endDrag = this.endMove.createDelegate(this);
32093         dd.startDrag = this.startMove.createDelegate(this);
32094         dd.onDrag = this.onDrag.createDelegate(this);
32095         dd.scroll = false;
32096         this.dd = dd;
32097     }
32098     if(this.modal){
32099         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32100         this.mask.enableDisplayMode("block");
32101         this.mask.hide();
32102         this.el.addClass("x-dlg-modal");
32103     }
32104     if(this.shadow){
32105         this.shadow = new Roo.Shadow({
32106             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32107             offset : this.shadowOffset
32108         });
32109     }else{
32110         this.shadowOffset = 0;
32111     }
32112     if(Roo.useShims && this.shim !== false){
32113         this.shim = this.el.createShim();
32114         this.shim.hide = this.hideAction;
32115         this.shim.hide();
32116     }else{
32117         this.shim = false;
32118     }
32119     if(this.autoTabs){
32120         this.initTabs();
32121     }
32122     if (this.buttons) { 
32123         var bts= this.buttons;
32124         this.buttons = [];
32125         Roo.each(bts, function(b) {
32126             this.addButton(b);
32127         }, this);
32128     }
32129     
32130     
32131     this.addEvents({
32132         /**
32133          * @event keydown
32134          * Fires when a key is pressed
32135          * @param {Roo.BasicDialog} this
32136          * @param {Roo.EventObject} e
32137          */
32138         "keydown" : true,
32139         /**
32140          * @event move
32141          * Fires when this dialog is moved by the user.
32142          * @param {Roo.BasicDialog} this
32143          * @param {Number} x The new page X
32144          * @param {Number} y The new page Y
32145          */
32146         "move" : true,
32147         /**
32148          * @event resize
32149          * Fires when this dialog is resized by the user.
32150          * @param {Roo.BasicDialog} this
32151          * @param {Number} width The new width
32152          * @param {Number} height The new height
32153          */
32154         "resize" : true,
32155         /**
32156          * @event beforehide
32157          * Fires before this dialog is hidden.
32158          * @param {Roo.BasicDialog} this
32159          */
32160         "beforehide" : true,
32161         /**
32162          * @event hide
32163          * Fires when this dialog is hidden.
32164          * @param {Roo.BasicDialog} this
32165          */
32166         "hide" : true,
32167         /**
32168          * @event beforeshow
32169          * Fires before this dialog is shown.
32170          * @param {Roo.BasicDialog} this
32171          */
32172         "beforeshow" : true,
32173         /**
32174          * @event show
32175          * Fires when this dialog is shown.
32176          * @param {Roo.BasicDialog} this
32177          */
32178         "show" : true
32179     });
32180     el.on("keydown", this.onKeyDown, this);
32181     el.on("mousedown", this.toFront, this);
32182     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32183     this.el.hide();
32184     Roo.DialogManager.register(this);
32185     Roo.BasicDialog.superclass.constructor.call(this);
32186 };
32187
32188 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32189     shadowOffset: Roo.isIE ? 6 : 5,
32190     minHeight: 80,
32191     minWidth: 200,
32192     minButtonWidth: 75,
32193     defaultButton: null,
32194     buttonAlign: "right",
32195     tabTag: 'div',
32196     firstShow: true,
32197
32198     /**
32199      * Sets the dialog title text
32200      * @param {String} text The title text to display
32201      * @return {Roo.BasicDialog} this
32202      */
32203     setTitle : function(text){
32204         this.header.update(text);
32205         return this;
32206     },
32207
32208     // private
32209     closeClick : function(){
32210         this.hide();
32211     },
32212
32213     // private
32214     collapseClick : function(){
32215         this[this.collapsed ? "expand" : "collapse"]();
32216     },
32217
32218     /**
32219      * Collapses the dialog to its minimized state (only the title bar is visible).
32220      * Equivalent to the user clicking the collapse dialog button.
32221      */
32222     collapse : function(){
32223         if(!this.collapsed){
32224             this.collapsed = true;
32225             this.el.addClass("x-dlg-collapsed");
32226             this.restoreHeight = this.el.getHeight();
32227             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32228         }
32229     },
32230
32231     /**
32232      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32233      * clicking the expand dialog button.
32234      */
32235     expand : function(){
32236         if(this.collapsed){
32237             this.collapsed = false;
32238             this.el.removeClass("x-dlg-collapsed");
32239             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32240         }
32241     },
32242
32243     /**
32244      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32245      * @return {Roo.TabPanel} The tabs component
32246      */
32247     initTabs : function(){
32248         var tabs = this.getTabs();
32249         while(tabs.getTab(0)){
32250             tabs.removeTab(0);
32251         }
32252         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32253             var dom = el.dom;
32254             tabs.addTab(Roo.id(dom), dom.title);
32255             dom.title = "";
32256         });
32257         tabs.activate(0);
32258         return tabs;
32259     },
32260
32261     // private
32262     beforeResize : function(){
32263         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32264     },
32265
32266     // private
32267     onResize : function(){
32268         this.refreshSize();
32269         this.syncBodyHeight();
32270         this.adjustAssets();
32271         this.focus();
32272         this.fireEvent("resize", this, this.size.width, this.size.height);
32273     },
32274
32275     // private
32276     onKeyDown : function(e){
32277         if(this.isVisible()){
32278             this.fireEvent("keydown", this, e);
32279         }
32280     },
32281
32282     /**
32283      * Resizes the dialog.
32284      * @param {Number} width
32285      * @param {Number} height
32286      * @return {Roo.BasicDialog} this
32287      */
32288     resizeTo : function(width, height){
32289         this.el.setSize(width, height);
32290         this.size = {width: width, height: height};
32291         this.syncBodyHeight();
32292         if(this.fixedcenter){
32293             this.center();
32294         }
32295         if(this.isVisible()){
32296             this.constrainXY();
32297             this.adjustAssets();
32298         }
32299         this.fireEvent("resize", this, width, height);
32300         return this;
32301     },
32302
32303
32304     /**
32305      * Resizes the dialog to fit the specified content size.
32306      * @param {Number} width
32307      * @param {Number} height
32308      * @return {Roo.BasicDialog} this
32309      */
32310     setContentSize : function(w, h){
32311         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32312         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32313         //if(!this.el.isBorderBox()){
32314             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32315             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32316         //}
32317         if(this.tabs){
32318             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32319             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32320         }
32321         this.resizeTo(w, h);
32322         return this;
32323     },
32324
32325     /**
32326      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32327      * executed in response to a particular key being pressed while the dialog is active.
32328      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32329      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32330      * @param {Function} fn The function to call
32331      * @param {Object} scope (optional) The scope of the function
32332      * @return {Roo.BasicDialog} this
32333      */
32334     addKeyListener : function(key, fn, scope){
32335         var keyCode, shift, ctrl, alt;
32336         if(typeof key == "object" && !(key instanceof Array)){
32337             keyCode = key["key"];
32338             shift = key["shift"];
32339             ctrl = key["ctrl"];
32340             alt = key["alt"];
32341         }else{
32342             keyCode = key;
32343         }
32344         var handler = function(dlg, e){
32345             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32346                 var k = e.getKey();
32347                 if(keyCode instanceof Array){
32348                     for(var i = 0, len = keyCode.length; i < len; i++){
32349                         if(keyCode[i] == k){
32350                           fn.call(scope || window, dlg, k, e);
32351                           return;
32352                         }
32353                     }
32354                 }else{
32355                     if(k == keyCode){
32356                         fn.call(scope || window, dlg, k, e);
32357                     }
32358                 }
32359             }
32360         };
32361         this.on("keydown", handler);
32362         return this;
32363     },
32364
32365     /**
32366      * Returns the TabPanel component (creates it if it doesn't exist).
32367      * Note: If you wish to simply check for the existence of tabs without creating them,
32368      * check for a null 'tabs' property.
32369      * @return {Roo.TabPanel} The tabs component
32370      */
32371     getTabs : function(){
32372         if(!this.tabs){
32373             this.el.addClass("x-dlg-auto-tabs");
32374             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32375             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32376         }
32377         return this.tabs;
32378     },
32379
32380     /**
32381      * Adds a button to the footer section of the dialog.
32382      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32383      * object or a valid Roo.DomHelper element config
32384      * @param {Function} handler The function called when the button is clicked
32385      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32386      * @return {Roo.Button} The new button
32387      */
32388     addButton : function(config, handler, scope){
32389         var dh = Roo.DomHelper;
32390         if(!this.footer){
32391             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32392         }
32393         if(!this.btnContainer){
32394             var tb = this.footer.createChild({
32395
32396                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32397                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32398             }, null, true);
32399             this.btnContainer = tb.firstChild.firstChild.firstChild;
32400         }
32401         var bconfig = {
32402             handler: handler,
32403             scope: scope,
32404             minWidth: this.minButtonWidth,
32405             hideParent:true
32406         };
32407         if(typeof config == "string"){
32408             bconfig.text = config;
32409         }else{
32410             if(config.tag){
32411                 bconfig.dhconfig = config;
32412             }else{
32413                 Roo.apply(bconfig, config);
32414             }
32415         }
32416         var fc = false;
32417         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32418             bconfig.position = Math.max(0, bconfig.position);
32419             fc = this.btnContainer.childNodes[bconfig.position];
32420         }
32421          
32422         var btn = new Roo.Button(
32423             fc ? 
32424                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32425                 : this.btnContainer.appendChild(document.createElement("td")),
32426             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32427             bconfig
32428         );
32429         this.syncBodyHeight();
32430         if(!this.buttons){
32431             /**
32432              * Array of all the buttons that have been added to this dialog via addButton
32433              * @type Array
32434              */
32435             this.buttons = [];
32436         }
32437         this.buttons.push(btn);
32438         return btn;
32439     },
32440
32441     /**
32442      * Sets the default button to be focused when the dialog is displayed.
32443      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32444      * @return {Roo.BasicDialog} this
32445      */
32446     setDefaultButton : function(btn){
32447         this.defaultButton = btn;
32448         return this;
32449     },
32450
32451     // private
32452     getHeaderFooterHeight : function(safe){
32453         var height = 0;
32454         if(this.header){
32455            height += this.header.getHeight();
32456         }
32457         if(this.footer){
32458            var fm = this.footer.getMargins();
32459             height += (this.footer.getHeight()+fm.top+fm.bottom);
32460         }
32461         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32462         height += this.centerBg.getPadding("tb");
32463         return height;
32464     },
32465
32466     // private
32467     syncBodyHeight : function()
32468     {
32469         var bd = this.body, // the text
32470             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32471             bw = this.bwrap;
32472         var height = this.size.height - this.getHeaderFooterHeight(false);
32473         bd.setHeight(height-bd.getMargins("tb"));
32474         var hh = this.header.getHeight();
32475         var h = this.size.height-hh;
32476         cb.setHeight(h);
32477         
32478         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32479         bw.setHeight(h-cb.getPadding("tb"));
32480         
32481         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32482         bd.setWidth(bw.getWidth(true));
32483         if(this.tabs){
32484             this.tabs.syncHeight();
32485             if(Roo.isIE){
32486                 this.tabs.el.repaint();
32487             }
32488         }
32489     },
32490
32491     /**
32492      * Restores the previous state of the dialog if Roo.state is configured.
32493      * @return {Roo.BasicDialog} this
32494      */
32495     restoreState : function(){
32496         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32497         if(box && box.width){
32498             this.xy = [box.x, box.y];
32499             this.resizeTo(box.width, box.height);
32500         }
32501         return this;
32502     },
32503
32504     // private
32505     beforeShow : function(){
32506         this.expand();
32507         if(this.fixedcenter){
32508             this.xy = this.el.getCenterXY(true);
32509         }
32510         if(this.modal){
32511             Roo.get(document.body).addClass("x-body-masked");
32512             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32513             this.mask.show();
32514         }
32515         this.constrainXY();
32516     },
32517
32518     // private
32519     animShow : function(){
32520         var b = Roo.get(this.animateTarget).getBox();
32521         this.proxy.setSize(b.width, b.height);
32522         this.proxy.setLocation(b.x, b.y);
32523         this.proxy.show();
32524         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32525                     true, .35, this.showEl.createDelegate(this));
32526     },
32527
32528     /**
32529      * Shows the dialog.
32530      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32531      * @return {Roo.BasicDialog} this
32532      */
32533     show : function(animateTarget){
32534         if (this.fireEvent("beforeshow", this) === false){
32535             return;
32536         }
32537         if(this.syncHeightBeforeShow){
32538             this.syncBodyHeight();
32539         }else if(this.firstShow){
32540             this.firstShow = false;
32541             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32542         }
32543         this.animateTarget = animateTarget || this.animateTarget;
32544         if(!this.el.isVisible()){
32545             this.beforeShow();
32546             if(this.animateTarget && Roo.get(this.animateTarget)){
32547                 this.animShow();
32548             }else{
32549                 this.showEl();
32550             }
32551         }
32552         return this;
32553     },
32554
32555     // private
32556     showEl : function(){
32557         this.proxy.hide();
32558         this.el.setXY(this.xy);
32559         this.el.show();
32560         this.adjustAssets(true);
32561         this.toFront();
32562         this.focus();
32563         // IE peekaboo bug - fix found by Dave Fenwick
32564         if(Roo.isIE){
32565             this.el.repaint();
32566         }
32567         this.fireEvent("show", this);
32568     },
32569
32570     /**
32571      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32572      * dialog itself will receive focus.
32573      */
32574     focus : function(){
32575         if(this.defaultButton){
32576             this.defaultButton.focus();
32577         }else{
32578             this.focusEl.focus();
32579         }
32580     },
32581
32582     // private
32583     constrainXY : function(){
32584         if(this.constraintoviewport !== false){
32585             if(!this.viewSize){
32586                 if(this.container){
32587                     var s = this.container.getSize();
32588                     this.viewSize = [s.width, s.height];
32589                 }else{
32590                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32591                 }
32592             }
32593             var s = Roo.get(this.container||document).getScroll();
32594
32595             var x = this.xy[0], y = this.xy[1];
32596             var w = this.size.width, h = this.size.height;
32597             var vw = this.viewSize[0], vh = this.viewSize[1];
32598             // only move it if it needs it
32599             var moved = false;
32600             // first validate right/bottom
32601             if(x + w > vw+s.left){
32602                 x = vw - w;
32603                 moved = true;
32604             }
32605             if(y + h > vh+s.top){
32606                 y = vh - h;
32607                 moved = true;
32608             }
32609             // then make sure top/left isn't negative
32610             if(x < s.left){
32611                 x = s.left;
32612                 moved = true;
32613             }
32614             if(y < s.top){
32615                 y = s.top;
32616                 moved = true;
32617             }
32618             if(moved){
32619                 // cache xy
32620                 this.xy = [x, y];
32621                 if(this.isVisible()){
32622                     this.el.setLocation(x, y);
32623                     this.adjustAssets();
32624                 }
32625             }
32626         }
32627     },
32628
32629     // private
32630     onDrag : function(){
32631         if(!this.proxyDrag){
32632             this.xy = this.el.getXY();
32633             this.adjustAssets();
32634         }
32635     },
32636
32637     // private
32638     adjustAssets : function(doShow){
32639         var x = this.xy[0], y = this.xy[1];
32640         var w = this.size.width, h = this.size.height;
32641         if(doShow === true){
32642             if(this.shadow){
32643                 this.shadow.show(this.el);
32644             }
32645             if(this.shim){
32646                 this.shim.show();
32647             }
32648         }
32649         if(this.shadow && this.shadow.isVisible()){
32650             this.shadow.show(this.el);
32651         }
32652         if(this.shim && this.shim.isVisible()){
32653             this.shim.setBounds(x, y, w, h);
32654         }
32655     },
32656
32657     // private
32658     adjustViewport : function(w, h){
32659         if(!w || !h){
32660             w = Roo.lib.Dom.getViewWidth();
32661             h = Roo.lib.Dom.getViewHeight();
32662         }
32663         // cache the size
32664         this.viewSize = [w, h];
32665         if(this.modal && this.mask.isVisible()){
32666             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32667             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32668         }
32669         if(this.isVisible()){
32670             this.constrainXY();
32671         }
32672     },
32673
32674     /**
32675      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32676      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32677      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32678      */
32679     destroy : function(removeEl){
32680         if(this.isVisible()){
32681             this.animateTarget = null;
32682             this.hide();
32683         }
32684         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32685         if(this.tabs){
32686             this.tabs.destroy(removeEl);
32687         }
32688         Roo.destroy(
32689              this.shim,
32690              this.proxy,
32691              this.resizer,
32692              this.close,
32693              this.mask
32694         );
32695         if(this.dd){
32696             this.dd.unreg();
32697         }
32698         if(this.buttons){
32699            for(var i = 0, len = this.buttons.length; i < len; i++){
32700                this.buttons[i].destroy();
32701            }
32702         }
32703         this.el.removeAllListeners();
32704         if(removeEl === true){
32705             this.el.update("");
32706             this.el.remove();
32707         }
32708         Roo.DialogManager.unregister(this);
32709     },
32710
32711     // private
32712     startMove : function(){
32713         if(this.proxyDrag){
32714             this.proxy.show();
32715         }
32716         if(this.constraintoviewport !== false){
32717             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32718         }
32719     },
32720
32721     // private
32722     endMove : function(){
32723         if(!this.proxyDrag){
32724             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32725         }else{
32726             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32727             this.proxy.hide();
32728         }
32729         this.refreshSize();
32730         this.adjustAssets();
32731         this.focus();
32732         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32733     },
32734
32735     /**
32736      * Brings this dialog to the front of any other visible dialogs
32737      * @return {Roo.BasicDialog} this
32738      */
32739     toFront : function(){
32740         Roo.DialogManager.bringToFront(this);
32741         return this;
32742     },
32743
32744     /**
32745      * Sends this dialog to the back (under) of any other visible dialogs
32746      * @return {Roo.BasicDialog} this
32747      */
32748     toBack : function(){
32749         Roo.DialogManager.sendToBack(this);
32750         return this;
32751     },
32752
32753     /**
32754      * Centers this dialog in the viewport
32755      * @return {Roo.BasicDialog} this
32756      */
32757     center : function(){
32758         var xy = this.el.getCenterXY(true);
32759         this.moveTo(xy[0], xy[1]);
32760         return this;
32761     },
32762
32763     /**
32764      * Moves the dialog's top-left corner to the specified point
32765      * @param {Number} x
32766      * @param {Number} y
32767      * @return {Roo.BasicDialog} this
32768      */
32769     moveTo : function(x, y){
32770         this.xy = [x,y];
32771         if(this.isVisible()){
32772             this.el.setXY(this.xy);
32773             this.adjustAssets();
32774         }
32775         return this;
32776     },
32777
32778     /**
32779      * Aligns the dialog to the specified element
32780      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32781      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32782      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32783      * @return {Roo.BasicDialog} this
32784      */
32785     alignTo : function(element, position, offsets){
32786         this.xy = this.el.getAlignToXY(element, position, offsets);
32787         if(this.isVisible()){
32788             this.el.setXY(this.xy);
32789             this.adjustAssets();
32790         }
32791         return this;
32792     },
32793
32794     /**
32795      * Anchors an element to another element and realigns it when the window is resized.
32796      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32797      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32798      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32799      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32800      * is a number, it is used as the buffer delay (defaults to 50ms).
32801      * @return {Roo.BasicDialog} this
32802      */
32803     anchorTo : function(el, alignment, offsets, monitorScroll){
32804         var action = function(){
32805             this.alignTo(el, alignment, offsets);
32806         };
32807         Roo.EventManager.onWindowResize(action, this);
32808         var tm = typeof monitorScroll;
32809         if(tm != 'undefined'){
32810             Roo.EventManager.on(window, 'scroll', action, this,
32811                 {buffer: tm == 'number' ? monitorScroll : 50});
32812         }
32813         action.call(this);
32814         return this;
32815     },
32816
32817     /**
32818      * Returns true if the dialog is visible
32819      * @return {Boolean}
32820      */
32821     isVisible : function(){
32822         return this.el.isVisible();
32823     },
32824
32825     // private
32826     animHide : function(callback){
32827         var b = Roo.get(this.animateTarget).getBox();
32828         this.proxy.show();
32829         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32830         this.el.hide();
32831         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32832                     this.hideEl.createDelegate(this, [callback]));
32833     },
32834
32835     /**
32836      * Hides the dialog.
32837      * @param {Function} callback (optional) Function to call when the dialog is hidden
32838      * @return {Roo.BasicDialog} this
32839      */
32840     hide : function(callback){
32841         if (this.fireEvent("beforehide", this) === false){
32842             return;
32843         }
32844         if(this.shadow){
32845             this.shadow.hide();
32846         }
32847         if(this.shim) {
32848           this.shim.hide();
32849         }
32850         // sometimes animateTarget seems to get set.. causing problems...
32851         // this just double checks..
32852         if(this.animateTarget && Roo.get(this.animateTarget)) {
32853            this.animHide(callback);
32854         }else{
32855             this.el.hide();
32856             this.hideEl(callback);
32857         }
32858         return this;
32859     },
32860
32861     // private
32862     hideEl : function(callback){
32863         this.proxy.hide();
32864         if(this.modal){
32865             this.mask.hide();
32866             Roo.get(document.body).removeClass("x-body-masked");
32867         }
32868         this.fireEvent("hide", this);
32869         if(typeof callback == "function"){
32870             callback();
32871         }
32872     },
32873
32874     // private
32875     hideAction : function(){
32876         this.setLeft("-10000px");
32877         this.setTop("-10000px");
32878         this.setStyle("visibility", "hidden");
32879     },
32880
32881     // private
32882     refreshSize : function(){
32883         this.size = this.el.getSize();
32884         this.xy = this.el.getXY();
32885         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32886     },
32887
32888     // private
32889     // z-index is managed by the DialogManager and may be overwritten at any time
32890     setZIndex : function(index){
32891         if(this.modal){
32892             this.mask.setStyle("z-index", index);
32893         }
32894         if(this.shim){
32895             this.shim.setStyle("z-index", ++index);
32896         }
32897         if(this.shadow){
32898             this.shadow.setZIndex(++index);
32899         }
32900         this.el.setStyle("z-index", ++index);
32901         if(this.proxy){
32902             this.proxy.setStyle("z-index", ++index);
32903         }
32904         if(this.resizer){
32905             this.resizer.proxy.setStyle("z-index", ++index);
32906         }
32907
32908         this.lastZIndex = index;
32909     },
32910
32911     /**
32912      * Returns the element for this dialog
32913      * @return {Roo.Element} The underlying dialog Element
32914      */
32915     getEl : function(){
32916         return this.el;
32917     }
32918 });
32919
32920 /**
32921  * @class Roo.DialogManager
32922  * Provides global access to BasicDialogs that have been created and
32923  * support for z-indexing (layering) multiple open dialogs.
32924  */
32925 Roo.DialogManager = function(){
32926     var list = {};
32927     var accessList = [];
32928     var front = null;
32929
32930     // private
32931     var sortDialogs = function(d1, d2){
32932         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32933     };
32934
32935     // private
32936     var orderDialogs = function(){
32937         accessList.sort(sortDialogs);
32938         var seed = Roo.DialogManager.zseed;
32939         for(var i = 0, len = accessList.length; i < len; i++){
32940             var dlg = accessList[i];
32941             if(dlg){
32942                 dlg.setZIndex(seed + (i*10));
32943             }
32944         }
32945     };
32946
32947     return {
32948         /**
32949          * The starting z-index for BasicDialogs (defaults to 9000)
32950          * @type Number The z-index value
32951          */
32952         zseed : 9000,
32953
32954         // private
32955         register : function(dlg){
32956             list[dlg.id] = dlg;
32957             accessList.push(dlg);
32958         },
32959
32960         // private
32961         unregister : function(dlg){
32962             delete list[dlg.id];
32963             var i=0;
32964             var len=0;
32965             if(!accessList.indexOf){
32966                 for(  i = 0, len = accessList.length; i < len; i++){
32967                     if(accessList[i] == dlg){
32968                         accessList.splice(i, 1);
32969                         return;
32970                     }
32971                 }
32972             }else{
32973                  i = accessList.indexOf(dlg);
32974                 if(i != -1){
32975                     accessList.splice(i, 1);
32976                 }
32977             }
32978         },
32979
32980         /**
32981          * Gets a registered dialog by id
32982          * @param {String/Object} id The id of the dialog or a dialog
32983          * @return {Roo.BasicDialog} this
32984          */
32985         get : function(id){
32986             return typeof id == "object" ? id : list[id];
32987         },
32988
32989         /**
32990          * Brings the specified dialog to the front
32991          * @param {String/Object} dlg The id of the dialog or a dialog
32992          * @return {Roo.BasicDialog} this
32993          */
32994         bringToFront : function(dlg){
32995             dlg = this.get(dlg);
32996             if(dlg != front){
32997                 front = dlg;
32998                 dlg._lastAccess = new Date().getTime();
32999                 orderDialogs();
33000             }
33001             return dlg;
33002         },
33003
33004         /**
33005          * Sends the specified dialog to the back
33006          * @param {String/Object} dlg The id of the dialog or a dialog
33007          * @return {Roo.BasicDialog} this
33008          */
33009         sendToBack : function(dlg){
33010             dlg = this.get(dlg);
33011             dlg._lastAccess = -(new Date().getTime());
33012             orderDialogs();
33013             return dlg;
33014         },
33015
33016         /**
33017          * Hides all dialogs
33018          */
33019         hideAll : function(){
33020             for(var id in list){
33021                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33022                     list[id].hide();
33023                 }
33024             }
33025         }
33026     };
33027 }();
33028
33029 /**
33030  * @class Roo.LayoutDialog
33031  * @extends Roo.BasicDialog
33032  * Dialog which provides adjustments for working with a layout in a Dialog.
33033  * Add your necessary layout config options to the dialog's config.<br>
33034  * Example usage (including a nested layout):
33035  * <pre><code>
33036 if(!dialog){
33037     dialog = new Roo.LayoutDialog("download-dlg", {
33038         modal: true,
33039         width:600,
33040         height:450,
33041         shadow:true,
33042         minWidth:500,
33043         minHeight:350,
33044         autoTabs:true,
33045         proxyDrag:true,
33046         // layout config merges with the dialog config
33047         center:{
33048             tabPosition: "top",
33049             alwaysShowTabs: true
33050         }
33051     });
33052     dialog.addKeyListener(27, dialog.hide, dialog);
33053     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33054     dialog.addButton("Build It!", this.getDownload, this);
33055
33056     // we can even add nested layouts
33057     var innerLayout = new Roo.BorderLayout("dl-inner", {
33058         east: {
33059             initialSize: 200,
33060             autoScroll:true,
33061             split:true
33062         },
33063         center: {
33064             autoScroll:true
33065         }
33066     });
33067     innerLayout.beginUpdate();
33068     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33069     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33070     innerLayout.endUpdate(true);
33071
33072     var layout = dialog.getLayout();
33073     layout.beginUpdate();
33074     layout.add("center", new Roo.ContentPanel("standard-panel",
33075                         {title: "Download the Source", fitToFrame:true}));
33076     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33077                {title: "Build your own roo.js"}));
33078     layout.getRegion("center").showPanel(sp);
33079     layout.endUpdate();
33080 }
33081 </code></pre>
33082     * @constructor
33083     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33084     * @param {Object} config configuration options
33085   */
33086 Roo.LayoutDialog = function(el, cfg){
33087     
33088     var config=  cfg;
33089     if (typeof(cfg) == 'undefined') {
33090         config = Roo.apply({}, el);
33091         // not sure why we use documentElement here.. - it should always be body.
33092         // IE7 borks horribly if we use documentElement.
33093         // webkit also does not like documentElement - it creates a body element...
33094         el = Roo.get( document.body || document.documentElement ).createChild();
33095         //config.autoCreate = true;
33096     }
33097     
33098     
33099     config.autoTabs = false;
33100     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33101     this.body.setStyle({overflow:"hidden", position:"relative"});
33102     this.layout = new Roo.BorderLayout(this.body.dom, config);
33103     this.layout.monitorWindowResize = false;
33104     this.el.addClass("x-dlg-auto-layout");
33105     // fix case when center region overwrites center function
33106     this.center = Roo.BasicDialog.prototype.center;
33107     this.on("show", this.layout.layout, this.layout, true);
33108     if (config.items) {
33109         var xitems = config.items;
33110         delete config.items;
33111         Roo.each(xitems, this.addxtype, this);
33112     }
33113     
33114     
33115 };
33116 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33117     /**
33118      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33119      * @deprecated
33120      */
33121     endUpdate : function(){
33122         this.layout.endUpdate();
33123     },
33124
33125     /**
33126      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33127      *  @deprecated
33128      */
33129     beginUpdate : function(){
33130         this.layout.beginUpdate();
33131     },
33132
33133     /**
33134      * Get the BorderLayout for this dialog
33135      * @return {Roo.BorderLayout}
33136      */
33137     getLayout : function(){
33138         return this.layout;
33139     },
33140
33141     showEl : function(){
33142         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33143         if(Roo.isIE7){
33144             this.layout.layout();
33145         }
33146     },
33147
33148     // private
33149     // Use the syncHeightBeforeShow config option to control this automatically
33150     syncBodyHeight : function(){
33151         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33152         if(this.layout){this.layout.layout();}
33153     },
33154     
33155       /**
33156      * Add an xtype element (actually adds to the layout.)
33157      * @return {Object} xdata xtype object data.
33158      */
33159     
33160     addxtype : function(c) {
33161         return this.layout.addxtype(c);
33162     }
33163 });/*
33164  * Based on:
33165  * Ext JS Library 1.1.1
33166  * Copyright(c) 2006-2007, Ext JS, LLC.
33167  *
33168  * Originally Released Under LGPL - original licence link has changed is not relivant.
33169  *
33170  * Fork - LGPL
33171  * <script type="text/javascript">
33172  */
33173  
33174 /**
33175  * @class Roo.MessageBox
33176  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33177  * Example usage:
33178  *<pre><code>
33179 // Basic alert:
33180 Roo.Msg.alert('Status', 'Changes saved successfully.');
33181
33182 // Prompt for user data:
33183 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33184     if (btn == 'ok'){
33185         // process text value...
33186     }
33187 });
33188
33189 // Show a dialog using config options:
33190 Roo.Msg.show({
33191    title:'Save Changes?',
33192    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33193    buttons: Roo.Msg.YESNOCANCEL,
33194    fn: processResult,
33195    animEl: 'elId'
33196 });
33197 </code></pre>
33198  * @singleton
33199  */
33200 Roo.MessageBox = function(){
33201     var dlg, opt, mask, waitTimer;
33202     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33203     var buttons, activeTextEl, bwidth;
33204
33205     // private
33206     var handleButton = function(button){
33207         dlg.hide();
33208         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33209     };
33210
33211     // private
33212     var handleHide = function(){
33213         if(opt && opt.cls){
33214             dlg.el.removeClass(opt.cls);
33215         }
33216         if(waitTimer){
33217             Roo.TaskMgr.stop(waitTimer);
33218             waitTimer = null;
33219         }
33220     };
33221
33222     // private
33223     var updateButtons = function(b){
33224         var width = 0;
33225         if(!b){
33226             buttons["ok"].hide();
33227             buttons["cancel"].hide();
33228             buttons["yes"].hide();
33229             buttons["no"].hide();
33230             dlg.footer.dom.style.display = 'none';
33231             return width;
33232         }
33233         dlg.footer.dom.style.display = '';
33234         for(var k in buttons){
33235             if(typeof buttons[k] != "function"){
33236                 if(b[k]){
33237                     buttons[k].show();
33238                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33239                     width += buttons[k].el.getWidth()+15;
33240                 }else{
33241                     buttons[k].hide();
33242                 }
33243             }
33244         }
33245         return width;
33246     };
33247
33248     // private
33249     var handleEsc = function(d, k, e){
33250         if(opt && opt.closable !== false){
33251             dlg.hide();
33252         }
33253         if(e){
33254             e.stopEvent();
33255         }
33256     };
33257
33258     return {
33259         /**
33260          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33261          * @return {Roo.BasicDialog} The BasicDialog element
33262          */
33263         getDialog : function(){
33264            if(!dlg){
33265                 dlg = new Roo.BasicDialog("x-msg-box", {
33266                     autoCreate : true,
33267                     shadow: true,
33268                     draggable: true,
33269                     resizable:false,
33270                     constraintoviewport:false,
33271                     fixedcenter:true,
33272                     collapsible : false,
33273                     shim:true,
33274                     modal: true,
33275                     width:400, height:100,
33276                     buttonAlign:"center",
33277                     closeClick : function(){
33278                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33279                             handleButton("no");
33280                         }else{
33281                             handleButton("cancel");
33282                         }
33283                     }
33284                 });
33285                 dlg.on("hide", handleHide);
33286                 mask = dlg.mask;
33287                 dlg.addKeyListener(27, handleEsc);
33288                 buttons = {};
33289                 var bt = this.buttonText;
33290                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33291                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33292                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33293                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33294                 bodyEl = dlg.body.createChild({
33295
33296                     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>'
33297                 });
33298                 msgEl = bodyEl.dom.firstChild;
33299                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33300                 textboxEl.enableDisplayMode();
33301                 textboxEl.addKeyListener([10,13], function(){
33302                     if(dlg.isVisible() && opt && opt.buttons){
33303                         if(opt.buttons.ok){
33304                             handleButton("ok");
33305                         }else if(opt.buttons.yes){
33306                             handleButton("yes");
33307                         }
33308                     }
33309                 });
33310                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33311                 textareaEl.enableDisplayMode();
33312                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33313                 progressEl.enableDisplayMode();
33314                 var pf = progressEl.dom.firstChild;
33315                 if (pf) {
33316                     pp = Roo.get(pf.firstChild);
33317                     pp.setHeight(pf.offsetHeight);
33318                 }
33319                 
33320             }
33321             return dlg;
33322         },
33323
33324         /**
33325          * Updates the message box body text
33326          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33327          * the XHTML-compliant non-breaking space character '&amp;#160;')
33328          * @return {Roo.MessageBox} This message box
33329          */
33330         updateText : function(text){
33331             if(!dlg.isVisible() && !opt.width){
33332                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33333             }
33334             msgEl.innerHTML = text || '&#160;';
33335       
33336             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33337             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33338             var w = Math.max(
33339                     Math.min(opt.width || cw , this.maxWidth), 
33340                     Math.max(opt.minWidth || this.minWidth, bwidth)
33341             );
33342             if(opt.prompt){
33343                 activeTextEl.setWidth(w);
33344             }
33345             if(dlg.isVisible()){
33346                 dlg.fixedcenter = false;
33347             }
33348             // to big, make it scroll. = But as usual stupid IE does not support
33349             // !important..
33350             
33351             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33352                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33353                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33354             } else {
33355                 bodyEl.dom.style.height = '';
33356                 bodyEl.dom.style.overflowY = '';
33357             }
33358             if (cw > w) {
33359                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33360             } else {
33361                 bodyEl.dom.style.overflowX = '';
33362             }
33363             
33364             dlg.setContentSize(w, bodyEl.getHeight());
33365             if(dlg.isVisible()){
33366                 dlg.fixedcenter = true;
33367             }
33368             return this;
33369         },
33370
33371         /**
33372          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33373          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33374          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33375          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33376          * @return {Roo.MessageBox} This message box
33377          */
33378         updateProgress : function(value, text){
33379             if(text){
33380                 this.updateText(text);
33381             }
33382             if (pp) { // weird bug on my firefox - for some reason this is not defined
33383                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33384             }
33385             return this;
33386         },        
33387
33388         /**
33389          * Returns true if the message box is currently displayed
33390          * @return {Boolean} True if the message box is visible, else false
33391          */
33392         isVisible : function(){
33393             return dlg && dlg.isVisible();  
33394         },
33395
33396         /**
33397          * Hides the message box if it is displayed
33398          */
33399         hide : function(){
33400             if(this.isVisible()){
33401                 dlg.hide();
33402             }  
33403         },
33404
33405         /**
33406          * Displays a new message box, or reinitializes an existing message box, based on the config options
33407          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33408          * The following config object properties are supported:
33409          * <pre>
33410 Property    Type             Description
33411 ----------  ---------------  ------------------------------------------------------------------------------------
33412 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33413                                    closes (defaults to undefined)
33414 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33415                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33416 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33417                                    progress and wait dialogs will ignore this property and always hide the
33418                                    close button as they can only be closed programmatically.
33419 cls               String           A custom CSS class to apply to the message box element
33420 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33421                                    displayed (defaults to 75)
33422 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33423                                    function will be btn (the name of the button that was clicked, if applicable,
33424                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33425                                    Progress and wait dialogs will ignore this option since they do not respond to
33426                                    user actions and can only be closed programmatically, so any required function
33427                                    should be called by the same code after it closes the dialog.
33428 icon              String           A CSS class that provides a background image to be used as an icon for
33429                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33430 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33431 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33432 modal             Boolean          False to allow user interaction with the page while the message box is
33433                                    displayed (defaults to true)
33434 msg               String           A string that will replace the existing message box body text (defaults
33435                                    to the XHTML-compliant non-breaking space character '&#160;')
33436 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33437 progress          Boolean          True to display a progress bar (defaults to false)
33438 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33439 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33440 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33441 title             String           The title text
33442 value             String           The string value to set into the active textbox element if displayed
33443 wait              Boolean          True to display a progress bar (defaults to false)
33444 width             Number           The width of the dialog in pixels
33445 </pre>
33446          *
33447          * Example usage:
33448          * <pre><code>
33449 Roo.Msg.show({
33450    title: 'Address',
33451    msg: 'Please enter your address:',
33452    width: 300,
33453    buttons: Roo.MessageBox.OKCANCEL,
33454    multiline: true,
33455    fn: saveAddress,
33456    animEl: 'addAddressBtn'
33457 });
33458 </code></pre>
33459          * @param {Object} config Configuration options
33460          * @return {Roo.MessageBox} This message box
33461          */
33462         show : function(options)
33463         {
33464             
33465             // this causes nightmares if you show one dialog after another
33466             // especially on callbacks..
33467              
33468             if(this.isVisible()){
33469                 
33470                 this.hide();
33471                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33472                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33473                 Roo.log("New Dialog Message:" +  options.msg )
33474                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33475                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33476                 
33477             }
33478             var d = this.getDialog();
33479             opt = options;
33480             d.setTitle(opt.title || "&#160;");
33481             d.close.setDisplayed(opt.closable !== false);
33482             activeTextEl = textboxEl;
33483             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33484             if(opt.prompt){
33485                 if(opt.multiline){
33486                     textboxEl.hide();
33487                     textareaEl.show();
33488                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33489                         opt.multiline : this.defaultTextHeight);
33490                     activeTextEl = textareaEl;
33491                 }else{
33492                     textboxEl.show();
33493                     textareaEl.hide();
33494                 }
33495             }else{
33496                 textboxEl.hide();
33497                 textareaEl.hide();
33498             }
33499             progressEl.setDisplayed(opt.progress === true);
33500             this.updateProgress(0);
33501             activeTextEl.dom.value = opt.value || "";
33502             if(opt.prompt){
33503                 dlg.setDefaultButton(activeTextEl);
33504             }else{
33505                 var bs = opt.buttons;
33506                 var db = null;
33507                 if(bs && bs.ok){
33508                     db = buttons["ok"];
33509                 }else if(bs && bs.yes){
33510                     db = buttons["yes"];
33511                 }
33512                 dlg.setDefaultButton(db);
33513             }
33514             bwidth = updateButtons(opt.buttons);
33515             this.updateText(opt.msg);
33516             if(opt.cls){
33517                 d.el.addClass(opt.cls);
33518             }
33519             d.proxyDrag = opt.proxyDrag === true;
33520             d.modal = opt.modal !== false;
33521             d.mask = opt.modal !== false ? mask : false;
33522             if(!d.isVisible()){
33523                 // force it to the end of the z-index stack so it gets a cursor in FF
33524                 document.body.appendChild(dlg.el.dom);
33525                 d.animateTarget = null;
33526                 d.show(options.animEl);
33527             }
33528             return this;
33529         },
33530
33531         /**
33532          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33533          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33534          * and closing the message box when the process is complete.
33535          * @param {String} title The title bar text
33536          * @param {String} msg The message box body text
33537          * @return {Roo.MessageBox} This message box
33538          */
33539         progress : function(title, msg){
33540             this.show({
33541                 title : title,
33542                 msg : msg,
33543                 buttons: false,
33544                 progress:true,
33545                 closable:false,
33546                 minWidth: this.minProgressWidth,
33547                 modal : true
33548             });
33549             return this;
33550         },
33551
33552         /**
33553          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33554          * If a callback function is passed it will be called after the user clicks the button, and the
33555          * id of the button that was clicked will be passed as the only parameter to the callback
33556          * (could also be the top-right close button).
33557          * @param {String} title The title bar text
33558          * @param {String} msg The message box body text
33559          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33560          * @param {Object} scope (optional) The scope of the callback function
33561          * @return {Roo.MessageBox} This message box
33562          */
33563         alert : function(title, msg, fn, scope){
33564             this.show({
33565                 title : title,
33566                 msg : msg,
33567                 buttons: this.OK,
33568                 fn: fn,
33569                 scope : scope,
33570                 modal : true
33571             });
33572             return this;
33573         },
33574
33575         /**
33576          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33577          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33578          * You are responsible for closing the message box when the process is complete.
33579          * @param {String} msg The message box body text
33580          * @param {String} title (optional) The title bar text
33581          * @return {Roo.MessageBox} This message box
33582          */
33583         wait : function(msg, title){
33584             this.show({
33585                 title : title,
33586                 msg : msg,
33587                 buttons: false,
33588                 closable:false,
33589                 progress:true,
33590                 modal:true,
33591                 width:300,
33592                 wait:true
33593             });
33594             waitTimer = Roo.TaskMgr.start({
33595                 run: function(i){
33596                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33597                 },
33598                 interval: 1000
33599             });
33600             return this;
33601         },
33602
33603         /**
33604          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33605          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33606          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33607          * @param {String} title The title bar text
33608          * @param {String} msg The message box body text
33609          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33610          * @param {Object} scope (optional) The scope of the callback function
33611          * @return {Roo.MessageBox} This message box
33612          */
33613         confirm : function(title, msg, fn, scope){
33614             this.show({
33615                 title : title,
33616                 msg : msg,
33617                 buttons: this.YESNO,
33618                 fn: fn,
33619                 scope : scope,
33620                 modal : true
33621             });
33622             return this;
33623         },
33624
33625         /**
33626          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33627          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33628          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33629          * (could also be the top-right close button) and the text that was entered will be passed as the two
33630          * parameters to the callback.
33631          * @param {String} title The title bar text
33632          * @param {String} msg The message box body text
33633          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33634          * @param {Object} scope (optional) The scope of the callback function
33635          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33636          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33637          * @return {Roo.MessageBox} This message box
33638          */
33639         prompt : function(title, msg, fn, scope, multiline){
33640             this.show({
33641                 title : title,
33642                 msg : msg,
33643                 buttons: this.OKCANCEL,
33644                 fn: fn,
33645                 minWidth:250,
33646                 scope : scope,
33647                 prompt:true,
33648                 multiline: multiline,
33649                 modal : true
33650             });
33651             return this;
33652         },
33653
33654         /**
33655          * Button config that displays a single OK button
33656          * @type Object
33657          */
33658         OK : {ok:true},
33659         /**
33660          * Button config that displays Yes and No buttons
33661          * @type Object
33662          */
33663         YESNO : {yes:true, no:true},
33664         /**
33665          * Button config that displays OK and Cancel buttons
33666          * @type Object
33667          */
33668         OKCANCEL : {ok:true, cancel:true},
33669         /**
33670          * Button config that displays Yes, No and Cancel buttons
33671          * @type Object
33672          */
33673         YESNOCANCEL : {yes:true, no:true, cancel:true},
33674
33675         /**
33676          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33677          * @type Number
33678          */
33679         defaultTextHeight : 75,
33680         /**
33681          * The maximum width in pixels of the message box (defaults to 600)
33682          * @type Number
33683          */
33684         maxWidth : 600,
33685         /**
33686          * The minimum width in pixels of the message box (defaults to 100)
33687          * @type Number
33688          */
33689         minWidth : 100,
33690         /**
33691          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33692          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33693          * @type Number
33694          */
33695         minProgressWidth : 250,
33696         /**
33697          * An object containing the default button text strings that can be overriden for localized language support.
33698          * Supported properties are: ok, cancel, yes and no.
33699          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33700          * @type Object
33701          */
33702         buttonText : {
33703             ok : "OK",
33704             cancel : "Cancel",
33705             yes : "Yes",
33706             no : "No"
33707         }
33708     };
33709 }();
33710
33711 /**
33712  * Shorthand for {@link Roo.MessageBox}
33713  */
33714 Roo.Msg = Roo.MessageBox;/*
33715  * Based on:
33716  * Ext JS Library 1.1.1
33717  * Copyright(c) 2006-2007, Ext JS, LLC.
33718  *
33719  * Originally Released Under LGPL - original licence link has changed is not relivant.
33720  *
33721  * Fork - LGPL
33722  * <script type="text/javascript">
33723  */
33724 /**
33725  * @class Roo.QuickTips
33726  * Provides attractive and customizable tooltips for any element.
33727  * @singleton
33728  */
33729 Roo.QuickTips = function(){
33730     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33731     var ce, bd, xy, dd;
33732     var visible = false, disabled = true, inited = false;
33733     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33734     
33735     var onOver = function(e){
33736         if(disabled){
33737             return;
33738         }
33739         var t = e.getTarget();
33740         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33741             return;
33742         }
33743         if(ce && t == ce.el){
33744             clearTimeout(hideProc);
33745             return;
33746         }
33747         if(t && tagEls[t.id]){
33748             tagEls[t.id].el = t;
33749             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33750             return;
33751         }
33752         var ttp, et = Roo.fly(t);
33753         var ns = cfg.namespace;
33754         if(tm.interceptTitles && t.title){
33755             ttp = t.title;
33756             t.qtip = ttp;
33757             t.removeAttribute("title");
33758             e.preventDefault();
33759         }else{
33760             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33761         }
33762         if(ttp){
33763             showProc = show.defer(tm.showDelay, tm, [{
33764                 el: t, 
33765                 text: ttp.replace(/\\n/g,'<br/>'),
33766                 width: et.getAttributeNS(ns, cfg.width),
33767                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33768                 title: et.getAttributeNS(ns, cfg.title),
33769                     cls: et.getAttributeNS(ns, cfg.cls)
33770             }]);
33771         }
33772     };
33773     
33774     var onOut = function(e){
33775         clearTimeout(showProc);
33776         var t = e.getTarget();
33777         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33778             hideProc = setTimeout(hide, tm.hideDelay);
33779         }
33780     };
33781     
33782     var onMove = function(e){
33783         if(disabled){
33784             return;
33785         }
33786         xy = e.getXY();
33787         xy[1] += 18;
33788         if(tm.trackMouse && ce){
33789             el.setXY(xy);
33790         }
33791     };
33792     
33793     var onDown = function(e){
33794         clearTimeout(showProc);
33795         clearTimeout(hideProc);
33796         if(!e.within(el)){
33797             if(tm.hideOnClick){
33798                 hide();
33799                 tm.disable();
33800                 tm.enable.defer(100, tm);
33801             }
33802         }
33803     };
33804     
33805     var getPad = function(){
33806         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33807     };
33808
33809     var show = function(o){
33810         if(disabled){
33811             return;
33812         }
33813         clearTimeout(dismissProc);
33814         ce = o;
33815         if(removeCls){ // in case manually hidden
33816             el.removeClass(removeCls);
33817             removeCls = null;
33818         }
33819         if(ce.cls){
33820             el.addClass(ce.cls);
33821             removeCls = ce.cls;
33822         }
33823         if(ce.title){
33824             tipTitle.update(ce.title);
33825             tipTitle.show();
33826         }else{
33827             tipTitle.update('');
33828             tipTitle.hide();
33829         }
33830         el.dom.style.width  = tm.maxWidth+'px';
33831         //tipBody.dom.style.width = '';
33832         tipBodyText.update(o.text);
33833         var p = getPad(), w = ce.width;
33834         if(!w){
33835             var td = tipBodyText.dom;
33836             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33837             if(aw > tm.maxWidth){
33838                 w = tm.maxWidth;
33839             }else if(aw < tm.minWidth){
33840                 w = tm.minWidth;
33841             }else{
33842                 w = aw;
33843             }
33844         }
33845         //tipBody.setWidth(w);
33846         el.setWidth(parseInt(w, 10) + p);
33847         if(ce.autoHide === false){
33848             close.setDisplayed(true);
33849             if(dd){
33850                 dd.unlock();
33851             }
33852         }else{
33853             close.setDisplayed(false);
33854             if(dd){
33855                 dd.lock();
33856             }
33857         }
33858         if(xy){
33859             el.avoidY = xy[1]-18;
33860             el.setXY(xy);
33861         }
33862         if(tm.animate){
33863             el.setOpacity(.1);
33864             el.setStyle("visibility", "visible");
33865             el.fadeIn({callback: afterShow});
33866         }else{
33867             afterShow();
33868         }
33869     };
33870     
33871     var afterShow = function(){
33872         if(ce){
33873             el.show();
33874             esc.enable();
33875             if(tm.autoDismiss && ce.autoHide !== false){
33876                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33877             }
33878         }
33879     };
33880     
33881     var hide = function(noanim){
33882         clearTimeout(dismissProc);
33883         clearTimeout(hideProc);
33884         ce = null;
33885         if(el.isVisible()){
33886             esc.disable();
33887             if(noanim !== true && tm.animate){
33888                 el.fadeOut({callback: afterHide});
33889             }else{
33890                 afterHide();
33891             } 
33892         }
33893     };
33894     
33895     var afterHide = function(){
33896         el.hide();
33897         if(removeCls){
33898             el.removeClass(removeCls);
33899             removeCls = null;
33900         }
33901     };
33902     
33903     return {
33904         /**
33905         * @cfg {Number} minWidth
33906         * The minimum width of the quick tip (defaults to 40)
33907         */
33908        minWidth : 40,
33909         /**
33910         * @cfg {Number} maxWidth
33911         * The maximum width of the quick tip (defaults to 300)
33912         */
33913        maxWidth : 300,
33914         /**
33915         * @cfg {Boolean} interceptTitles
33916         * True to automatically use the element's DOM title value if available (defaults to false)
33917         */
33918        interceptTitles : false,
33919         /**
33920         * @cfg {Boolean} trackMouse
33921         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33922         */
33923        trackMouse : false,
33924         /**
33925         * @cfg {Boolean} hideOnClick
33926         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33927         */
33928        hideOnClick : true,
33929         /**
33930         * @cfg {Number} showDelay
33931         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33932         */
33933        showDelay : 500,
33934         /**
33935         * @cfg {Number} hideDelay
33936         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33937         */
33938        hideDelay : 200,
33939         /**
33940         * @cfg {Boolean} autoHide
33941         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33942         * Used in conjunction with hideDelay.
33943         */
33944        autoHide : true,
33945         /**
33946         * @cfg {Boolean}
33947         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33948         * (defaults to true).  Used in conjunction with autoDismissDelay.
33949         */
33950        autoDismiss : true,
33951         /**
33952         * @cfg {Number}
33953         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33954         */
33955        autoDismissDelay : 5000,
33956        /**
33957         * @cfg {Boolean} animate
33958         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33959         */
33960        animate : false,
33961
33962        /**
33963         * @cfg {String} title
33964         * Title text to display (defaults to '').  This can be any valid HTML markup.
33965         */
33966         title: '',
33967        /**
33968         * @cfg {String} text
33969         * Body text to display (defaults to '').  This can be any valid HTML markup.
33970         */
33971         text : '',
33972        /**
33973         * @cfg {String} cls
33974         * A CSS class to apply to the base quick tip element (defaults to '').
33975         */
33976         cls : '',
33977        /**
33978         * @cfg {Number} width
33979         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33980         * minWidth or maxWidth.
33981         */
33982         width : null,
33983
33984     /**
33985      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33986      * or display QuickTips in a page.
33987      */
33988        init : function(){
33989           tm = Roo.QuickTips;
33990           cfg = tm.tagConfig;
33991           if(!inited){
33992               if(!Roo.isReady){ // allow calling of init() before onReady
33993                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33994                   return;
33995               }
33996               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33997               el.fxDefaults = {stopFx: true};
33998               // maximum custom styling
33999               //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>');
34000               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>');              
34001               tipTitle = el.child('h3');
34002               tipTitle.enableDisplayMode("block");
34003               tipBody = el.child('div.x-tip-bd');
34004               tipBodyText = el.child('div.x-tip-bd-inner');
34005               //bdLeft = el.child('div.x-tip-bd-left');
34006               //bdRight = el.child('div.x-tip-bd-right');
34007               close = el.child('div.x-tip-close');
34008               close.enableDisplayMode("block");
34009               close.on("click", hide);
34010               var d = Roo.get(document);
34011               d.on("mousedown", onDown);
34012               d.on("mouseover", onOver);
34013               d.on("mouseout", onOut);
34014               d.on("mousemove", onMove);
34015               esc = d.addKeyListener(27, hide);
34016               esc.disable();
34017               if(Roo.dd.DD){
34018                   dd = el.initDD("default", null, {
34019                       onDrag : function(){
34020                           el.sync();  
34021                       }
34022                   });
34023                   dd.setHandleElId(tipTitle.id);
34024                   dd.lock();
34025               }
34026               inited = true;
34027           }
34028           this.enable(); 
34029        },
34030
34031     /**
34032      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34033      * are supported:
34034      * <pre>
34035 Property    Type                   Description
34036 ----------  ---------------------  ------------------------------------------------------------------------
34037 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34038      * </ul>
34039      * @param {Object} config The config object
34040      */
34041        register : function(config){
34042            var cs = config instanceof Array ? config : arguments;
34043            for(var i = 0, len = cs.length; i < len; i++) {
34044                var c = cs[i];
34045                var target = c.target;
34046                if(target){
34047                    if(target instanceof Array){
34048                        for(var j = 0, jlen = target.length; j < jlen; j++){
34049                            tagEls[target[j]] = c;
34050                        }
34051                    }else{
34052                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34053                    }
34054                }
34055            }
34056        },
34057
34058     /**
34059      * Removes this quick tip from its element and destroys it.
34060      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34061      */
34062        unregister : function(el){
34063            delete tagEls[Roo.id(el)];
34064        },
34065
34066     /**
34067      * Enable this quick tip.
34068      */
34069        enable : function(){
34070            if(inited && disabled){
34071                locks.pop();
34072                if(locks.length < 1){
34073                    disabled = false;
34074                }
34075            }
34076        },
34077
34078     /**
34079      * Disable this quick tip.
34080      */
34081        disable : function(){
34082           disabled = true;
34083           clearTimeout(showProc);
34084           clearTimeout(hideProc);
34085           clearTimeout(dismissProc);
34086           if(ce){
34087               hide(true);
34088           }
34089           locks.push(1);
34090        },
34091
34092     /**
34093      * Returns true if the quick tip is enabled, else false.
34094      */
34095        isEnabled : function(){
34096             return !disabled;
34097        },
34098
34099         // private
34100        tagConfig : {
34101            namespace : "roo", // was ext?? this may break..
34102            alt_namespace : "ext",
34103            attribute : "qtip",
34104            width : "width",
34105            target : "target",
34106            title : "qtitle",
34107            hide : "hide",
34108            cls : "qclass"
34109        }
34110    };
34111 }();
34112
34113 // backwards compat
34114 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34115  * Based on:
34116  * Ext JS Library 1.1.1
34117  * Copyright(c) 2006-2007, Ext JS, LLC.
34118  *
34119  * Originally Released Under LGPL - original licence link has changed is not relivant.
34120  *
34121  * Fork - LGPL
34122  * <script type="text/javascript">
34123  */
34124  
34125
34126 /**
34127  * @class Roo.tree.TreePanel
34128  * @extends Roo.data.Tree
34129
34130  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34131  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34132  * @cfg {Boolean} enableDD true to enable drag and drop
34133  * @cfg {Boolean} enableDrag true to enable just drag
34134  * @cfg {Boolean} enableDrop true to enable just drop
34135  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34136  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34137  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34138  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34139  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34140  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34141  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34142  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34143  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34144  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34145  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34146  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34147  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34148  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34149  * @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>
34150  * @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>
34151  * 
34152  * @constructor
34153  * @param {String/HTMLElement/Element} el The container element
34154  * @param {Object} config
34155  */
34156 Roo.tree.TreePanel = function(el, config){
34157     var root = false;
34158     var loader = false;
34159     if (config.root) {
34160         root = config.root;
34161         delete config.root;
34162     }
34163     if (config.loader) {
34164         loader = config.loader;
34165         delete config.loader;
34166     }
34167     
34168     Roo.apply(this, config);
34169     Roo.tree.TreePanel.superclass.constructor.call(this);
34170     this.el = Roo.get(el);
34171     this.el.addClass('x-tree');
34172     //console.log(root);
34173     if (root) {
34174         this.setRootNode( Roo.factory(root, Roo.tree));
34175     }
34176     if (loader) {
34177         this.loader = Roo.factory(loader, Roo.tree);
34178     }
34179    /**
34180     * Read-only. The id of the container element becomes this TreePanel's id.
34181     */
34182     this.id = this.el.id;
34183     this.addEvents({
34184         /**
34185         * @event beforeload
34186         * Fires before a node is loaded, return false to cancel
34187         * @param {Node} node The node being loaded
34188         */
34189         "beforeload" : true,
34190         /**
34191         * @event load
34192         * Fires when a node is loaded
34193         * @param {Node} node The node that was loaded
34194         */
34195         "load" : true,
34196         /**
34197         * @event textchange
34198         * Fires when the text for a node is changed
34199         * @param {Node} node The node
34200         * @param {String} text The new text
34201         * @param {String} oldText The old text
34202         */
34203         "textchange" : true,
34204         /**
34205         * @event beforeexpand
34206         * Fires before a node is expanded, return false to cancel.
34207         * @param {Node} node The node
34208         * @param {Boolean} deep
34209         * @param {Boolean} anim
34210         */
34211         "beforeexpand" : true,
34212         /**
34213         * @event beforecollapse
34214         * Fires before a node is collapsed, return false to cancel.
34215         * @param {Node} node The node
34216         * @param {Boolean} deep
34217         * @param {Boolean} anim
34218         */
34219         "beforecollapse" : true,
34220         /**
34221         * @event expand
34222         * Fires when a node is expanded
34223         * @param {Node} node The node
34224         */
34225         "expand" : true,
34226         /**
34227         * @event disabledchange
34228         * Fires when the disabled status of a node changes
34229         * @param {Node} node The node
34230         * @param {Boolean} disabled
34231         */
34232         "disabledchange" : true,
34233         /**
34234         * @event collapse
34235         * Fires when a node is collapsed
34236         * @param {Node} node The node
34237         */
34238         "collapse" : true,
34239         /**
34240         * @event beforeclick
34241         * Fires before click processing on a node. Return false to cancel the default action.
34242         * @param {Node} node The node
34243         * @param {Roo.EventObject} e The event object
34244         */
34245         "beforeclick":true,
34246         /**
34247         * @event checkchange
34248         * Fires when a node with a checkbox's checked property changes
34249         * @param {Node} this This node
34250         * @param {Boolean} checked
34251         */
34252         "checkchange":true,
34253         /**
34254         * @event click
34255         * Fires when a node is clicked
34256         * @param {Node} node The node
34257         * @param {Roo.EventObject} e The event object
34258         */
34259         "click":true,
34260         /**
34261         * @event dblclick
34262         * Fires when a node is double clicked
34263         * @param {Node} node The node
34264         * @param {Roo.EventObject} e The event object
34265         */
34266         "dblclick":true,
34267         /**
34268         * @event contextmenu
34269         * Fires when a node is right clicked
34270         * @param {Node} node The node
34271         * @param {Roo.EventObject} e The event object
34272         */
34273         "contextmenu":true,
34274         /**
34275         * @event beforechildrenrendered
34276         * Fires right before the child nodes for a node are rendered
34277         * @param {Node} node The node
34278         */
34279         "beforechildrenrendered":true,
34280         /**
34281         * @event startdrag
34282         * Fires when a node starts being dragged
34283         * @param {Roo.tree.TreePanel} this
34284         * @param {Roo.tree.TreeNode} node
34285         * @param {event} e The raw browser event
34286         */ 
34287        "startdrag" : true,
34288        /**
34289         * @event enddrag
34290         * Fires when a drag operation is complete
34291         * @param {Roo.tree.TreePanel} this
34292         * @param {Roo.tree.TreeNode} node
34293         * @param {event} e The raw browser event
34294         */
34295        "enddrag" : true,
34296        /**
34297         * @event dragdrop
34298         * Fires when a dragged node is dropped on a valid DD target
34299         * @param {Roo.tree.TreePanel} this
34300         * @param {Roo.tree.TreeNode} node
34301         * @param {DD} dd The dd it was dropped on
34302         * @param {event} e The raw browser event
34303         */
34304        "dragdrop" : true,
34305        /**
34306         * @event beforenodedrop
34307         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34308         * passed to handlers has the following properties:<br />
34309         * <ul style="padding:5px;padding-left:16px;">
34310         * <li>tree - The TreePanel</li>
34311         * <li>target - The node being targeted for the drop</li>
34312         * <li>data - The drag data from the drag source</li>
34313         * <li>point - The point of the drop - append, above or below</li>
34314         * <li>source - The drag source</li>
34315         * <li>rawEvent - Raw mouse event</li>
34316         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34317         * to be inserted by setting them on this object.</li>
34318         * <li>cancel - Set this to true to cancel the drop.</li>
34319         * </ul>
34320         * @param {Object} dropEvent
34321         */
34322        "beforenodedrop" : true,
34323        /**
34324         * @event nodedrop
34325         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34326         * passed to handlers has the following properties:<br />
34327         * <ul style="padding:5px;padding-left:16px;">
34328         * <li>tree - The TreePanel</li>
34329         * <li>target - The node being targeted for the drop</li>
34330         * <li>data - The drag data from the drag source</li>
34331         * <li>point - The point of the drop - append, above or below</li>
34332         * <li>source - The drag source</li>
34333         * <li>rawEvent - Raw mouse event</li>
34334         * <li>dropNode - Dropped node(s).</li>
34335         * </ul>
34336         * @param {Object} dropEvent
34337         */
34338        "nodedrop" : true,
34339         /**
34340         * @event nodedragover
34341         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34342         * passed to handlers has the following properties:<br />
34343         * <ul style="padding:5px;padding-left:16px;">
34344         * <li>tree - The TreePanel</li>
34345         * <li>target - The node being targeted for the drop</li>
34346         * <li>data - The drag data from the drag source</li>
34347         * <li>point - The point of the drop - append, above or below</li>
34348         * <li>source - The drag source</li>
34349         * <li>rawEvent - Raw mouse event</li>
34350         * <li>dropNode - Drop node(s) provided by the source.</li>
34351         * <li>cancel - Set this to true to signal drop not allowed.</li>
34352         * </ul>
34353         * @param {Object} dragOverEvent
34354         */
34355        "nodedragover" : true,
34356        /**
34357         * @event appendnode
34358         * Fires when append node to the tree
34359         * @param {Roo.tree.TreePanel} this
34360         * @param {Roo.tree.TreeNode} node
34361         * @param {Number} index The index of the newly appended node
34362         */
34363        "appendnode" : true
34364         
34365     });
34366     if(this.singleExpand){
34367        this.on("beforeexpand", this.restrictExpand, this);
34368     }
34369     if (this.editor) {
34370         this.editor.tree = this;
34371         this.editor = Roo.factory(this.editor, Roo.tree);
34372     }
34373     
34374     if (this.selModel) {
34375         this.selModel = Roo.factory(this.selModel, Roo.tree);
34376     }
34377    
34378 };
34379 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34380     rootVisible : true,
34381     animate: Roo.enableFx,
34382     lines : true,
34383     enableDD : false,
34384     hlDrop : Roo.enableFx,
34385   
34386     renderer: false,
34387     
34388     rendererTip: false,
34389     // private
34390     restrictExpand : function(node){
34391         var p = node.parentNode;
34392         if(p){
34393             if(p.expandedChild && p.expandedChild.parentNode == p){
34394                 p.expandedChild.collapse();
34395             }
34396             p.expandedChild = node;
34397         }
34398     },
34399
34400     // private override
34401     setRootNode : function(node){
34402         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34403         if(!this.rootVisible){
34404             node.ui = new Roo.tree.RootTreeNodeUI(node);
34405         }
34406         return node;
34407     },
34408
34409     /**
34410      * Returns the container element for this TreePanel
34411      */
34412     getEl : function(){
34413         return this.el;
34414     },
34415
34416     /**
34417      * Returns the default TreeLoader for this TreePanel
34418      */
34419     getLoader : function(){
34420         return this.loader;
34421     },
34422
34423     /**
34424      * Expand all nodes
34425      */
34426     expandAll : function(){
34427         this.root.expand(true);
34428     },
34429
34430     /**
34431      * Collapse all nodes
34432      */
34433     collapseAll : function(){
34434         this.root.collapse(true);
34435     },
34436
34437     /**
34438      * Returns the selection model used by this TreePanel
34439      */
34440     getSelectionModel : function(){
34441         if(!this.selModel){
34442             this.selModel = new Roo.tree.DefaultSelectionModel();
34443         }
34444         return this.selModel;
34445     },
34446
34447     /**
34448      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34449      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34450      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34451      * @return {Array}
34452      */
34453     getChecked : function(a, startNode){
34454         startNode = startNode || this.root;
34455         var r = [];
34456         var f = function(){
34457             if(this.attributes.checked){
34458                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34459             }
34460         }
34461         startNode.cascade(f);
34462         return r;
34463     },
34464
34465     /**
34466      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34467      * @param {String} path
34468      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34469      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34470      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34471      */
34472     expandPath : function(path, attr, callback){
34473         attr = attr || "id";
34474         var keys = path.split(this.pathSeparator);
34475         var curNode = this.root;
34476         if(curNode.attributes[attr] != keys[1]){ // invalid root
34477             if(callback){
34478                 callback(false, null);
34479             }
34480             return;
34481         }
34482         var index = 1;
34483         var f = function(){
34484             if(++index == keys.length){
34485                 if(callback){
34486                     callback(true, curNode);
34487                 }
34488                 return;
34489             }
34490             var c = curNode.findChild(attr, keys[index]);
34491             if(!c){
34492                 if(callback){
34493                     callback(false, curNode);
34494                 }
34495                 return;
34496             }
34497             curNode = c;
34498             c.expand(false, false, f);
34499         };
34500         curNode.expand(false, false, f);
34501     },
34502
34503     /**
34504      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34505      * @param {String} path
34506      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34507      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34508      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34509      */
34510     selectPath : function(path, attr, callback){
34511         attr = attr || "id";
34512         var keys = path.split(this.pathSeparator);
34513         var v = keys.pop();
34514         if(keys.length > 0){
34515             var f = function(success, node){
34516                 if(success && node){
34517                     var n = node.findChild(attr, v);
34518                     if(n){
34519                         n.select();
34520                         if(callback){
34521                             callback(true, n);
34522                         }
34523                     }else if(callback){
34524                         callback(false, n);
34525                     }
34526                 }else{
34527                     if(callback){
34528                         callback(false, n);
34529                     }
34530                 }
34531             };
34532             this.expandPath(keys.join(this.pathSeparator), attr, f);
34533         }else{
34534             this.root.select();
34535             if(callback){
34536                 callback(true, this.root);
34537             }
34538         }
34539     },
34540
34541     getTreeEl : function(){
34542         return this.el;
34543     },
34544
34545     /**
34546      * Trigger rendering of this TreePanel
34547      */
34548     render : function(){
34549         if (this.innerCt) {
34550             return this; // stop it rendering more than once!!
34551         }
34552         
34553         this.innerCt = this.el.createChild({tag:"ul",
34554                cls:"x-tree-root-ct " +
34555                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34556
34557         if(this.containerScroll){
34558             Roo.dd.ScrollManager.register(this.el);
34559         }
34560         if((this.enableDD || this.enableDrop) && !this.dropZone){
34561            /**
34562             * The dropZone used by this tree if drop is enabled
34563             * @type Roo.tree.TreeDropZone
34564             */
34565              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34566                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34567            });
34568         }
34569         if((this.enableDD || this.enableDrag) && !this.dragZone){
34570            /**
34571             * The dragZone used by this tree if drag is enabled
34572             * @type Roo.tree.TreeDragZone
34573             */
34574             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34575                ddGroup: this.ddGroup || "TreeDD",
34576                scroll: this.ddScroll
34577            });
34578         }
34579         this.getSelectionModel().init(this);
34580         if (!this.root) {
34581             Roo.log("ROOT not set in tree");
34582             return this;
34583         }
34584         this.root.render();
34585         if(!this.rootVisible){
34586             this.root.renderChildren();
34587         }
34588         return this;
34589     }
34590 });/*
34591  * Based on:
34592  * Ext JS Library 1.1.1
34593  * Copyright(c) 2006-2007, Ext JS, LLC.
34594  *
34595  * Originally Released Under LGPL - original licence link has changed is not relivant.
34596  *
34597  * Fork - LGPL
34598  * <script type="text/javascript">
34599  */
34600  
34601
34602 /**
34603  * @class Roo.tree.DefaultSelectionModel
34604  * @extends Roo.util.Observable
34605  * The default single selection for a TreePanel.
34606  * @param {Object} cfg Configuration
34607  */
34608 Roo.tree.DefaultSelectionModel = function(cfg){
34609    this.selNode = null;
34610    
34611    
34612    
34613    this.addEvents({
34614        /**
34615         * @event selectionchange
34616         * Fires when the selected node changes
34617         * @param {DefaultSelectionModel} this
34618         * @param {TreeNode} node the new selection
34619         */
34620        "selectionchange" : true,
34621
34622        /**
34623         * @event beforeselect
34624         * Fires before the selected node changes, return false to cancel the change
34625         * @param {DefaultSelectionModel} this
34626         * @param {TreeNode} node the new selection
34627         * @param {TreeNode} node the old selection
34628         */
34629        "beforeselect" : true
34630    });
34631    
34632     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34633 };
34634
34635 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34636     init : function(tree){
34637         this.tree = tree;
34638         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34639         tree.on("click", this.onNodeClick, this);
34640     },
34641     
34642     onNodeClick : function(node, e){
34643         if (e.ctrlKey && this.selNode == node)  {
34644             this.unselect(node);
34645             return;
34646         }
34647         this.select(node);
34648     },
34649     
34650     /**
34651      * Select a node.
34652      * @param {TreeNode} node The node to select
34653      * @return {TreeNode} The selected node
34654      */
34655     select : function(node){
34656         var last = this.selNode;
34657         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34658             if(last){
34659                 last.ui.onSelectedChange(false);
34660             }
34661             this.selNode = node;
34662             node.ui.onSelectedChange(true);
34663             this.fireEvent("selectionchange", this, node, last);
34664         }
34665         return node;
34666     },
34667     
34668     /**
34669      * Deselect a node.
34670      * @param {TreeNode} node The node to unselect
34671      */
34672     unselect : function(node){
34673         if(this.selNode == node){
34674             this.clearSelections();
34675         }    
34676     },
34677     
34678     /**
34679      * Clear all selections
34680      */
34681     clearSelections : function(){
34682         var n = this.selNode;
34683         if(n){
34684             n.ui.onSelectedChange(false);
34685             this.selNode = null;
34686             this.fireEvent("selectionchange", this, null);
34687         }
34688         return n;
34689     },
34690     
34691     /**
34692      * Get the selected node
34693      * @return {TreeNode} The selected node
34694      */
34695     getSelectedNode : function(){
34696         return this.selNode;    
34697     },
34698     
34699     /**
34700      * Returns true if the node is selected
34701      * @param {TreeNode} node The node to check
34702      * @return {Boolean}
34703      */
34704     isSelected : function(node){
34705         return this.selNode == node;  
34706     },
34707
34708     /**
34709      * Selects the node above the selected node in the tree, intelligently walking the nodes
34710      * @return TreeNode The new selection
34711      */
34712     selectPrevious : function(){
34713         var s = this.selNode || this.lastSelNode;
34714         if(!s){
34715             return null;
34716         }
34717         var ps = s.previousSibling;
34718         if(ps){
34719             if(!ps.isExpanded() || ps.childNodes.length < 1){
34720                 return this.select(ps);
34721             } else{
34722                 var lc = ps.lastChild;
34723                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34724                     lc = lc.lastChild;
34725                 }
34726                 return this.select(lc);
34727             }
34728         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34729             return this.select(s.parentNode);
34730         }
34731         return null;
34732     },
34733
34734     /**
34735      * Selects the node above the selected node in the tree, intelligently walking the nodes
34736      * @return TreeNode The new selection
34737      */
34738     selectNext : function(){
34739         var s = this.selNode || this.lastSelNode;
34740         if(!s){
34741             return null;
34742         }
34743         if(s.firstChild && s.isExpanded()){
34744              return this.select(s.firstChild);
34745          }else if(s.nextSibling){
34746              return this.select(s.nextSibling);
34747          }else if(s.parentNode){
34748             var newS = null;
34749             s.parentNode.bubble(function(){
34750                 if(this.nextSibling){
34751                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34752                     return false;
34753                 }
34754             });
34755             return newS;
34756          }
34757         return null;
34758     },
34759
34760     onKeyDown : function(e){
34761         var s = this.selNode || this.lastSelNode;
34762         // undesirable, but required
34763         var sm = this;
34764         if(!s){
34765             return;
34766         }
34767         var k = e.getKey();
34768         switch(k){
34769              case e.DOWN:
34770                  e.stopEvent();
34771                  this.selectNext();
34772              break;
34773              case e.UP:
34774                  e.stopEvent();
34775                  this.selectPrevious();
34776              break;
34777              case e.RIGHT:
34778                  e.preventDefault();
34779                  if(s.hasChildNodes()){
34780                      if(!s.isExpanded()){
34781                          s.expand();
34782                      }else if(s.firstChild){
34783                          this.select(s.firstChild, e);
34784                      }
34785                  }
34786              break;
34787              case e.LEFT:
34788                  e.preventDefault();
34789                  if(s.hasChildNodes() && s.isExpanded()){
34790                      s.collapse();
34791                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34792                      this.select(s.parentNode, e);
34793                  }
34794              break;
34795         };
34796     }
34797 });
34798
34799 /**
34800  * @class Roo.tree.MultiSelectionModel
34801  * @extends Roo.util.Observable
34802  * Multi selection for a TreePanel.
34803  * @param {Object} cfg Configuration
34804  */
34805 Roo.tree.MultiSelectionModel = function(){
34806    this.selNodes = [];
34807    this.selMap = {};
34808    this.addEvents({
34809        /**
34810         * @event selectionchange
34811         * Fires when the selected nodes change
34812         * @param {MultiSelectionModel} this
34813         * @param {Array} nodes Array of the selected nodes
34814         */
34815        "selectionchange" : true
34816    });
34817    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34818    
34819 };
34820
34821 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34822     init : function(tree){
34823         this.tree = tree;
34824         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34825         tree.on("click", this.onNodeClick, this);
34826     },
34827     
34828     onNodeClick : function(node, e){
34829         this.select(node, e, e.ctrlKey);
34830     },
34831     
34832     /**
34833      * Select a node.
34834      * @param {TreeNode} node The node to select
34835      * @param {EventObject} e (optional) An event associated with the selection
34836      * @param {Boolean} keepExisting True to retain existing selections
34837      * @return {TreeNode} The selected node
34838      */
34839     select : function(node, e, keepExisting){
34840         if(keepExisting !== true){
34841             this.clearSelections(true);
34842         }
34843         if(this.isSelected(node)){
34844             this.lastSelNode = node;
34845             return node;
34846         }
34847         this.selNodes.push(node);
34848         this.selMap[node.id] = node;
34849         this.lastSelNode = node;
34850         node.ui.onSelectedChange(true);
34851         this.fireEvent("selectionchange", this, this.selNodes);
34852         return node;
34853     },
34854     
34855     /**
34856      * Deselect a node.
34857      * @param {TreeNode} node The node to unselect
34858      */
34859     unselect : function(node){
34860         if(this.selMap[node.id]){
34861             node.ui.onSelectedChange(false);
34862             var sn = this.selNodes;
34863             var index = -1;
34864             if(sn.indexOf){
34865                 index = sn.indexOf(node);
34866             }else{
34867                 for(var i = 0, len = sn.length; i < len; i++){
34868                     if(sn[i] == node){
34869                         index = i;
34870                         break;
34871                     }
34872                 }
34873             }
34874             if(index != -1){
34875                 this.selNodes.splice(index, 1);
34876             }
34877             delete this.selMap[node.id];
34878             this.fireEvent("selectionchange", this, this.selNodes);
34879         }
34880     },
34881     
34882     /**
34883      * Clear all selections
34884      */
34885     clearSelections : function(suppressEvent){
34886         var sn = this.selNodes;
34887         if(sn.length > 0){
34888             for(var i = 0, len = sn.length; i < len; i++){
34889                 sn[i].ui.onSelectedChange(false);
34890             }
34891             this.selNodes = [];
34892             this.selMap = {};
34893             if(suppressEvent !== true){
34894                 this.fireEvent("selectionchange", this, this.selNodes);
34895             }
34896         }
34897     },
34898     
34899     /**
34900      * Returns true if the node is selected
34901      * @param {TreeNode} node The node to check
34902      * @return {Boolean}
34903      */
34904     isSelected : function(node){
34905         return this.selMap[node.id] ? true : false;  
34906     },
34907     
34908     /**
34909      * Returns an array of the selected nodes
34910      * @return {Array}
34911      */
34912     getSelectedNodes : function(){
34913         return this.selNodes;    
34914     },
34915
34916     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34917
34918     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34919
34920     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34921 });/*
34922  * Based on:
34923  * Ext JS Library 1.1.1
34924  * Copyright(c) 2006-2007, Ext JS, LLC.
34925  *
34926  * Originally Released Under LGPL - original licence link has changed is not relivant.
34927  *
34928  * Fork - LGPL
34929  * <script type="text/javascript">
34930  */
34931  
34932 /**
34933  * @class Roo.tree.TreeNode
34934  * @extends Roo.data.Node
34935  * @cfg {String} text The text for this node
34936  * @cfg {Boolean} expanded true to start the node expanded
34937  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34938  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34939  * @cfg {Boolean} disabled true to start the node disabled
34940  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34941  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34942  * @cfg {String} cls A css class to be added to the node
34943  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34944  * @cfg {String} href URL of the link used for the node (defaults to #)
34945  * @cfg {String} hrefTarget target frame for the link
34946  * @cfg {String} qtip An Ext QuickTip for the node
34947  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34948  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34949  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34950  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34951  * (defaults to undefined with no checkbox rendered)
34952  * @constructor
34953  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34954  */
34955 Roo.tree.TreeNode = function(attributes){
34956     attributes = attributes || {};
34957     if(typeof attributes == "string"){
34958         attributes = {text: attributes};
34959     }
34960     this.childrenRendered = false;
34961     this.rendered = false;
34962     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34963     this.expanded = attributes.expanded === true;
34964     this.isTarget = attributes.isTarget !== false;
34965     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34966     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34967
34968     /**
34969      * Read-only. The text for this node. To change it use setText().
34970      * @type String
34971      */
34972     this.text = attributes.text;
34973     /**
34974      * True if this node is disabled.
34975      * @type Boolean
34976      */
34977     this.disabled = attributes.disabled === true;
34978
34979     this.addEvents({
34980         /**
34981         * @event textchange
34982         * Fires when the text for this node is changed
34983         * @param {Node} this This node
34984         * @param {String} text The new text
34985         * @param {String} oldText The old text
34986         */
34987         "textchange" : true,
34988         /**
34989         * @event beforeexpand
34990         * Fires before this node is expanded, return false to cancel.
34991         * @param {Node} this This node
34992         * @param {Boolean} deep
34993         * @param {Boolean} anim
34994         */
34995         "beforeexpand" : true,
34996         /**
34997         * @event beforecollapse
34998         * Fires before this node is collapsed, return false to cancel.
34999         * @param {Node} this This node
35000         * @param {Boolean} deep
35001         * @param {Boolean} anim
35002         */
35003         "beforecollapse" : true,
35004         /**
35005         * @event expand
35006         * Fires when this node is expanded
35007         * @param {Node} this This node
35008         */
35009         "expand" : true,
35010         /**
35011         * @event disabledchange
35012         * Fires when the disabled status of this node changes
35013         * @param {Node} this This node
35014         * @param {Boolean} disabled
35015         */
35016         "disabledchange" : true,
35017         /**
35018         * @event collapse
35019         * Fires when this node is collapsed
35020         * @param {Node} this This node
35021         */
35022         "collapse" : true,
35023         /**
35024         * @event beforeclick
35025         * Fires before click processing. Return false to cancel the default action.
35026         * @param {Node} this This node
35027         * @param {Roo.EventObject} e The event object
35028         */
35029         "beforeclick":true,
35030         /**
35031         * @event checkchange
35032         * Fires when a node with a checkbox's checked property changes
35033         * @param {Node} this This node
35034         * @param {Boolean} checked
35035         */
35036         "checkchange":true,
35037         /**
35038         * @event click
35039         * Fires when this node is clicked
35040         * @param {Node} this This node
35041         * @param {Roo.EventObject} e The event object
35042         */
35043         "click":true,
35044         /**
35045         * @event dblclick
35046         * Fires when this node is double clicked
35047         * @param {Node} this This node
35048         * @param {Roo.EventObject} e The event object
35049         */
35050         "dblclick":true,
35051         /**
35052         * @event contextmenu
35053         * Fires when this node is right clicked
35054         * @param {Node} this This node
35055         * @param {Roo.EventObject} e The event object
35056         */
35057         "contextmenu":true,
35058         /**
35059         * @event beforechildrenrendered
35060         * Fires right before the child nodes for this node are rendered
35061         * @param {Node} this This node
35062         */
35063         "beforechildrenrendered":true
35064     });
35065
35066     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35067
35068     /**
35069      * Read-only. The UI for this node
35070      * @type TreeNodeUI
35071      */
35072     this.ui = new uiClass(this);
35073     
35074     // finally support items[]
35075     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35076         return;
35077     }
35078     
35079     
35080     Roo.each(this.attributes.items, function(c) {
35081         this.appendChild(Roo.factory(c,Roo.Tree));
35082     }, this);
35083     delete this.attributes.items;
35084     
35085     
35086     
35087 };
35088 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35089     preventHScroll: true,
35090     /**
35091      * Returns true if this node is expanded
35092      * @return {Boolean}
35093      */
35094     isExpanded : function(){
35095         return this.expanded;
35096     },
35097
35098     /**
35099      * Returns the UI object for this node
35100      * @return {TreeNodeUI}
35101      */
35102     getUI : function(){
35103         return this.ui;
35104     },
35105
35106     // private override
35107     setFirstChild : function(node){
35108         var of = this.firstChild;
35109         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35110         if(this.childrenRendered && of && node != of){
35111             of.renderIndent(true, true);
35112         }
35113         if(this.rendered){
35114             this.renderIndent(true, true);
35115         }
35116     },
35117
35118     // private override
35119     setLastChild : function(node){
35120         var ol = this.lastChild;
35121         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35122         if(this.childrenRendered && ol && node != ol){
35123             ol.renderIndent(true, true);
35124         }
35125         if(this.rendered){
35126             this.renderIndent(true, true);
35127         }
35128     },
35129
35130     // these methods are overridden to provide lazy rendering support
35131     // private override
35132     appendChild : function()
35133     {
35134         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35135         if(node && this.childrenRendered){
35136             node.render();
35137         }
35138         this.ui.updateExpandIcon();
35139         return node;
35140     },
35141
35142     // private override
35143     removeChild : function(node){
35144         this.ownerTree.getSelectionModel().unselect(node);
35145         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35146         // if it's been rendered remove dom node
35147         if(this.childrenRendered){
35148             node.ui.remove();
35149         }
35150         if(this.childNodes.length < 1){
35151             this.collapse(false, false);
35152         }else{
35153             this.ui.updateExpandIcon();
35154         }
35155         if(!this.firstChild) {
35156             this.childrenRendered = false;
35157         }
35158         return node;
35159     },
35160
35161     // private override
35162     insertBefore : function(node, refNode){
35163         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35164         if(newNode && refNode && this.childrenRendered){
35165             node.render();
35166         }
35167         this.ui.updateExpandIcon();
35168         return newNode;
35169     },
35170
35171     /**
35172      * Sets the text for this node
35173      * @param {String} text
35174      */
35175     setText : function(text){
35176         var oldText = this.text;
35177         this.text = text;
35178         this.attributes.text = text;
35179         if(this.rendered){ // event without subscribing
35180             this.ui.onTextChange(this, text, oldText);
35181         }
35182         this.fireEvent("textchange", this, text, oldText);
35183     },
35184
35185     /**
35186      * Triggers selection of this node
35187      */
35188     select : function(){
35189         this.getOwnerTree().getSelectionModel().select(this);
35190     },
35191
35192     /**
35193      * Triggers deselection of this node
35194      */
35195     unselect : function(){
35196         this.getOwnerTree().getSelectionModel().unselect(this);
35197     },
35198
35199     /**
35200      * Returns true if this node is selected
35201      * @return {Boolean}
35202      */
35203     isSelected : function(){
35204         return this.getOwnerTree().getSelectionModel().isSelected(this);
35205     },
35206
35207     /**
35208      * Expand this node.
35209      * @param {Boolean} deep (optional) True to expand all children as well
35210      * @param {Boolean} anim (optional) false to cancel the default animation
35211      * @param {Function} callback (optional) A callback to be called when
35212      * expanding this node completes (does not wait for deep expand to complete).
35213      * Called with 1 parameter, this node.
35214      */
35215     expand : function(deep, anim, callback){
35216         if(!this.expanded){
35217             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35218                 return;
35219             }
35220             if(!this.childrenRendered){
35221                 this.renderChildren();
35222             }
35223             this.expanded = true;
35224             
35225             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35226                 this.ui.animExpand(function(){
35227                     this.fireEvent("expand", this);
35228                     if(typeof callback == "function"){
35229                         callback(this);
35230                     }
35231                     if(deep === true){
35232                         this.expandChildNodes(true);
35233                     }
35234                 }.createDelegate(this));
35235                 return;
35236             }else{
35237                 this.ui.expand();
35238                 this.fireEvent("expand", this);
35239                 if(typeof callback == "function"){
35240                     callback(this);
35241                 }
35242             }
35243         }else{
35244            if(typeof callback == "function"){
35245                callback(this);
35246            }
35247         }
35248         if(deep === true){
35249             this.expandChildNodes(true);
35250         }
35251     },
35252
35253     isHiddenRoot : function(){
35254         return this.isRoot && !this.getOwnerTree().rootVisible;
35255     },
35256
35257     /**
35258      * Collapse this node.
35259      * @param {Boolean} deep (optional) True to collapse all children as well
35260      * @param {Boolean} anim (optional) false to cancel the default animation
35261      */
35262     collapse : function(deep, anim){
35263         if(this.expanded && !this.isHiddenRoot()){
35264             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35265                 return;
35266             }
35267             this.expanded = false;
35268             if((this.getOwnerTree().animate && anim !== false) || anim){
35269                 this.ui.animCollapse(function(){
35270                     this.fireEvent("collapse", this);
35271                     if(deep === true){
35272                         this.collapseChildNodes(true);
35273                     }
35274                 }.createDelegate(this));
35275                 return;
35276             }else{
35277                 this.ui.collapse();
35278                 this.fireEvent("collapse", this);
35279             }
35280         }
35281         if(deep === true){
35282             var cs = this.childNodes;
35283             for(var i = 0, len = cs.length; i < len; i++) {
35284                 cs[i].collapse(true, false);
35285             }
35286         }
35287     },
35288
35289     // private
35290     delayedExpand : function(delay){
35291         if(!this.expandProcId){
35292             this.expandProcId = this.expand.defer(delay, this);
35293         }
35294     },
35295
35296     // private
35297     cancelExpand : function(){
35298         if(this.expandProcId){
35299             clearTimeout(this.expandProcId);
35300         }
35301         this.expandProcId = false;
35302     },
35303
35304     /**
35305      * Toggles expanded/collapsed state of the node
35306      */
35307     toggle : function(){
35308         if(this.expanded){
35309             this.collapse();
35310         }else{
35311             this.expand();
35312         }
35313     },
35314
35315     /**
35316      * Ensures all parent nodes are expanded
35317      */
35318     ensureVisible : function(callback){
35319         var tree = this.getOwnerTree();
35320         tree.expandPath(this.parentNode.getPath(), false, function(){
35321             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35322             Roo.callback(callback);
35323         }.createDelegate(this));
35324     },
35325
35326     /**
35327      * Expand all child nodes
35328      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35329      */
35330     expandChildNodes : function(deep){
35331         var cs = this.childNodes;
35332         for(var i = 0, len = cs.length; i < len; i++) {
35333                 cs[i].expand(deep);
35334         }
35335     },
35336
35337     /**
35338      * Collapse all child nodes
35339      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35340      */
35341     collapseChildNodes : function(deep){
35342         var cs = this.childNodes;
35343         for(var i = 0, len = cs.length; i < len; i++) {
35344                 cs[i].collapse(deep);
35345         }
35346     },
35347
35348     /**
35349      * Disables this node
35350      */
35351     disable : function(){
35352         this.disabled = true;
35353         this.unselect();
35354         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35355             this.ui.onDisableChange(this, true);
35356         }
35357         this.fireEvent("disabledchange", this, true);
35358     },
35359
35360     /**
35361      * Enables this node
35362      */
35363     enable : function(){
35364         this.disabled = false;
35365         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35366             this.ui.onDisableChange(this, false);
35367         }
35368         this.fireEvent("disabledchange", this, false);
35369     },
35370
35371     // private
35372     renderChildren : function(suppressEvent){
35373         if(suppressEvent !== false){
35374             this.fireEvent("beforechildrenrendered", this);
35375         }
35376         var cs = this.childNodes;
35377         for(var i = 0, len = cs.length; i < len; i++){
35378             cs[i].render(true);
35379         }
35380         this.childrenRendered = true;
35381     },
35382
35383     // private
35384     sort : function(fn, scope){
35385         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35386         if(this.childrenRendered){
35387             var cs = this.childNodes;
35388             for(var i = 0, len = cs.length; i < len; i++){
35389                 cs[i].render(true);
35390             }
35391         }
35392     },
35393
35394     // private
35395     render : function(bulkRender){
35396         this.ui.render(bulkRender);
35397         if(!this.rendered){
35398             this.rendered = true;
35399             if(this.expanded){
35400                 this.expanded = false;
35401                 this.expand(false, false);
35402             }
35403         }
35404     },
35405
35406     // private
35407     renderIndent : function(deep, refresh){
35408         if(refresh){
35409             this.ui.childIndent = null;
35410         }
35411         this.ui.renderIndent();
35412         if(deep === true && this.childrenRendered){
35413             var cs = this.childNodes;
35414             for(var i = 0, len = cs.length; i < len; i++){
35415                 cs[i].renderIndent(true, refresh);
35416             }
35417         }
35418     }
35419 });/*
35420  * Based on:
35421  * Ext JS Library 1.1.1
35422  * Copyright(c) 2006-2007, Ext JS, LLC.
35423  *
35424  * Originally Released Under LGPL - original licence link has changed is not relivant.
35425  *
35426  * Fork - LGPL
35427  * <script type="text/javascript">
35428  */
35429  
35430 /**
35431  * @class Roo.tree.AsyncTreeNode
35432  * @extends Roo.tree.TreeNode
35433  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35434  * @constructor
35435  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35436  */
35437  Roo.tree.AsyncTreeNode = function(config){
35438     this.loaded = false;
35439     this.loading = false;
35440     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35441     /**
35442     * @event beforeload
35443     * Fires before this node is loaded, return false to cancel
35444     * @param {Node} this This node
35445     */
35446     this.addEvents({'beforeload':true, 'load': true});
35447     /**
35448     * @event load
35449     * Fires when this node is loaded
35450     * @param {Node} this This node
35451     */
35452     /**
35453      * The loader used by this node (defaults to using the tree's defined loader)
35454      * @type TreeLoader
35455      * @property loader
35456      */
35457 };
35458 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35459     expand : function(deep, anim, callback){
35460         if(this.loading){ // if an async load is already running, waiting til it's done
35461             var timer;
35462             var f = function(){
35463                 if(!this.loading){ // done loading
35464                     clearInterval(timer);
35465                     this.expand(deep, anim, callback);
35466                 }
35467             }.createDelegate(this);
35468             timer = setInterval(f, 200);
35469             return;
35470         }
35471         if(!this.loaded){
35472             if(this.fireEvent("beforeload", this) === false){
35473                 return;
35474             }
35475             this.loading = true;
35476             this.ui.beforeLoad(this);
35477             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35478             if(loader){
35479                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35480                 return;
35481             }
35482         }
35483         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35484     },
35485     
35486     /**
35487      * Returns true if this node is currently loading
35488      * @return {Boolean}
35489      */
35490     isLoading : function(){
35491         return this.loading;  
35492     },
35493     
35494     loadComplete : function(deep, anim, callback){
35495         this.loading = false;
35496         this.loaded = true;
35497         this.ui.afterLoad(this);
35498         this.fireEvent("load", this);
35499         this.expand(deep, anim, callback);
35500     },
35501     
35502     /**
35503      * Returns true if this node has been loaded
35504      * @return {Boolean}
35505      */
35506     isLoaded : function(){
35507         return this.loaded;
35508     },
35509     
35510     hasChildNodes : function(){
35511         if(!this.isLeaf() && !this.loaded){
35512             return true;
35513         }else{
35514             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35515         }
35516     },
35517
35518     /**
35519      * Trigger a reload for this node
35520      * @param {Function} callback
35521      */
35522     reload : function(callback){
35523         this.collapse(false, false);
35524         while(this.firstChild){
35525             this.removeChild(this.firstChild);
35526         }
35527         this.childrenRendered = false;
35528         this.loaded = false;
35529         if(this.isHiddenRoot()){
35530             this.expanded = false;
35531         }
35532         this.expand(false, false, callback);
35533     }
35534 });/*
35535  * Based on:
35536  * Ext JS Library 1.1.1
35537  * Copyright(c) 2006-2007, Ext JS, LLC.
35538  *
35539  * Originally Released Under LGPL - original licence link has changed is not relivant.
35540  *
35541  * Fork - LGPL
35542  * <script type="text/javascript">
35543  */
35544  
35545 /**
35546  * @class Roo.tree.TreeNodeUI
35547  * @constructor
35548  * @param {Object} node The node to render
35549  * The TreeNode UI implementation is separate from the
35550  * tree implementation. Unless you are customizing the tree UI,
35551  * you should never have to use this directly.
35552  */
35553 Roo.tree.TreeNodeUI = function(node){
35554     this.node = node;
35555     this.rendered = false;
35556     this.animating = false;
35557     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35558 };
35559
35560 Roo.tree.TreeNodeUI.prototype = {
35561     removeChild : function(node){
35562         if(this.rendered){
35563             this.ctNode.removeChild(node.ui.getEl());
35564         }
35565     },
35566
35567     beforeLoad : function(){
35568          this.addClass("x-tree-node-loading");
35569     },
35570
35571     afterLoad : function(){
35572          this.removeClass("x-tree-node-loading");
35573     },
35574
35575     onTextChange : function(node, text, oldText){
35576         if(this.rendered){
35577             this.textNode.innerHTML = text;
35578         }
35579     },
35580
35581     onDisableChange : function(node, state){
35582         this.disabled = state;
35583         if(state){
35584             this.addClass("x-tree-node-disabled");
35585         }else{
35586             this.removeClass("x-tree-node-disabled");
35587         }
35588     },
35589
35590     onSelectedChange : function(state){
35591         if(state){
35592             this.focus();
35593             this.addClass("x-tree-selected");
35594         }else{
35595             //this.blur();
35596             this.removeClass("x-tree-selected");
35597         }
35598     },
35599
35600     onMove : function(tree, node, oldParent, newParent, index, refNode){
35601         this.childIndent = null;
35602         if(this.rendered){
35603             var targetNode = newParent.ui.getContainer();
35604             if(!targetNode){//target not rendered
35605                 this.holder = document.createElement("div");
35606                 this.holder.appendChild(this.wrap);
35607                 return;
35608             }
35609             var insertBefore = refNode ? refNode.ui.getEl() : null;
35610             if(insertBefore){
35611                 targetNode.insertBefore(this.wrap, insertBefore);
35612             }else{
35613                 targetNode.appendChild(this.wrap);
35614             }
35615             this.node.renderIndent(true);
35616         }
35617     },
35618
35619     addClass : function(cls){
35620         if(this.elNode){
35621             Roo.fly(this.elNode).addClass(cls);
35622         }
35623     },
35624
35625     removeClass : function(cls){
35626         if(this.elNode){
35627             Roo.fly(this.elNode).removeClass(cls);
35628         }
35629     },
35630
35631     remove : function(){
35632         if(this.rendered){
35633             this.holder = document.createElement("div");
35634             this.holder.appendChild(this.wrap);
35635         }
35636     },
35637
35638     fireEvent : function(){
35639         return this.node.fireEvent.apply(this.node, arguments);
35640     },
35641
35642     initEvents : function(){
35643         this.node.on("move", this.onMove, this);
35644         var E = Roo.EventManager;
35645         var a = this.anchor;
35646
35647         var el = Roo.fly(a, '_treeui');
35648
35649         if(Roo.isOpera){ // opera render bug ignores the CSS
35650             el.setStyle("text-decoration", "none");
35651         }
35652
35653         el.on("click", this.onClick, this);
35654         el.on("dblclick", this.onDblClick, this);
35655
35656         if(this.checkbox){
35657             Roo.EventManager.on(this.checkbox,
35658                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35659         }
35660
35661         el.on("contextmenu", this.onContextMenu, this);
35662
35663         var icon = Roo.fly(this.iconNode);
35664         icon.on("click", this.onClick, this);
35665         icon.on("dblclick", this.onDblClick, this);
35666         icon.on("contextmenu", this.onContextMenu, this);
35667         E.on(this.ecNode, "click", this.ecClick, this, true);
35668
35669         if(this.node.disabled){
35670             this.addClass("x-tree-node-disabled");
35671         }
35672         if(this.node.hidden){
35673             this.addClass("x-tree-node-disabled");
35674         }
35675         var ot = this.node.getOwnerTree();
35676         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35677         if(dd && (!this.node.isRoot || ot.rootVisible)){
35678             Roo.dd.Registry.register(this.elNode, {
35679                 node: this.node,
35680                 handles: this.getDDHandles(),
35681                 isHandle: false
35682             });
35683         }
35684     },
35685
35686     getDDHandles : function(){
35687         return [this.iconNode, this.textNode];
35688     },
35689
35690     hide : function(){
35691         if(this.rendered){
35692             this.wrap.style.display = "none";
35693         }
35694     },
35695
35696     show : function(){
35697         if(this.rendered){
35698             this.wrap.style.display = "";
35699         }
35700     },
35701
35702     onContextMenu : function(e){
35703         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35704             e.preventDefault();
35705             this.focus();
35706             this.fireEvent("contextmenu", this.node, e);
35707         }
35708     },
35709
35710     onClick : function(e){
35711         if(this.dropping){
35712             e.stopEvent();
35713             return;
35714         }
35715         if(this.fireEvent("beforeclick", this.node, e) !== false){
35716             if(!this.disabled && this.node.attributes.href){
35717                 this.fireEvent("click", this.node, e);
35718                 return;
35719             }
35720             e.preventDefault();
35721             if(this.disabled){
35722                 return;
35723             }
35724
35725             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35726                 this.node.toggle();
35727             }
35728
35729             this.fireEvent("click", this.node, e);
35730         }else{
35731             e.stopEvent();
35732         }
35733     },
35734
35735     onDblClick : function(e){
35736         e.preventDefault();
35737         if(this.disabled){
35738             return;
35739         }
35740         if(this.checkbox){
35741             this.toggleCheck();
35742         }
35743         if(!this.animating && this.node.hasChildNodes()){
35744             this.node.toggle();
35745         }
35746         this.fireEvent("dblclick", this.node, e);
35747     },
35748
35749     onCheckChange : function(){
35750         var checked = this.checkbox.checked;
35751         this.node.attributes.checked = checked;
35752         this.fireEvent('checkchange', this.node, checked);
35753     },
35754
35755     ecClick : function(e){
35756         if(!this.animating && this.node.hasChildNodes()){
35757             this.node.toggle();
35758         }
35759     },
35760
35761     startDrop : function(){
35762         this.dropping = true;
35763     },
35764
35765     // delayed drop so the click event doesn't get fired on a drop
35766     endDrop : function(){
35767        setTimeout(function(){
35768            this.dropping = false;
35769        }.createDelegate(this), 50);
35770     },
35771
35772     expand : function(){
35773         this.updateExpandIcon();
35774         this.ctNode.style.display = "";
35775     },
35776
35777     focus : function(){
35778         if(!this.node.preventHScroll){
35779             try{this.anchor.focus();
35780             }catch(e){}
35781         }else if(!Roo.isIE){
35782             try{
35783                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35784                 var l = noscroll.scrollLeft;
35785                 this.anchor.focus();
35786                 noscroll.scrollLeft = l;
35787             }catch(e){}
35788         }
35789     },
35790
35791     toggleCheck : function(value){
35792         var cb = this.checkbox;
35793         if(cb){
35794             cb.checked = (value === undefined ? !cb.checked : value);
35795         }
35796     },
35797
35798     blur : function(){
35799         try{
35800             this.anchor.blur();
35801         }catch(e){}
35802     },
35803
35804     animExpand : function(callback){
35805         var ct = Roo.get(this.ctNode);
35806         ct.stopFx();
35807         if(!this.node.hasChildNodes()){
35808             this.updateExpandIcon();
35809             this.ctNode.style.display = "";
35810             Roo.callback(callback);
35811             return;
35812         }
35813         this.animating = true;
35814         this.updateExpandIcon();
35815
35816         ct.slideIn('t', {
35817            callback : function(){
35818                this.animating = false;
35819                Roo.callback(callback);
35820             },
35821             scope: this,
35822             duration: this.node.ownerTree.duration || .25
35823         });
35824     },
35825
35826     highlight : function(){
35827         var tree = this.node.getOwnerTree();
35828         Roo.fly(this.wrap).highlight(
35829             tree.hlColor || "C3DAF9",
35830             {endColor: tree.hlBaseColor}
35831         );
35832     },
35833
35834     collapse : function(){
35835         this.updateExpandIcon();
35836         this.ctNode.style.display = "none";
35837     },
35838
35839     animCollapse : function(callback){
35840         var ct = Roo.get(this.ctNode);
35841         ct.enableDisplayMode('block');
35842         ct.stopFx();
35843
35844         this.animating = true;
35845         this.updateExpandIcon();
35846
35847         ct.slideOut('t', {
35848             callback : function(){
35849                this.animating = false;
35850                Roo.callback(callback);
35851             },
35852             scope: this,
35853             duration: this.node.ownerTree.duration || .25
35854         });
35855     },
35856
35857     getContainer : function(){
35858         return this.ctNode;
35859     },
35860
35861     getEl : function(){
35862         return this.wrap;
35863     },
35864
35865     appendDDGhost : function(ghostNode){
35866         ghostNode.appendChild(this.elNode.cloneNode(true));
35867     },
35868
35869     getDDRepairXY : function(){
35870         return Roo.lib.Dom.getXY(this.iconNode);
35871     },
35872
35873     onRender : function(){
35874         this.render();
35875     },
35876
35877     render : function(bulkRender){
35878         var n = this.node, a = n.attributes;
35879         var targetNode = n.parentNode ?
35880               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35881
35882         if(!this.rendered){
35883             this.rendered = true;
35884
35885             this.renderElements(n, a, targetNode, bulkRender);
35886
35887             if(a.qtip){
35888                if(this.textNode.setAttributeNS){
35889                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35890                    if(a.qtipTitle){
35891                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35892                    }
35893                }else{
35894                    this.textNode.setAttribute("ext:qtip", a.qtip);
35895                    if(a.qtipTitle){
35896                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35897                    }
35898                }
35899             }else if(a.qtipCfg){
35900                 a.qtipCfg.target = Roo.id(this.textNode);
35901                 Roo.QuickTips.register(a.qtipCfg);
35902             }
35903             this.initEvents();
35904             if(!this.node.expanded){
35905                 this.updateExpandIcon();
35906             }
35907         }else{
35908             if(bulkRender === true) {
35909                 targetNode.appendChild(this.wrap);
35910             }
35911         }
35912     },
35913
35914     renderElements : function(n, a, targetNode, bulkRender)
35915     {
35916         // add some indent caching, this helps performance when rendering a large tree
35917         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35918         var t = n.getOwnerTree();
35919         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35920         if (typeof(n.attributes.html) != 'undefined') {
35921             txt = n.attributes.html;
35922         }
35923         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35924         var cb = typeof a.checked == 'boolean';
35925         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35926         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35927             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35928             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35929             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35930             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35931             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35932              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35933                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35934             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35935             "</li>"];
35936
35937         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35938             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35939                                 n.nextSibling.ui.getEl(), buf.join(""));
35940         }else{
35941             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35942         }
35943
35944         this.elNode = this.wrap.childNodes[0];
35945         this.ctNode = this.wrap.childNodes[1];
35946         var cs = this.elNode.childNodes;
35947         this.indentNode = cs[0];
35948         this.ecNode = cs[1];
35949         this.iconNode = cs[2];
35950         var index = 3;
35951         if(cb){
35952             this.checkbox = cs[3];
35953             index++;
35954         }
35955         this.anchor = cs[index];
35956         this.textNode = cs[index].firstChild;
35957     },
35958
35959     getAnchor : function(){
35960         return this.anchor;
35961     },
35962
35963     getTextEl : function(){
35964         return this.textNode;
35965     },
35966
35967     getIconEl : function(){
35968         return this.iconNode;
35969     },
35970
35971     isChecked : function(){
35972         return this.checkbox ? this.checkbox.checked : false;
35973     },
35974
35975     updateExpandIcon : function(){
35976         if(this.rendered){
35977             var n = this.node, c1, c2;
35978             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35979             var hasChild = n.hasChildNodes();
35980             if(hasChild){
35981                 if(n.expanded){
35982                     cls += "-minus";
35983                     c1 = "x-tree-node-collapsed";
35984                     c2 = "x-tree-node-expanded";
35985                 }else{
35986                     cls += "-plus";
35987                     c1 = "x-tree-node-expanded";
35988                     c2 = "x-tree-node-collapsed";
35989                 }
35990                 if(this.wasLeaf){
35991                     this.removeClass("x-tree-node-leaf");
35992                     this.wasLeaf = false;
35993                 }
35994                 if(this.c1 != c1 || this.c2 != c2){
35995                     Roo.fly(this.elNode).replaceClass(c1, c2);
35996                     this.c1 = c1; this.c2 = c2;
35997                 }
35998             }else{
35999                 // this changes non-leafs into leafs if they have no children.
36000                 // it's not very rational behaviour..
36001                 
36002                 if(!this.wasLeaf && this.node.leaf){
36003                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36004                     delete this.c1;
36005                     delete this.c2;
36006                     this.wasLeaf = true;
36007                 }
36008             }
36009             var ecc = "x-tree-ec-icon "+cls;
36010             if(this.ecc != ecc){
36011                 this.ecNode.className = ecc;
36012                 this.ecc = ecc;
36013             }
36014         }
36015     },
36016
36017     getChildIndent : function(){
36018         if(!this.childIndent){
36019             var buf = [];
36020             var p = this.node;
36021             while(p){
36022                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36023                     if(!p.isLast()) {
36024                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36025                     } else {
36026                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36027                     }
36028                 }
36029                 p = p.parentNode;
36030             }
36031             this.childIndent = buf.join("");
36032         }
36033         return this.childIndent;
36034     },
36035
36036     renderIndent : function(){
36037         if(this.rendered){
36038             var indent = "";
36039             var p = this.node.parentNode;
36040             if(p){
36041                 indent = p.ui.getChildIndent();
36042             }
36043             if(this.indentMarkup != indent){ // don't rerender if not required
36044                 this.indentNode.innerHTML = indent;
36045                 this.indentMarkup = indent;
36046             }
36047             this.updateExpandIcon();
36048         }
36049     }
36050 };
36051
36052 Roo.tree.RootTreeNodeUI = function(){
36053     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36054 };
36055 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36056     render : function(){
36057         if(!this.rendered){
36058             var targetNode = this.node.ownerTree.innerCt.dom;
36059             this.node.expanded = true;
36060             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36061             this.wrap = this.ctNode = targetNode.firstChild;
36062         }
36063     },
36064     collapse : function(){
36065     },
36066     expand : function(){
36067     }
36068 });/*
36069  * Based on:
36070  * Ext JS Library 1.1.1
36071  * Copyright(c) 2006-2007, Ext JS, LLC.
36072  *
36073  * Originally Released Under LGPL - original licence link has changed is not relivant.
36074  *
36075  * Fork - LGPL
36076  * <script type="text/javascript">
36077  */
36078 /**
36079  * @class Roo.tree.TreeLoader
36080  * @extends Roo.util.Observable
36081  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36082  * nodes from a specified URL. The response must be a javascript Array definition
36083  * who's elements are node definition objects. eg:
36084  * <pre><code>
36085 {  success : true,
36086    data :      [
36087    
36088     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36089     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36090     ]
36091 }
36092
36093
36094 </code></pre>
36095  * <br><br>
36096  * The old style respose with just an array is still supported, but not recommended.
36097  * <br><br>
36098  *
36099  * A server request is sent, and child nodes are loaded only when a node is expanded.
36100  * The loading node's id is passed to the server under the parameter name "node" to
36101  * enable the server to produce the correct child nodes.
36102  * <br><br>
36103  * To pass extra parameters, an event handler may be attached to the "beforeload"
36104  * event, and the parameters specified in the TreeLoader's baseParams property:
36105  * <pre><code>
36106     myTreeLoader.on("beforeload", function(treeLoader, node) {
36107         this.baseParams.category = node.attributes.category;
36108     }, this);
36109     
36110 </code></pre>
36111  *
36112  * This would pass an HTTP parameter called "category" to the server containing
36113  * the value of the Node's "category" attribute.
36114  * @constructor
36115  * Creates a new Treeloader.
36116  * @param {Object} config A config object containing config properties.
36117  */
36118 Roo.tree.TreeLoader = function(config){
36119     this.baseParams = {};
36120     this.requestMethod = "POST";
36121     Roo.apply(this, config);
36122
36123     this.addEvents({
36124     
36125         /**
36126          * @event beforeload
36127          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36128          * @param {Object} This TreeLoader object.
36129          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36130          * @param {Object} callback The callback function specified in the {@link #load} call.
36131          */
36132         beforeload : true,
36133         /**
36134          * @event load
36135          * Fires when the node has been successfuly loaded.
36136          * @param {Object} This TreeLoader object.
36137          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36138          * @param {Object} response The response object containing the data from the server.
36139          */
36140         load : true,
36141         /**
36142          * @event loadexception
36143          * Fires if the network request failed.
36144          * @param {Object} This TreeLoader object.
36145          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36146          * @param {Object} response The response object containing the data from the server.
36147          */
36148         loadexception : true,
36149         /**
36150          * @event create
36151          * Fires before a node is created, enabling you to return custom Node types 
36152          * @param {Object} This TreeLoader object.
36153          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36154          */
36155         create : true
36156     });
36157
36158     Roo.tree.TreeLoader.superclass.constructor.call(this);
36159 };
36160
36161 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36162     /**
36163     * @cfg {String} dataUrl The URL from which to request a Json string which
36164     * specifies an array of node definition object representing the child nodes
36165     * to be loaded.
36166     */
36167     /**
36168     * @cfg {String} requestMethod either GET or POST
36169     * defaults to POST (due to BC)
36170     * to be loaded.
36171     */
36172     /**
36173     * @cfg {Object} baseParams (optional) An object containing properties which
36174     * specify HTTP parameters to be passed to each request for child nodes.
36175     */
36176     /**
36177     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36178     * created by this loader. If the attributes sent by the server have an attribute in this object,
36179     * they take priority.
36180     */
36181     /**
36182     * @cfg {Object} uiProviders (optional) An object containing properties which
36183     * 
36184     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36185     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36186     * <i>uiProvider</i> attribute of a returned child node is a string rather
36187     * than a reference to a TreeNodeUI implementation, this that string value
36188     * is used as a property name in the uiProviders object. You can define the provider named
36189     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36190     */
36191     uiProviders : {},
36192
36193     /**
36194     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36195     * child nodes before loading.
36196     */
36197     clearOnLoad : true,
36198
36199     /**
36200     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36201     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36202     * Grid query { data : [ .....] }
36203     */
36204     
36205     root : false,
36206      /**
36207     * @cfg {String} queryParam (optional) 
36208     * Name of the query as it will be passed on the querystring (defaults to 'node')
36209     * eg. the request will be ?node=[id]
36210     */
36211     
36212     
36213     queryParam: false,
36214     
36215     /**
36216      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36217      * This is called automatically when a node is expanded, but may be used to reload
36218      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36219      * @param {Roo.tree.TreeNode} node
36220      * @param {Function} callback
36221      */
36222     load : function(node, callback){
36223         if(this.clearOnLoad){
36224             while(node.firstChild){
36225                 node.removeChild(node.firstChild);
36226             }
36227         }
36228         if(node.attributes.children){ // preloaded json children
36229             var cs = node.attributes.children;
36230             for(var i = 0, len = cs.length; i < len; i++){
36231                 node.appendChild(this.createNode(cs[i]));
36232             }
36233             if(typeof callback == "function"){
36234                 callback();
36235             }
36236         }else if(this.dataUrl){
36237             this.requestData(node, callback);
36238         }
36239     },
36240
36241     getParams: function(node){
36242         var buf = [], bp = this.baseParams;
36243         for(var key in bp){
36244             if(typeof bp[key] != "function"){
36245                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36246             }
36247         }
36248         var n = this.queryParam === false ? 'node' : this.queryParam;
36249         buf.push(n + "=", encodeURIComponent(node.id));
36250         return buf.join("");
36251     },
36252
36253     requestData : function(node, callback){
36254         if(this.fireEvent("beforeload", this, node, callback) !== false){
36255             this.transId = Roo.Ajax.request({
36256                 method:this.requestMethod,
36257                 url: this.dataUrl||this.url,
36258                 success: this.handleResponse,
36259                 failure: this.handleFailure,
36260                 scope: this,
36261                 argument: {callback: callback, node: node},
36262                 params: this.getParams(node)
36263             });
36264         }else{
36265             // if the load is cancelled, make sure we notify
36266             // the node that we are done
36267             if(typeof callback == "function"){
36268                 callback();
36269             }
36270         }
36271     },
36272
36273     isLoading : function(){
36274         return this.transId ? true : false;
36275     },
36276
36277     abort : function(){
36278         if(this.isLoading()){
36279             Roo.Ajax.abort(this.transId);
36280         }
36281     },
36282
36283     // private
36284     createNode : function(attr)
36285     {
36286         // apply baseAttrs, nice idea Corey!
36287         if(this.baseAttrs){
36288             Roo.applyIf(attr, this.baseAttrs);
36289         }
36290         if(this.applyLoader !== false){
36291             attr.loader = this;
36292         }
36293         // uiProvider = depreciated..
36294         
36295         if(typeof(attr.uiProvider) == 'string'){
36296            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36297                 /**  eval:var:attr */ eval(attr.uiProvider);
36298         }
36299         if(typeof(this.uiProviders['default']) != 'undefined') {
36300             attr.uiProvider = this.uiProviders['default'];
36301         }
36302         
36303         this.fireEvent('create', this, attr);
36304         
36305         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36306         return(attr.leaf ?
36307                         new Roo.tree.TreeNode(attr) :
36308                         new Roo.tree.AsyncTreeNode(attr));
36309     },
36310
36311     processResponse : function(response, node, callback)
36312     {
36313         var json = response.responseText;
36314         try {
36315             
36316             var o = Roo.decode(json);
36317             
36318             if (this.root === false && typeof(o.success) != undefined) {
36319                 this.root = 'data'; // the default behaviour for list like data..
36320                 }
36321                 
36322             if (this.root !== false &&  !o.success) {
36323                 // it's a failure condition.
36324                 var a = response.argument;
36325                 this.fireEvent("loadexception", this, a.node, response);
36326                 Roo.log("Load failed - should have a handler really");
36327                 return;
36328             }
36329             
36330             
36331             
36332             if (this.root !== false) {
36333                  o = o[this.root];
36334             }
36335             
36336             for(var i = 0, len = o.length; i < len; i++){
36337                 var n = this.createNode(o[i]);
36338                 if(n){
36339                     node.appendChild(n);
36340                 }
36341             }
36342             if(typeof callback == "function"){
36343                 callback(this, node);
36344             }
36345         }catch(e){
36346             this.handleFailure(response);
36347         }
36348     },
36349
36350     handleResponse : function(response){
36351         this.transId = false;
36352         var a = response.argument;
36353         this.processResponse(response, a.node, a.callback);
36354         this.fireEvent("load", this, a.node, response);
36355     },
36356
36357     handleFailure : function(response)
36358     {
36359         // should handle failure better..
36360         this.transId = false;
36361         var a = response.argument;
36362         this.fireEvent("loadexception", this, a.node, response);
36363         if(typeof a.callback == "function"){
36364             a.callback(this, a.node);
36365         }
36366     }
36367 });/*
36368  * Based on:
36369  * Ext JS Library 1.1.1
36370  * Copyright(c) 2006-2007, Ext JS, LLC.
36371  *
36372  * Originally Released Under LGPL - original licence link has changed is not relivant.
36373  *
36374  * Fork - LGPL
36375  * <script type="text/javascript">
36376  */
36377
36378 /**
36379 * @class Roo.tree.TreeFilter
36380 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36381 * @param {TreePanel} tree
36382 * @param {Object} config (optional)
36383  */
36384 Roo.tree.TreeFilter = function(tree, config){
36385     this.tree = tree;
36386     this.filtered = {};
36387     Roo.apply(this, config);
36388 };
36389
36390 Roo.tree.TreeFilter.prototype = {
36391     clearBlank:false,
36392     reverse:false,
36393     autoClear:false,
36394     remove:false,
36395
36396      /**
36397      * Filter the data by a specific attribute.
36398      * @param {String/RegExp} value Either string that the attribute value
36399      * should start with or a RegExp to test against the attribute
36400      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36401      * @param {TreeNode} startNode (optional) The node to start the filter at.
36402      */
36403     filter : function(value, attr, startNode){
36404         attr = attr || "text";
36405         var f;
36406         if(typeof value == "string"){
36407             var vlen = value.length;
36408             // auto clear empty filter
36409             if(vlen == 0 && this.clearBlank){
36410                 this.clear();
36411                 return;
36412             }
36413             value = value.toLowerCase();
36414             f = function(n){
36415                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36416             };
36417         }else if(value.exec){ // regex?
36418             f = function(n){
36419                 return value.test(n.attributes[attr]);
36420             };
36421         }else{
36422             throw 'Illegal filter type, must be string or regex';
36423         }
36424         this.filterBy(f, null, startNode);
36425         },
36426
36427     /**
36428      * Filter by a function. The passed function will be called with each
36429      * node in the tree (or from the startNode). If the function returns true, the node is kept
36430      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36431      * @param {Function} fn The filter function
36432      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36433      */
36434     filterBy : function(fn, scope, startNode){
36435         startNode = startNode || this.tree.root;
36436         if(this.autoClear){
36437             this.clear();
36438         }
36439         var af = this.filtered, rv = this.reverse;
36440         var f = function(n){
36441             if(n == startNode){
36442                 return true;
36443             }
36444             if(af[n.id]){
36445                 return false;
36446             }
36447             var m = fn.call(scope || n, n);
36448             if(!m || rv){
36449                 af[n.id] = n;
36450                 n.ui.hide();
36451                 return false;
36452             }
36453             return true;
36454         };
36455         startNode.cascade(f);
36456         if(this.remove){
36457            for(var id in af){
36458                if(typeof id != "function"){
36459                    var n = af[id];
36460                    if(n && n.parentNode){
36461                        n.parentNode.removeChild(n);
36462                    }
36463                }
36464            }
36465         }
36466     },
36467
36468     /**
36469      * Clears the current filter. Note: with the "remove" option
36470      * set a filter cannot be cleared.
36471      */
36472     clear : function(){
36473         var t = this.tree;
36474         var af = this.filtered;
36475         for(var id in af){
36476             if(typeof id != "function"){
36477                 var n = af[id];
36478                 if(n){
36479                     n.ui.show();
36480                 }
36481             }
36482         }
36483         this.filtered = {};
36484     }
36485 };
36486 /*
36487  * Based on:
36488  * Ext JS Library 1.1.1
36489  * Copyright(c) 2006-2007, Ext JS, LLC.
36490  *
36491  * Originally Released Under LGPL - original licence link has changed is not relivant.
36492  *
36493  * Fork - LGPL
36494  * <script type="text/javascript">
36495  */
36496  
36497
36498 /**
36499  * @class Roo.tree.TreeSorter
36500  * Provides sorting of nodes in a TreePanel
36501  * 
36502  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36503  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36504  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36505  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36506  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36507  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36508  * @constructor
36509  * @param {TreePanel} tree
36510  * @param {Object} config
36511  */
36512 Roo.tree.TreeSorter = function(tree, config){
36513     Roo.apply(this, config);
36514     tree.on("beforechildrenrendered", this.doSort, this);
36515     tree.on("append", this.updateSort, this);
36516     tree.on("insert", this.updateSort, this);
36517     
36518     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36519     var p = this.property || "text";
36520     var sortType = this.sortType;
36521     var fs = this.folderSort;
36522     var cs = this.caseSensitive === true;
36523     var leafAttr = this.leafAttr || 'leaf';
36524
36525     this.sortFn = function(n1, n2){
36526         if(fs){
36527             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36528                 return 1;
36529             }
36530             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36531                 return -1;
36532             }
36533         }
36534         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36535         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36536         if(v1 < v2){
36537                         return dsc ? +1 : -1;
36538                 }else if(v1 > v2){
36539                         return dsc ? -1 : +1;
36540         }else{
36541                 return 0;
36542         }
36543     };
36544 };
36545
36546 Roo.tree.TreeSorter.prototype = {
36547     doSort : function(node){
36548         node.sort(this.sortFn);
36549     },
36550     
36551     compareNodes : function(n1, n2){
36552         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36553     },
36554     
36555     updateSort : function(tree, node){
36556         if(node.childrenRendered){
36557             this.doSort.defer(1, this, [node]);
36558         }
36559     }
36560 };/*
36561  * Based on:
36562  * Ext JS Library 1.1.1
36563  * Copyright(c) 2006-2007, Ext JS, LLC.
36564  *
36565  * Originally Released Under LGPL - original licence link has changed is not relivant.
36566  *
36567  * Fork - LGPL
36568  * <script type="text/javascript">
36569  */
36570
36571 if(Roo.dd.DropZone){
36572     
36573 Roo.tree.TreeDropZone = function(tree, config){
36574     this.allowParentInsert = false;
36575     this.allowContainerDrop = false;
36576     this.appendOnly = false;
36577     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36578     this.tree = tree;
36579     this.lastInsertClass = "x-tree-no-status";
36580     this.dragOverData = {};
36581 };
36582
36583 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36584     ddGroup : "TreeDD",
36585     scroll:  true,
36586     
36587     expandDelay : 1000,
36588     
36589     expandNode : function(node){
36590         if(node.hasChildNodes() && !node.isExpanded()){
36591             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36592         }
36593     },
36594     
36595     queueExpand : function(node){
36596         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36597     },
36598     
36599     cancelExpand : function(){
36600         if(this.expandProcId){
36601             clearTimeout(this.expandProcId);
36602             this.expandProcId = false;
36603         }
36604     },
36605     
36606     isValidDropPoint : function(n, pt, dd, e, data){
36607         if(!n || !data){ return false; }
36608         var targetNode = n.node;
36609         var dropNode = data.node;
36610         // default drop rules
36611         if(!(targetNode && targetNode.isTarget && pt)){
36612             return false;
36613         }
36614         if(pt == "append" && targetNode.allowChildren === false){
36615             return false;
36616         }
36617         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36618             return false;
36619         }
36620         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36621             return false;
36622         }
36623         // reuse the object
36624         var overEvent = this.dragOverData;
36625         overEvent.tree = this.tree;
36626         overEvent.target = targetNode;
36627         overEvent.data = data;
36628         overEvent.point = pt;
36629         overEvent.source = dd;
36630         overEvent.rawEvent = e;
36631         overEvent.dropNode = dropNode;
36632         overEvent.cancel = false;  
36633         var result = this.tree.fireEvent("nodedragover", overEvent);
36634         return overEvent.cancel === false && result !== false;
36635     },
36636     
36637     getDropPoint : function(e, n, dd)
36638     {
36639         var tn = n.node;
36640         if(tn.isRoot){
36641             return tn.allowChildren !== false ? "append" : false; // always append for root
36642         }
36643         var dragEl = n.ddel;
36644         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36645         var y = Roo.lib.Event.getPageY(e);
36646         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36647         
36648         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36649         var noAppend = tn.allowChildren === false;
36650         if(this.appendOnly || tn.parentNode.allowChildren === false){
36651             return noAppend ? false : "append";
36652         }
36653         var noBelow = false;
36654         if(!this.allowParentInsert){
36655             noBelow = tn.hasChildNodes() && tn.isExpanded();
36656         }
36657         var q = (b - t) / (noAppend ? 2 : 3);
36658         if(y >= t && y < (t + q)){
36659             return "above";
36660         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36661             return "below";
36662         }else{
36663             return "append";
36664         }
36665     },
36666     
36667     onNodeEnter : function(n, dd, e, data)
36668     {
36669         this.cancelExpand();
36670     },
36671     
36672     onNodeOver : function(n, dd, e, data)
36673     {
36674        
36675         var pt = this.getDropPoint(e, n, dd);
36676         var node = n.node;
36677         
36678         // auto node expand check
36679         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36680             this.queueExpand(node);
36681         }else if(pt != "append"){
36682             this.cancelExpand();
36683         }
36684         
36685         // set the insert point style on the target node
36686         var returnCls = this.dropNotAllowed;
36687         if(this.isValidDropPoint(n, pt, dd, e, data)){
36688            if(pt){
36689                var el = n.ddel;
36690                var cls;
36691                if(pt == "above"){
36692                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36693                    cls = "x-tree-drag-insert-above";
36694                }else if(pt == "below"){
36695                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36696                    cls = "x-tree-drag-insert-below";
36697                }else{
36698                    returnCls = "x-tree-drop-ok-append";
36699                    cls = "x-tree-drag-append";
36700                }
36701                if(this.lastInsertClass != cls){
36702                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36703                    this.lastInsertClass = cls;
36704                }
36705            }
36706        }
36707        return returnCls;
36708     },
36709     
36710     onNodeOut : function(n, dd, e, data){
36711         
36712         this.cancelExpand();
36713         this.removeDropIndicators(n);
36714     },
36715     
36716     onNodeDrop : function(n, dd, e, data){
36717         var point = this.getDropPoint(e, n, dd);
36718         var targetNode = n.node;
36719         targetNode.ui.startDrop();
36720         if(!this.isValidDropPoint(n, point, dd, e, data)){
36721             targetNode.ui.endDrop();
36722             return false;
36723         }
36724         // first try to find the drop node
36725         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36726         var dropEvent = {
36727             tree : this.tree,
36728             target: targetNode,
36729             data: data,
36730             point: point,
36731             source: dd,
36732             rawEvent: e,
36733             dropNode: dropNode,
36734             cancel: !dropNode   
36735         };
36736         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36737         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36738             targetNode.ui.endDrop();
36739             return false;
36740         }
36741         // allow target changing
36742         targetNode = dropEvent.target;
36743         if(point == "append" && !targetNode.isExpanded()){
36744             targetNode.expand(false, null, function(){
36745                 this.completeDrop(dropEvent);
36746             }.createDelegate(this));
36747         }else{
36748             this.completeDrop(dropEvent);
36749         }
36750         return true;
36751     },
36752     
36753     completeDrop : function(de){
36754         var ns = de.dropNode, p = de.point, t = de.target;
36755         if(!(ns instanceof Array)){
36756             ns = [ns];
36757         }
36758         var n;
36759         for(var i = 0, len = ns.length; i < len; i++){
36760             n = ns[i];
36761             if(p == "above"){
36762                 t.parentNode.insertBefore(n, t);
36763             }else if(p == "below"){
36764                 t.parentNode.insertBefore(n, t.nextSibling);
36765             }else{
36766                 t.appendChild(n);
36767             }
36768         }
36769         n.ui.focus();
36770         if(this.tree.hlDrop){
36771             n.ui.highlight();
36772         }
36773         t.ui.endDrop();
36774         this.tree.fireEvent("nodedrop", de);
36775     },
36776     
36777     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36778         if(this.tree.hlDrop){
36779             dropNode.ui.focus();
36780             dropNode.ui.highlight();
36781         }
36782         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36783     },
36784     
36785     getTree : function(){
36786         return this.tree;
36787     },
36788     
36789     removeDropIndicators : function(n){
36790         if(n && n.ddel){
36791             var el = n.ddel;
36792             Roo.fly(el).removeClass([
36793                     "x-tree-drag-insert-above",
36794                     "x-tree-drag-insert-below",
36795                     "x-tree-drag-append"]);
36796             this.lastInsertClass = "_noclass";
36797         }
36798     },
36799     
36800     beforeDragDrop : function(target, e, id){
36801         this.cancelExpand();
36802         return true;
36803     },
36804     
36805     afterRepair : function(data){
36806         if(data && Roo.enableFx){
36807             data.node.ui.highlight();
36808         }
36809         this.hideProxy();
36810     } 
36811     
36812 });
36813
36814 }
36815 /*
36816  * Based on:
36817  * Ext JS Library 1.1.1
36818  * Copyright(c) 2006-2007, Ext JS, LLC.
36819  *
36820  * Originally Released Under LGPL - original licence link has changed is not relivant.
36821  *
36822  * Fork - LGPL
36823  * <script type="text/javascript">
36824  */
36825  
36826
36827 if(Roo.dd.DragZone){
36828 Roo.tree.TreeDragZone = function(tree, config){
36829     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36830     this.tree = tree;
36831 };
36832
36833 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36834     ddGroup : "TreeDD",
36835    
36836     onBeforeDrag : function(data, e){
36837         var n = data.node;
36838         return n && n.draggable && !n.disabled;
36839     },
36840      
36841     
36842     onInitDrag : function(e){
36843         var data = this.dragData;
36844         this.tree.getSelectionModel().select(data.node);
36845         this.proxy.update("");
36846         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36847         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36848     },
36849     
36850     getRepairXY : function(e, data){
36851         return data.node.ui.getDDRepairXY();
36852     },
36853     
36854     onEndDrag : function(data, e){
36855         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36856         
36857         
36858     },
36859     
36860     onValidDrop : function(dd, e, id){
36861         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36862         this.hideProxy();
36863     },
36864     
36865     beforeInvalidDrop : function(e, id){
36866         // this scrolls the original position back into view
36867         var sm = this.tree.getSelectionModel();
36868         sm.clearSelections();
36869         sm.select(this.dragData.node);
36870     }
36871 });
36872 }/*
36873  * Based on:
36874  * Ext JS Library 1.1.1
36875  * Copyright(c) 2006-2007, Ext JS, LLC.
36876  *
36877  * Originally Released Under LGPL - original licence link has changed is not relivant.
36878  *
36879  * Fork - LGPL
36880  * <script type="text/javascript">
36881  */
36882 /**
36883  * @class Roo.tree.TreeEditor
36884  * @extends Roo.Editor
36885  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36886  * as the editor field.
36887  * @constructor
36888  * @param {Object} config (used to be the tree panel.)
36889  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36890  * 
36891  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36892  * @cfg {Roo.form.TextField|Object} field The field configuration
36893  *
36894  * 
36895  */
36896 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36897     var tree = config;
36898     var field;
36899     if (oldconfig) { // old style..
36900         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36901     } else {
36902         // new style..
36903         tree = config.tree;
36904         config.field = config.field  || {};
36905         config.field.xtype = 'TextField';
36906         field = Roo.factory(config.field, Roo.form);
36907     }
36908     config = config || {};
36909     
36910     
36911     this.addEvents({
36912         /**
36913          * @event beforenodeedit
36914          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36915          * false from the handler of this event.
36916          * @param {Editor} this
36917          * @param {Roo.tree.Node} node 
36918          */
36919         "beforenodeedit" : true
36920     });
36921     
36922     //Roo.log(config);
36923     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36924
36925     this.tree = tree;
36926
36927     tree.on('beforeclick', this.beforeNodeClick, this);
36928     tree.getTreeEl().on('mousedown', this.hide, this);
36929     this.on('complete', this.updateNode, this);
36930     this.on('beforestartedit', this.fitToTree, this);
36931     this.on('startedit', this.bindScroll, this, {delay:10});
36932     this.on('specialkey', this.onSpecialKey, this);
36933 };
36934
36935 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36936     /**
36937      * @cfg {String} alignment
36938      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36939      */
36940     alignment: "l-l",
36941     // inherit
36942     autoSize: false,
36943     /**
36944      * @cfg {Boolean} hideEl
36945      * True to hide the bound element while the editor is displayed (defaults to false)
36946      */
36947     hideEl : false,
36948     /**
36949      * @cfg {String} cls
36950      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36951      */
36952     cls: "x-small-editor x-tree-editor",
36953     /**
36954      * @cfg {Boolean} shim
36955      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36956      */
36957     shim:false,
36958     // inherit
36959     shadow:"frame",
36960     /**
36961      * @cfg {Number} maxWidth
36962      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36963      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36964      * scroll and client offsets into account prior to each edit.
36965      */
36966     maxWidth: 250,
36967
36968     editDelay : 350,
36969
36970     // private
36971     fitToTree : function(ed, el){
36972         var td = this.tree.getTreeEl().dom, nd = el.dom;
36973         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36974             td.scrollLeft = nd.offsetLeft;
36975         }
36976         var w = Math.min(
36977                 this.maxWidth,
36978                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36979         this.setSize(w, '');
36980         
36981         return this.fireEvent('beforenodeedit', this, this.editNode);
36982         
36983     },
36984
36985     // private
36986     triggerEdit : function(node){
36987         this.completeEdit();
36988         this.editNode = node;
36989         this.startEdit(node.ui.textNode, node.text);
36990     },
36991
36992     // private
36993     bindScroll : function(){
36994         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36995     },
36996
36997     // private
36998     beforeNodeClick : function(node, e){
36999         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37000         this.lastClick = new Date();
37001         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37002             e.stopEvent();
37003             this.triggerEdit(node);
37004             return false;
37005         }
37006         return true;
37007     },
37008
37009     // private
37010     updateNode : function(ed, value){
37011         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37012         this.editNode.setText(value);
37013     },
37014
37015     // private
37016     onHide : function(){
37017         Roo.tree.TreeEditor.superclass.onHide.call(this);
37018         if(this.editNode){
37019             this.editNode.ui.focus();
37020         }
37021     },
37022
37023     // private
37024     onSpecialKey : function(field, e){
37025         var k = e.getKey();
37026         if(k == e.ESC){
37027             e.stopEvent();
37028             this.cancelEdit();
37029         }else if(k == e.ENTER && !e.hasModifier()){
37030             e.stopEvent();
37031             this.completeEdit();
37032         }
37033     }
37034 });//<Script type="text/javascript">
37035 /*
37036  * Based on:
37037  * Ext JS Library 1.1.1
37038  * Copyright(c) 2006-2007, Ext JS, LLC.
37039  *
37040  * Originally Released Under LGPL - original licence link has changed is not relivant.
37041  *
37042  * Fork - LGPL
37043  * <script type="text/javascript">
37044  */
37045  
37046 /**
37047  * Not documented??? - probably should be...
37048  */
37049
37050 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37051     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37052     
37053     renderElements : function(n, a, targetNode, bulkRender){
37054         //consel.log("renderElements?");
37055         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37056
37057         var t = n.getOwnerTree();
37058         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37059         
37060         var cols = t.columns;
37061         var bw = t.borderWidth;
37062         var c = cols[0];
37063         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37064          var cb = typeof a.checked == "boolean";
37065         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37066         var colcls = 'x-t-' + tid + '-c0';
37067         var buf = [
37068             '<li class="x-tree-node">',
37069             
37070                 
37071                 '<div class="x-tree-node-el ', a.cls,'">',
37072                     // extran...
37073                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37074                 
37075                 
37076                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37077                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37078                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37079                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37080                            (a.iconCls ? ' '+a.iconCls : ''),
37081                            '" unselectable="on" />',
37082                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37083                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37084                              
37085                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37086                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37087                             '<span unselectable="on" qtip="' + tx + '">',
37088                              tx,
37089                              '</span></a>' ,
37090                     '</div>',
37091                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37092                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37093                  ];
37094         for(var i = 1, len = cols.length; i < len; i++){
37095             c = cols[i];
37096             colcls = 'x-t-' + tid + '-c' +i;
37097             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37098             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37099                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37100                       "</div>");
37101          }
37102          
37103          buf.push(
37104             '</a>',
37105             '<div class="x-clear"></div></div>',
37106             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37107             "</li>");
37108         
37109         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37110             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37111                                 n.nextSibling.ui.getEl(), buf.join(""));
37112         }else{
37113             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37114         }
37115         var el = this.wrap.firstChild;
37116         this.elRow = el;
37117         this.elNode = el.firstChild;
37118         this.ranchor = el.childNodes[1];
37119         this.ctNode = this.wrap.childNodes[1];
37120         var cs = el.firstChild.childNodes;
37121         this.indentNode = cs[0];
37122         this.ecNode = cs[1];
37123         this.iconNode = cs[2];
37124         var index = 3;
37125         if(cb){
37126             this.checkbox = cs[3];
37127             index++;
37128         }
37129         this.anchor = cs[index];
37130         
37131         this.textNode = cs[index].firstChild;
37132         
37133         //el.on("click", this.onClick, this);
37134         //el.on("dblclick", this.onDblClick, this);
37135         
37136         
37137        // console.log(this);
37138     },
37139     initEvents : function(){
37140         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37141         
37142             
37143         var a = this.ranchor;
37144
37145         var el = Roo.get(a);
37146
37147         if(Roo.isOpera){ // opera render bug ignores the CSS
37148             el.setStyle("text-decoration", "none");
37149         }
37150
37151         el.on("click", this.onClick, this);
37152         el.on("dblclick", this.onDblClick, this);
37153         el.on("contextmenu", this.onContextMenu, this);
37154         
37155     },
37156     
37157     /*onSelectedChange : function(state){
37158         if(state){
37159             this.focus();
37160             this.addClass("x-tree-selected");
37161         }else{
37162             //this.blur();
37163             this.removeClass("x-tree-selected");
37164         }
37165     },*/
37166     addClass : function(cls){
37167         if(this.elRow){
37168             Roo.fly(this.elRow).addClass(cls);
37169         }
37170         
37171     },
37172     
37173     
37174     removeClass : function(cls){
37175         if(this.elRow){
37176             Roo.fly(this.elRow).removeClass(cls);
37177         }
37178     }
37179
37180     
37181     
37182 });//<Script type="text/javascript">
37183
37184 /*
37185  * Based on:
37186  * Ext JS Library 1.1.1
37187  * Copyright(c) 2006-2007, Ext JS, LLC.
37188  *
37189  * Originally Released Under LGPL - original licence link has changed is not relivant.
37190  *
37191  * Fork - LGPL
37192  * <script type="text/javascript">
37193  */
37194  
37195
37196 /**
37197  * @class Roo.tree.ColumnTree
37198  * @extends Roo.data.TreePanel
37199  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37200  * @cfg {int} borderWidth  compined right/left border allowance
37201  * @constructor
37202  * @param {String/HTMLElement/Element} el The container element
37203  * @param {Object} config
37204  */
37205 Roo.tree.ColumnTree =  function(el, config)
37206 {
37207    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37208    this.addEvents({
37209         /**
37210         * @event resize
37211         * Fire this event on a container when it resizes
37212         * @param {int} w Width
37213         * @param {int} h Height
37214         */
37215        "resize" : true
37216     });
37217     this.on('resize', this.onResize, this);
37218 };
37219
37220 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37221     //lines:false,
37222     
37223     
37224     borderWidth: Roo.isBorderBox ? 0 : 2, 
37225     headEls : false,
37226     
37227     render : function(){
37228         // add the header.....
37229        
37230         Roo.tree.ColumnTree.superclass.render.apply(this);
37231         
37232         this.el.addClass('x-column-tree');
37233         
37234         this.headers = this.el.createChild(
37235             {cls:'x-tree-headers'},this.innerCt.dom);
37236    
37237         var cols = this.columns, c;
37238         var totalWidth = 0;
37239         this.headEls = [];
37240         var  len = cols.length;
37241         for(var i = 0; i < len; i++){
37242              c = cols[i];
37243              totalWidth += c.width;
37244             this.headEls.push(this.headers.createChild({
37245                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37246                  cn: {
37247                      cls:'x-tree-hd-text',
37248                      html: c.header
37249                  },
37250                  style:'width:'+(c.width-this.borderWidth)+'px;'
37251              }));
37252         }
37253         this.headers.createChild({cls:'x-clear'});
37254         // prevent floats from wrapping when clipped
37255         this.headers.setWidth(totalWidth);
37256         //this.innerCt.setWidth(totalWidth);
37257         this.innerCt.setStyle({ overflow: 'auto' });
37258         this.onResize(this.width, this.height);
37259              
37260         
37261     },
37262     onResize : function(w,h)
37263     {
37264         this.height = h;
37265         this.width = w;
37266         // resize cols..
37267         this.innerCt.setWidth(this.width);
37268         this.innerCt.setHeight(this.height-20);
37269         
37270         // headers...
37271         var cols = this.columns, c;
37272         var totalWidth = 0;
37273         var expEl = false;
37274         var len = cols.length;
37275         for(var i = 0; i < len; i++){
37276             c = cols[i];
37277             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37278                 // it's the expander..
37279                 expEl  = this.headEls[i];
37280                 continue;
37281             }
37282             totalWidth += c.width;
37283             
37284         }
37285         if (expEl) {
37286             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37287         }
37288         this.headers.setWidth(w-20);
37289
37290         
37291         
37292         
37293     }
37294 });
37295 /*
37296  * Based on:
37297  * Ext JS Library 1.1.1
37298  * Copyright(c) 2006-2007, Ext JS, LLC.
37299  *
37300  * Originally Released Under LGPL - original licence link has changed is not relivant.
37301  *
37302  * Fork - LGPL
37303  * <script type="text/javascript">
37304  */
37305  
37306 /**
37307  * @class Roo.menu.Menu
37308  * @extends Roo.util.Observable
37309  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37310  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37311  * @constructor
37312  * Creates a new Menu
37313  * @param {Object} config Configuration options
37314  */
37315 Roo.menu.Menu = function(config){
37316     
37317     Roo.menu.Menu.superclass.constructor.call(this, config);
37318     
37319     this.id = this.id || Roo.id();
37320     this.addEvents({
37321         /**
37322          * @event beforeshow
37323          * Fires before this menu is displayed
37324          * @param {Roo.menu.Menu} this
37325          */
37326         beforeshow : true,
37327         /**
37328          * @event beforehide
37329          * Fires before this menu is hidden
37330          * @param {Roo.menu.Menu} this
37331          */
37332         beforehide : true,
37333         /**
37334          * @event show
37335          * Fires after this menu is displayed
37336          * @param {Roo.menu.Menu} this
37337          */
37338         show : true,
37339         /**
37340          * @event hide
37341          * Fires after this menu is hidden
37342          * @param {Roo.menu.Menu} this
37343          */
37344         hide : true,
37345         /**
37346          * @event click
37347          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37348          * @param {Roo.menu.Menu} this
37349          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37350          * @param {Roo.EventObject} e
37351          */
37352         click : true,
37353         /**
37354          * @event mouseover
37355          * Fires when the mouse is hovering over this menu
37356          * @param {Roo.menu.Menu} this
37357          * @param {Roo.EventObject} e
37358          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37359          */
37360         mouseover : true,
37361         /**
37362          * @event mouseout
37363          * Fires when the mouse exits this menu
37364          * @param {Roo.menu.Menu} this
37365          * @param {Roo.EventObject} e
37366          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37367          */
37368         mouseout : true,
37369         /**
37370          * @event itemclick
37371          * Fires when a menu item contained in this menu is clicked
37372          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37373          * @param {Roo.EventObject} e
37374          */
37375         itemclick: true
37376     });
37377     if (this.registerMenu) {
37378         Roo.menu.MenuMgr.register(this);
37379     }
37380     
37381     var mis = this.items;
37382     this.items = new Roo.util.MixedCollection();
37383     if(mis){
37384         this.add.apply(this, mis);
37385     }
37386 };
37387
37388 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37389     /**
37390      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37391      */
37392     minWidth : 120,
37393     /**
37394      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37395      * for bottom-right shadow (defaults to "sides")
37396      */
37397     shadow : "sides",
37398     /**
37399      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37400      * this menu (defaults to "tl-tr?")
37401      */
37402     subMenuAlign : "tl-tr?",
37403     /**
37404      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37405      * relative to its element of origin (defaults to "tl-bl?")
37406      */
37407     defaultAlign : "tl-bl?",
37408     /**
37409      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37410      */
37411     allowOtherMenus : false,
37412     /**
37413      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37414      */
37415     registerMenu : true,
37416
37417     hidden:true,
37418
37419     // private
37420     render : function(){
37421         if(this.el){
37422             return;
37423         }
37424         var el = this.el = new Roo.Layer({
37425             cls: "x-menu",
37426             shadow:this.shadow,
37427             constrain: false,
37428             parentEl: this.parentEl || document.body,
37429             zindex:15000
37430         });
37431
37432         this.keyNav = new Roo.menu.MenuNav(this);
37433
37434         if(this.plain){
37435             el.addClass("x-menu-plain");
37436         }
37437         if(this.cls){
37438             el.addClass(this.cls);
37439         }
37440         // generic focus element
37441         this.focusEl = el.createChild({
37442             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37443         });
37444         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37445         //disabling touch- as it's causing issues ..
37446         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37447         ul.on('click'   , this.onClick, this);
37448         
37449         
37450         ul.on("mouseover", this.onMouseOver, this);
37451         ul.on("mouseout", this.onMouseOut, this);
37452         this.items.each(function(item){
37453             if (item.hidden) {
37454                 return;
37455             }
37456             
37457             var li = document.createElement("li");
37458             li.className = "x-menu-list-item";
37459             ul.dom.appendChild(li);
37460             item.render(li, this);
37461         }, this);
37462         this.ul = ul;
37463         this.autoWidth();
37464     },
37465
37466     // private
37467     autoWidth : function(){
37468         var el = this.el, ul = this.ul;
37469         if(!el){
37470             return;
37471         }
37472         var w = this.width;
37473         if(w){
37474             el.setWidth(w);
37475         }else if(Roo.isIE){
37476             el.setWidth(this.minWidth);
37477             var t = el.dom.offsetWidth; // force recalc
37478             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37479         }
37480     },
37481
37482     // private
37483     delayAutoWidth : function(){
37484         if(this.rendered){
37485             if(!this.awTask){
37486                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37487             }
37488             this.awTask.delay(20);
37489         }
37490     },
37491
37492     // private
37493     findTargetItem : function(e){
37494         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37495         if(t && t.menuItemId){
37496             return this.items.get(t.menuItemId);
37497         }
37498     },
37499
37500     // private
37501     onClick : function(e){
37502         Roo.log("menu.onClick");
37503         var t = this.findTargetItem(e);
37504         if(!t){
37505             return;
37506         }
37507         Roo.log(e);
37508         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37509             if(t == this.activeItem && t.shouldDeactivate(e)){
37510                 this.activeItem.deactivate();
37511                 delete this.activeItem;
37512                 return;
37513             }
37514             if(t.canActivate){
37515                 this.setActiveItem(t, true);
37516             }
37517             return;
37518             
37519             
37520         }
37521         
37522         t.onClick(e);
37523         this.fireEvent("click", this, t, e);
37524     },
37525
37526     // private
37527     setActiveItem : function(item, autoExpand){
37528         if(item != this.activeItem){
37529             if(this.activeItem){
37530                 this.activeItem.deactivate();
37531             }
37532             this.activeItem = item;
37533             item.activate(autoExpand);
37534         }else if(autoExpand){
37535             item.expandMenu();
37536         }
37537     },
37538
37539     // private
37540     tryActivate : function(start, step){
37541         var items = this.items;
37542         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37543             var item = items.get(i);
37544             if(!item.disabled && item.canActivate){
37545                 this.setActiveItem(item, false);
37546                 return item;
37547             }
37548         }
37549         return false;
37550     },
37551
37552     // private
37553     onMouseOver : function(e){
37554         var t;
37555         if(t = this.findTargetItem(e)){
37556             if(t.canActivate && !t.disabled){
37557                 this.setActiveItem(t, true);
37558             }
37559         }
37560         this.fireEvent("mouseover", this, e, t);
37561     },
37562
37563     // private
37564     onMouseOut : function(e){
37565         var t;
37566         if(t = this.findTargetItem(e)){
37567             if(t == this.activeItem && t.shouldDeactivate(e)){
37568                 this.activeItem.deactivate();
37569                 delete this.activeItem;
37570             }
37571         }
37572         this.fireEvent("mouseout", this, e, t);
37573     },
37574
37575     /**
37576      * Read-only.  Returns true if the menu is currently displayed, else false.
37577      * @type Boolean
37578      */
37579     isVisible : function(){
37580         return this.el && !this.hidden;
37581     },
37582
37583     /**
37584      * Displays this menu relative to another element
37585      * @param {String/HTMLElement/Roo.Element} element The element to align to
37586      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37587      * the element (defaults to this.defaultAlign)
37588      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37589      */
37590     show : function(el, pos, parentMenu){
37591         this.parentMenu = parentMenu;
37592         if(!this.el){
37593             this.render();
37594         }
37595         this.fireEvent("beforeshow", this);
37596         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37597     },
37598
37599     /**
37600      * Displays this menu at a specific xy position
37601      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37602      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37603      */
37604     showAt : function(xy, parentMenu, /* private: */_e){
37605         this.parentMenu = parentMenu;
37606         if(!this.el){
37607             this.render();
37608         }
37609         if(_e !== false){
37610             this.fireEvent("beforeshow", this);
37611             xy = this.el.adjustForConstraints(xy);
37612         }
37613         this.el.setXY(xy);
37614         this.el.show();
37615         this.hidden = false;
37616         this.focus();
37617         this.fireEvent("show", this);
37618     },
37619
37620     focus : function(){
37621         if(!this.hidden){
37622             this.doFocus.defer(50, this);
37623         }
37624     },
37625
37626     doFocus : function(){
37627         if(!this.hidden){
37628             this.focusEl.focus();
37629         }
37630     },
37631
37632     /**
37633      * Hides this menu and optionally all parent menus
37634      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37635      */
37636     hide : function(deep){
37637         if(this.el && this.isVisible()){
37638             this.fireEvent("beforehide", this);
37639             if(this.activeItem){
37640                 this.activeItem.deactivate();
37641                 this.activeItem = null;
37642             }
37643             this.el.hide();
37644             this.hidden = true;
37645             this.fireEvent("hide", this);
37646         }
37647         if(deep === true && this.parentMenu){
37648             this.parentMenu.hide(true);
37649         }
37650     },
37651
37652     /**
37653      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37654      * Any of the following are valid:
37655      * <ul>
37656      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37657      * <li>An HTMLElement object which will be converted to a menu item</li>
37658      * <li>A menu item config object that will be created as a new menu item</li>
37659      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37660      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37661      * </ul>
37662      * Usage:
37663      * <pre><code>
37664 // Create the menu
37665 var menu = new Roo.menu.Menu();
37666
37667 // Create a menu item to add by reference
37668 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37669
37670 // Add a bunch of items at once using different methods.
37671 // Only the last item added will be returned.
37672 var item = menu.add(
37673     menuItem,                // add existing item by ref
37674     'Dynamic Item',          // new TextItem
37675     '-',                     // new separator
37676     { text: 'Config Item' }  // new item by config
37677 );
37678 </code></pre>
37679      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37680      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37681      */
37682     add : function(){
37683         var a = arguments, l = a.length, item;
37684         for(var i = 0; i < l; i++){
37685             var el = a[i];
37686             if ((typeof(el) == "object") && el.xtype && el.xns) {
37687                 el = Roo.factory(el, Roo.menu);
37688             }
37689             
37690             if(el.render){ // some kind of Item
37691                 item = this.addItem(el);
37692             }else if(typeof el == "string"){ // string
37693                 if(el == "separator" || el == "-"){
37694                     item = this.addSeparator();
37695                 }else{
37696                     item = this.addText(el);
37697                 }
37698             }else if(el.tagName || el.el){ // element
37699                 item = this.addElement(el);
37700             }else if(typeof el == "object"){ // must be menu item config?
37701                 item = this.addMenuItem(el);
37702             }
37703         }
37704         return item;
37705     },
37706
37707     /**
37708      * Returns this menu's underlying {@link Roo.Element} object
37709      * @return {Roo.Element} The element
37710      */
37711     getEl : function(){
37712         if(!this.el){
37713             this.render();
37714         }
37715         return this.el;
37716     },
37717
37718     /**
37719      * Adds a separator bar to the menu
37720      * @return {Roo.menu.Item} The menu item that was added
37721      */
37722     addSeparator : function(){
37723         return this.addItem(new Roo.menu.Separator());
37724     },
37725
37726     /**
37727      * Adds an {@link Roo.Element} object to the menu
37728      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37729      * @return {Roo.menu.Item} The menu item that was added
37730      */
37731     addElement : function(el){
37732         return this.addItem(new Roo.menu.BaseItem(el));
37733     },
37734
37735     /**
37736      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37737      * @param {Roo.menu.Item} item The menu item to add
37738      * @return {Roo.menu.Item} The menu item that was added
37739      */
37740     addItem : function(item){
37741         this.items.add(item);
37742         if(this.ul){
37743             var li = document.createElement("li");
37744             li.className = "x-menu-list-item";
37745             this.ul.dom.appendChild(li);
37746             item.render(li, this);
37747             this.delayAutoWidth();
37748         }
37749         return item;
37750     },
37751
37752     /**
37753      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37754      * @param {Object} config A MenuItem config object
37755      * @return {Roo.menu.Item} The menu item that was added
37756      */
37757     addMenuItem : function(config){
37758         if(!(config instanceof Roo.menu.Item)){
37759             if(typeof config.checked == "boolean"){ // must be check menu item config?
37760                 config = new Roo.menu.CheckItem(config);
37761             }else{
37762                 config = new Roo.menu.Item(config);
37763             }
37764         }
37765         return this.addItem(config);
37766     },
37767
37768     /**
37769      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37770      * @param {String} text The text to display in the menu item
37771      * @return {Roo.menu.Item} The menu item that was added
37772      */
37773     addText : function(text){
37774         return this.addItem(new Roo.menu.TextItem({ text : text }));
37775     },
37776
37777     /**
37778      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37779      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37780      * @param {Roo.menu.Item} item The menu item to add
37781      * @return {Roo.menu.Item} The menu item that was added
37782      */
37783     insert : function(index, item){
37784         this.items.insert(index, item);
37785         if(this.ul){
37786             var li = document.createElement("li");
37787             li.className = "x-menu-list-item";
37788             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37789             item.render(li, this);
37790             this.delayAutoWidth();
37791         }
37792         return item;
37793     },
37794
37795     /**
37796      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37797      * @param {Roo.menu.Item} item The menu item to remove
37798      */
37799     remove : function(item){
37800         this.items.removeKey(item.id);
37801         item.destroy();
37802     },
37803
37804     /**
37805      * Removes and destroys all items in the menu
37806      */
37807     removeAll : function(){
37808         var f;
37809         while(f = this.items.first()){
37810             this.remove(f);
37811         }
37812     }
37813 });
37814
37815 // MenuNav is a private utility class used internally by the Menu
37816 Roo.menu.MenuNav = function(menu){
37817     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37818     this.scope = this.menu = menu;
37819 };
37820
37821 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37822     doRelay : function(e, h){
37823         var k = e.getKey();
37824         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37825             this.menu.tryActivate(0, 1);
37826             return false;
37827         }
37828         return h.call(this.scope || this, e, this.menu);
37829     },
37830
37831     up : function(e, m){
37832         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37833             m.tryActivate(m.items.length-1, -1);
37834         }
37835     },
37836
37837     down : function(e, m){
37838         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37839             m.tryActivate(0, 1);
37840         }
37841     },
37842
37843     right : function(e, m){
37844         if(m.activeItem){
37845             m.activeItem.expandMenu(true);
37846         }
37847     },
37848
37849     left : function(e, m){
37850         m.hide();
37851         if(m.parentMenu && m.parentMenu.activeItem){
37852             m.parentMenu.activeItem.activate();
37853         }
37854     },
37855
37856     enter : function(e, m){
37857         if(m.activeItem){
37858             e.stopPropagation();
37859             m.activeItem.onClick(e);
37860             m.fireEvent("click", this, m.activeItem);
37861             return true;
37862         }
37863     }
37864 });/*
37865  * Based on:
37866  * Ext JS Library 1.1.1
37867  * Copyright(c) 2006-2007, Ext JS, LLC.
37868  *
37869  * Originally Released Under LGPL - original licence link has changed is not relivant.
37870  *
37871  * Fork - LGPL
37872  * <script type="text/javascript">
37873  */
37874  
37875 /**
37876  * @class Roo.menu.MenuMgr
37877  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37878  * @singleton
37879  */
37880 Roo.menu.MenuMgr = function(){
37881    var menus, active, groups = {}, attached = false, lastShow = new Date();
37882
37883    // private - called when first menu is created
37884    function init(){
37885        menus = {};
37886        active = new Roo.util.MixedCollection();
37887        Roo.get(document).addKeyListener(27, function(){
37888            if(active.length > 0){
37889                hideAll();
37890            }
37891        });
37892    }
37893
37894    // private
37895    function hideAll(){
37896        if(active && active.length > 0){
37897            var c = active.clone();
37898            c.each(function(m){
37899                m.hide();
37900            });
37901        }
37902    }
37903
37904    // private
37905    function onHide(m){
37906        active.remove(m);
37907        if(active.length < 1){
37908            Roo.get(document).un("mousedown", onMouseDown);
37909            attached = false;
37910        }
37911    }
37912
37913    // private
37914    function onShow(m){
37915        var last = active.last();
37916        lastShow = new Date();
37917        active.add(m);
37918        if(!attached){
37919            Roo.get(document).on("mousedown", onMouseDown);
37920            attached = true;
37921        }
37922        if(m.parentMenu){
37923           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37924           m.parentMenu.activeChild = m;
37925        }else if(last && last.isVisible()){
37926           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37927        }
37928    }
37929
37930    // private
37931    function onBeforeHide(m){
37932        if(m.activeChild){
37933            m.activeChild.hide();
37934        }
37935        if(m.autoHideTimer){
37936            clearTimeout(m.autoHideTimer);
37937            delete m.autoHideTimer;
37938        }
37939    }
37940
37941    // private
37942    function onBeforeShow(m){
37943        var pm = m.parentMenu;
37944        if(!pm && !m.allowOtherMenus){
37945            hideAll();
37946        }else if(pm && pm.activeChild && active != m){
37947            pm.activeChild.hide();
37948        }
37949    }
37950
37951    // private
37952    function onMouseDown(e){
37953        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37954            hideAll();
37955        }
37956    }
37957
37958    // private
37959    function onBeforeCheck(mi, state){
37960        if(state){
37961            var g = groups[mi.group];
37962            for(var i = 0, l = g.length; i < l; i++){
37963                if(g[i] != mi){
37964                    g[i].setChecked(false);
37965                }
37966            }
37967        }
37968    }
37969
37970    return {
37971
37972        /**
37973         * Hides all menus that are currently visible
37974         */
37975        hideAll : function(){
37976             hideAll();  
37977        },
37978
37979        // private
37980        register : function(menu){
37981            if(!menus){
37982                init();
37983            }
37984            menus[menu.id] = menu;
37985            menu.on("beforehide", onBeforeHide);
37986            menu.on("hide", onHide);
37987            menu.on("beforeshow", onBeforeShow);
37988            menu.on("show", onShow);
37989            var g = menu.group;
37990            if(g && menu.events["checkchange"]){
37991                if(!groups[g]){
37992                    groups[g] = [];
37993                }
37994                groups[g].push(menu);
37995                menu.on("checkchange", onCheck);
37996            }
37997        },
37998
37999         /**
38000          * Returns a {@link Roo.menu.Menu} object
38001          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38002          * be used to generate and return a new Menu instance.
38003          */
38004        get : function(menu){
38005            if(typeof menu == "string"){ // menu id
38006                return menus[menu];
38007            }else if(menu.events){  // menu instance
38008                return menu;
38009            }else if(typeof menu.length == 'number'){ // array of menu items?
38010                return new Roo.menu.Menu({items:menu});
38011            }else{ // otherwise, must be a config
38012                return new Roo.menu.Menu(menu);
38013            }
38014        },
38015
38016        // private
38017        unregister : function(menu){
38018            delete menus[menu.id];
38019            menu.un("beforehide", onBeforeHide);
38020            menu.un("hide", onHide);
38021            menu.un("beforeshow", onBeforeShow);
38022            menu.un("show", onShow);
38023            var g = menu.group;
38024            if(g && menu.events["checkchange"]){
38025                groups[g].remove(menu);
38026                menu.un("checkchange", onCheck);
38027            }
38028        },
38029
38030        // private
38031        registerCheckable : function(menuItem){
38032            var g = menuItem.group;
38033            if(g){
38034                if(!groups[g]){
38035                    groups[g] = [];
38036                }
38037                groups[g].push(menuItem);
38038                menuItem.on("beforecheckchange", onBeforeCheck);
38039            }
38040        },
38041
38042        // private
38043        unregisterCheckable : function(menuItem){
38044            var g = menuItem.group;
38045            if(g){
38046                groups[g].remove(menuItem);
38047                menuItem.un("beforecheckchange", onBeforeCheck);
38048            }
38049        }
38050    };
38051 }();/*
38052  * Based on:
38053  * Ext JS Library 1.1.1
38054  * Copyright(c) 2006-2007, Ext JS, LLC.
38055  *
38056  * Originally Released Under LGPL - original licence link has changed is not relivant.
38057  *
38058  * Fork - LGPL
38059  * <script type="text/javascript">
38060  */
38061  
38062
38063 /**
38064  * @class Roo.menu.BaseItem
38065  * @extends Roo.Component
38066  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38067  * management and base configuration options shared by all menu components.
38068  * @constructor
38069  * Creates a new BaseItem
38070  * @param {Object} config Configuration options
38071  */
38072 Roo.menu.BaseItem = function(config){
38073     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38074
38075     this.addEvents({
38076         /**
38077          * @event click
38078          * Fires when this item is clicked
38079          * @param {Roo.menu.BaseItem} this
38080          * @param {Roo.EventObject} e
38081          */
38082         click: true,
38083         /**
38084          * @event activate
38085          * Fires when this item is activated
38086          * @param {Roo.menu.BaseItem} this
38087          */
38088         activate : true,
38089         /**
38090          * @event deactivate
38091          * Fires when this item is deactivated
38092          * @param {Roo.menu.BaseItem} this
38093          */
38094         deactivate : true
38095     });
38096
38097     if(this.handler){
38098         this.on("click", this.handler, this.scope, true);
38099     }
38100 };
38101
38102 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38103     /**
38104      * @cfg {Function} handler
38105      * A function that will handle the click event of this menu item (defaults to undefined)
38106      */
38107     /**
38108      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38109      */
38110     canActivate : false,
38111     
38112      /**
38113      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38114      */
38115     hidden: false,
38116     
38117     /**
38118      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38119      */
38120     activeClass : "x-menu-item-active",
38121     /**
38122      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38123      */
38124     hideOnClick : true,
38125     /**
38126      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38127      */
38128     hideDelay : 100,
38129
38130     // private
38131     ctype: "Roo.menu.BaseItem",
38132
38133     // private
38134     actionMode : "container",
38135
38136     // private
38137     render : function(container, parentMenu){
38138         this.parentMenu = parentMenu;
38139         Roo.menu.BaseItem.superclass.render.call(this, container);
38140         this.container.menuItemId = this.id;
38141     },
38142
38143     // private
38144     onRender : function(container, position){
38145         this.el = Roo.get(this.el);
38146         container.dom.appendChild(this.el.dom);
38147     },
38148
38149     // private
38150     onClick : function(e){
38151         if(!this.disabled && this.fireEvent("click", this, e) !== false
38152                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38153             this.handleClick(e);
38154         }else{
38155             e.stopEvent();
38156         }
38157     },
38158
38159     // private
38160     activate : function(){
38161         if(this.disabled){
38162             return false;
38163         }
38164         var li = this.container;
38165         li.addClass(this.activeClass);
38166         this.region = li.getRegion().adjust(2, 2, -2, -2);
38167         this.fireEvent("activate", this);
38168         return true;
38169     },
38170
38171     // private
38172     deactivate : function(){
38173         this.container.removeClass(this.activeClass);
38174         this.fireEvent("deactivate", this);
38175     },
38176
38177     // private
38178     shouldDeactivate : function(e){
38179         return !this.region || !this.region.contains(e.getPoint());
38180     },
38181
38182     // private
38183     handleClick : function(e){
38184         if(this.hideOnClick){
38185             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38186         }
38187     },
38188
38189     // private
38190     expandMenu : function(autoActivate){
38191         // do nothing
38192     },
38193
38194     // private
38195     hideMenu : function(){
38196         // do nothing
38197     }
38198 });/*
38199  * Based on:
38200  * Ext JS Library 1.1.1
38201  * Copyright(c) 2006-2007, Ext JS, LLC.
38202  *
38203  * Originally Released Under LGPL - original licence link has changed is not relivant.
38204  *
38205  * Fork - LGPL
38206  * <script type="text/javascript">
38207  */
38208  
38209 /**
38210  * @class Roo.menu.Adapter
38211  * @extends Roo.menu.BaseItem
38212  * 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.
38213  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38214  * @constructor
38215  * Creates a new Adapter
38216  * @param {Object} config Configuration options
38217  */
38218 Roo.menu.Adapter = function(component, config){
38219     Roo.menu.Adapter.superclass.constructor.call(this, config);
38220     this.component = component;
38221 };
38222 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38223     // private
38224     canActivate : true,
38225
38226     // private
38227     onRender : function(container, position){
38228         this.component.render(container);
38229         this.el = this.component.getEl();
38230     },
38231
38232     // private
38233     activate : function(){
38234         if(this.disabled){
38235             return false;
38236         }
38237         this.component.focus();
38238         this.fireEvent("activate", this);
38239         return true;
38240     },
38241
38242     // private
38243     deactivate : function(){
38244         this.fireEvent("deactivate", this);
38245     },
38246
38247     // private
38248     disable : function(){
38249         this.component.disable();
38250         Roo.menu.Adapter.superclass.disable.call(this);
38251     },
38252
38253     // private
38254     enable : function(){
38255         this.component.enable();
38256         Roo.menu.Adapter.superclass.enable.call(this);
38257     }
38258 });/*
38259  * Based on:
38260  * Ext JS Library 1.1.1
38261  * Copyright(c) 2006-2007, Ext JS, LLC.
38262  *
38263  * Originally Released Under LGPL - original licence link has changed is not relivant.
38264  *
38265  * Fork - LGPL
38266  * <script type="text/javascript">
38267  */
38268
38269 /**
38270  * @class Roo.menu.TextItem
38271  * @extends Roo.menu.BaseItem
38272  * Adds a static text string to a menu, usually used as either a heading or group separator.
38273  * Note: old style constructor with text is still supported.
38274  * 
38275  * @constructor
38276  * Creates a new TextItem
38277  * @param {Object} cfg Configuration
38278  */
38279 Roo.menu.TextItem = function(cfg){
38280     if (typeof(cfg) == 'string') {
38281         this.text = cfg;
38282     } else {
38283         Roo.apply(this,cfg);
38284     }
38285     
38286     Roo.menu.TextItem.superclass.constructor.call(this);
38287 };
38288
38289 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38290     /**
38291      * @cfg {Boolean} text Text to show on item.
38292      */
38293     text : '',
38294     
38295     /**
38296      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38297      */
38298     hideOnClick : false,
38299     /**
38300      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38301      */
38302     itemCls : "x-menu-text",
38303
38304     // private
38305     onRender : function(){
38306         var s = document.createElement("span");
38307         s.className = this.itemCls;
38308         s.innerHTML = this.text;
38309         this.el = s;
38310         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38311     }
38312 });/*
38313  * Based on:
38314  * Ext JS Library 1.1.1
38315  * Copyright(c) 2006-2007, Ext JS, LLC.
38316  *
38317  * Originally Released Under LGPL - original licence link has changed is not relivant.
38318  *
38319  * Fork - LGPL
38320  * <script type="text/javascript">
38321  */
38322
38323 /**
38324  * @class Roo.menu.Separator
38325  * @extends Roo.menu.BaseItem
38326  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38327  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38328  * @constructor
38329  * @param {Object} config Configuration options
38330  */
38331 Roo.menu.Separator = function(config){
38332     Roo.menu.Separator.superclass.constructor.call(this, config);
38333 };
38334
38335 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38336     /**
38337      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38338      */
38339     itemCls : "x-menu-sep",
38340     /**
38341      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38342      */
38343     hideOnClick : false,
38344
38345     // private
38346     onRender : function(li){
38347         var s = document.createElement("span");
38348         s.className = this.itemCls;
38349         s.innerHTML = "&#160;";
38350         this.el = s;
38351         li.addClass("x-menu-sep-li");
38352         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38353     }
38354 });/*
38355  * Based on:
38356  * Ext JS Library 1.1.1
38357  * Copyright(c) 2006-2007, Ext JS, LLC.
38358  *
38359  * Originally Released Under LGPL - original licence link has changed is not relivant.
38360  *
38361  * Fork - LGPL
38362  * <script type="text/javascript">
38363  */
38364 /**
38365  * @class Roo.menu.Item
38366  * @extends Roo.menu.BaseItem
38367  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38368  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38369  * activation and click handling.
38370  * @constructor
38371  * Creates a new Item
38372  * @param {Object} config Configuration options
38373  */
38374 Roo.menu.Item = function(config){
38375     Roo.menu.Item.superclass.constructor.call(this, config);
38376     if(this.menu){
38377         this.menu = Roo.menu.MenuMgr.get(this.menu);
38378     }
38379 };
38380 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38381     
38382     /**
38383      * @cfg {String} text
38384      * The text to show on the menu item.
38385      */
38386     text: '',
38387      /**
38388      * @cfg {String} HTML to render in menu
38389      * The text to show on the menu item (HTML version).
38390      */
38391     html: '',
38392     /**
38393      * @cfg {String} icon
38394      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38395      */
38396     icon: undefined,
38397     /**
38398      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38399      */
38400     itemCls : "x-menu-item",
38401     /**
38402      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38403      */
38404     canActivate : true,
38405     /**
38406      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38407      */
38408     showDelay: 200,
38409     // doc'd in BaseItem
38410     hideDelay: 200,
38411
38412     // private
38413     ctype: "Roo.menu.Item",
38414     
38415     // private
38416     onRender : function(container, position){
38417         var el = document.createElement("a");
38418         el.hideFocus = true;
38419         el.unselectable = "on";
38420         el.href = this.href || "#";
38421         if(this.hrefTarget){
38422             el.target = this.hrefTarget;
38423         }
38424         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38425         
38426         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38427         
38428         el.innerHTML = String.format(
38429                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38430                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38431         this.el = el;
38432         Roo.menu.Item.superclass.onRender.call(this, container, position);
38433     },
38434
38435     /**
38436      * Sets the text to display in this menu item
38437      * @param {String} text The text to display
38438      * @param {Boolean} isHTML true to indicate text is pure html.
38439      */
38440     setText : function(text, isHTML){
38441         if (isHTML) {
38442             this.html = text;
38443         } else {
38444             this.text = text;
38445             this.html = '';
38446         }
38447         if(this.rendered){
38448             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38449      
38450             this.el.update(String.format(
38451                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38452                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38453             this.parentMenu.autoWidth();
38454         }
38455     },
38456
38457     // private
38458     handleClick : function(e){
38459         if(!this.href){ // if no link defined, stop the event automatically
38460             e.stopEvent();
38461         }
38462         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38463     },
38464
38465     // private
38466     activate : function(autoExpand){
38467         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38468             this.focus();
38469             if(autoExpand){
38470                 this.expandMenu();
38471             }
38472         }
38473         return true;
38474     },
38475
38476     // private
38477     shouldDeactivate : function(e){
38478         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38479             if(this.menu && this.menu.isVisible()){
38480                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38481             }
38482             return true;
38483         }
38484         return false;
38485     },
38486
38487     // private
38488     deactivate : function(){
38489         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38490         this.hideMenu();
38491     },
38492
38493     // private
38494     expandMenu : function(autoActivate){
38495         if(!this.disabled && this.menu){
38496             clearTimeout(this.hideTimer);
38497             delete this.hideTimer;
38498             if(!this.menu.isVisible() && !this.showTimer){
38499                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38500             }else if (this.menu.isVisible() && autoActivate){
38501                 this.menu.tryActivate(0, 1);
38502             }
38503         }
38504     },
38505
38506     // private
38507     deferExpand : function(autoActivate){
38508         delete this.showTimer;
38509         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38510         if(autoActivate){
38511             this.menu.tryActivate(0, 1);
38512         }
38513     },
38514
38515     // private
38516     hideMenu : function(){
38517         clearTimeout(this.showTimer);
38518         delete this.showTimer;
38519         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38520             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38521         }
38522     },
38523
38524     // private
38525     deferHide : function(){
38526         delete this.hideTimer;
38527         this.menu.hide();
38528     }
38529 });/*
38530  * Based on:
38531  * Ext JS Library 1.1.1
38532  * Copyright(c) 2006-2007, Ext JS, LLC.
38533  *
38534  * Originally Released Under LGPL - original licence link has changed is not relivant.
38535  *
38536  * Fork - LGPL
38537  * <script type="text/javascript">
38538  */
38539  
38540 /**
38541  * @class Roo.menu.CheckItem
38542  * @extends Roo.menu.Item
38543  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38544  * @constructor
38545  * Creates a new CheckItem
38546  * @param {Object} config Configuration options
38547  */
38548 Roo.menu.CheckItem = function(config){
38549     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38550     this.addEvents({
38551         /**
38552          * @event beforecheckchange
38553          * Fires before the checked value is set, providing an opportunity to cancel if needed
38554          * @param {Roo.menu.CheckItem} this
38555          * @param {Boolean} checked The new checked value that will be set
38556          */
38557         "beforecheckchange" : true,
38558         /**
38559          * @event checkchange
38560          * Fires after the checked value has been set
38561          * @param {Roo.menu.CheckItem} this
38562          * @param {Boolean} checked The checked value that was set
38563          */
38564         "checkchange" : true
38565     });
38566     if(this.checkHandler){
38567         this.on('checkchange', this.checkHandler, this.scope);
38568     }
38569 };
38570 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38571     /**
38572      * @cfg {String} group
38573      * All check items with the same group name will automatically be grouped into a single-select
38574      * radio button group (defaults to '')
38575      */
38576     /**
38577      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38578      */
38579     itemCls : "x-menu-item x-menu-check-item",
38580     /**
38581      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38582      */
38583     groupClass : "x-menu-group-item",
38584
38585     /**
38586      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38587      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38588      * initialized with checked = true will be rendered as checked.
38589      */
38590     checked: false,
38591
38592     // private
38593     ctype: "Roo.menu.CheckItem",
38594
38595     // private
38596     onRender : function(c){
38597         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38598         if(this.group){
38599             this.el.addClass(this.groupClass);
38600         }
38601         Roo.menu.MenuMgr.registerCheckable(this);
38602         if(this.checked){
38603             this.checked = false;
38604             this.setChecked(true, true);
38605         }
38606     },
38607
38608     // private
38609     destroy : function(){
38610         if(this.rendered){
38611             Roo.menu.MenuMgr.unregisterCheckable(this);
38612         }
38613         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38614     },
38615
38616     /**
38617      * Set the checked state of this item
38618      * @param {Boolean} checked The new checked value
38619      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38620      */
38621     setChecked : function(state, suppressEvent){
38622         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38623             if(this.container){
38624                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38625             }
38626             this.checked = state;
38627             if(suppressEvent !== true){
38628                 this.fireEvent("checkchange", this, state);
38629             }
38630         }
38631     },
38632
38633     // private
38634     handleClick : function(e){
38635        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38636            this.setChecked(!this.checked);
38637        }
38638        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38639     }
38640 });/*
38641  * Based on:
38642  * Ext JS Library 1.1.1
38643  * Copyright(c) 2006-2007, Ext JS, LLC.
38644  *
38645  * Originally Released Under LGPL - original licence link has changed is not relivant.
38646  *
38647  * Fork - LGPL
38648  * <script type="text/javascript">
38649  */
38650  
38651 /**
38652  * @class Roo.menu.DateItem
38653  * @extends Roo.menu.Adapter
38654  * A menu item that wraps the {@link Roo.DatPicker} component.
38655  * @constructor
38656  * Creates a new DateItem
38657  * @param {Object} config Configuration options
38658  */
38659 Roo.menu.DateItem = function(config){
38660     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38661     /** The Roo.DatePicker object @type Roo.DatePicker */
38662     this.picker = this.component;
38663     this.addEvents({select: true});
38664     
38665     this.picker.on("render", function(picker){
38666         picker.getEl().swallowEvent("click");
38667         picker.container.addClass("x-menu-date-item");
38668     });
38669
38670     this.picker.on("select", this.onSelect, this);
38671 };
38672
38673 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38674     // private
38675     onSelect : function(picker, date){
38676         this.fireEvent("select", this, date, picker);
38677         Roo.menu.DateItem.superclass.handleClick.call(this);
38678     }
38679 });/*
38680  * Based on:
38681  * Ext JS Library 1.1.1
38682  * Copyright(c) 2006-2007, Ext JS, LLC.
38683  *
38684  * Originally Released Under LGPL - original licence link has changed is not relivant.
38685  *
38686  * Fork - LGPL
38687  * <script type="text/javascript">
38688  */
38689  
38690 /**
38691  * @class Roo.menu.ColorItem
38692  * @extends Roo.menu.Adapter
38693  * A menu item that wraps the {@link Roo.ColorPalette} component.
38694  * @constructor
38695  * Creates a new ColorItem
38696  * @param {Object} config Configuration options
38697  */
38698 Roo.menu.ColorItem = function(config){
38699     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38700     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38701     this.palette = this.component;
38702     this.relayEvents(this.palette, ["select"]);
38703     if(this.selectHandler){
38704         this.on('select', this.selectHandler, this.scope);
38705     }
38706 };
38707 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38708  * Based on:
38709  * Ext JS Library 1.1.1
38710  * Copyright(c) 2006-2007, Ext JS, LLC.
38711  *
38712  * Originally Released Under LGPL - original licence link has changed is not relivant.
38713  *
38714  * Fork - LGPL
38715  * <script type="text/javascript">
38716  */
38717  
38718
38719 /**
38720  * @class Roo.menu.DateMenu
38721  * @extends Roo.menu.Menu
38722  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38723  * @constructor
38724  * Creates a new DateMenu
38725  * @param {Object} config Configuration options
38726  */
38727 Roo.menu.DateMenu = function(config){
38728     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38729     this.plain = true;
38730     var di = new Roo.menu.DateItem(config);
38731     this.add(di);
38732     /**
38733      * The {@link Roo.DatePicker} instance for this DateMenu
38734      * @type DatePicker
38735      */
38736     this.picker = di.picker;
38737     /**
38738      * @event select
38739      * @param {DatePicker} picker
38740      * @param {Date} date
38741      */
38742     this.relayEvents(di, ["select"]);
38743     this.on('beforeshow', function(){
38744         if(this.picker){
38745             this.picker.hideMonthPicker(false);
38746         }
38747     }, this);
38748 };
38749 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38750     cls:'x-date-menu'
38751 });/*
38752  * Based on:
38753  * Ext JS Library 1.1.1
38754  * Copyright(c) 2006-2007, Ext JS, LLC.
38755  *
38756  * Originally Released Under LGPL - original licence link has changed is not relivant.
38757  *
38758  * Fork - LGPL
38759  * <script type="text/javascript">
38760  */
38761  
38762
38763 /**
38764  * @class Roo.menu.ColorMenu
38765  * @extends Roo.menu.Menu
38766  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38767  * @constructor
38768  * Creates a new ColorMenu
38769  * @param {Object} config Configuration options
38770  */
38771 Roo.menu.ColorMenu = function(config){
38772     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38773     this.plain = true;
38774     var ci = new Roo.menu.ColorItem(config);
38775     this.add(ci);
38776     /**
38777      * The {@link Roo.ColorPalette} instance for this ColorMenu
38778      * @type ColorPalette
38779      */
38780     this.palette = ci.palette;
38781     /**
38782      * @event select
38783      * @param {ColorPalette} palette
38784      * @param {String} color
38785      */
38786     this.relayEvents(ci, ["select"]);
38787 };
38788 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38789  * Based on:
38790  * Ext JS Library 1.1.1
38791  * Copyright(c) 2006-2007, Ext JS, LLC.
38792  *
38793  * Originally Released Under LGPL - original licence link has changed is not relivant.
38794  *
38795  * Fork - LGPL
38796  * <script type="text/javascript">
38797  */
38798  
38799 /**
38800  * @class Roo.form.TextItem
38801  * @extends Roo.BoxComponent
38802  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38803  * @constructor
38804  * Creates a new TextItem
38805  * @param {Object} config Configuration options
38806  */
38807 Roo.form.TextItem = function(config){
38808     Roo.form.TextItem.superclass.constructor.call(this, config);
38809 };
38810
38811 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38812     
38813     /**
38814      * @cfg {String} tag the tag for this item (default div)
38815      */
38816     tag : 'div',
38817     /**
38818      * @cfg {String} html the content for this item
38819      */
38820     html : '',
38821     
38822     getAutoCreate : function()
38823     {
38824         var cfg = {
38825             id: this.id,
38826             tag: this.tag,
38827             html: this.html,
38828             cls: 'x-form-item'
38829         };
38830         
38831         return cfg;
38832         
38833     },
38834     
38835     onRender : function(ct, position)
38836     {
38837         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38838         
38839         if(!this.el){
38840             var cfg = this.getAutoCreate();
38841             if(!cfg.name){
38842                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38843             }
38844             if (!cfg.name.length) {
38845                 delete cfg.name;
38846             }
38847             this.el = ct.createChild(cfg, position);
38848         }
38849     }
38850     
38851 });/*
38852  * Based on:
38853  * Ext JS Library 1.1.1
38854  * Copyright(c) 2006-2007, Ext JS, LLC.
38855  *
38856  * Originally Released Under LGPL - original licence link has changed is not relivant.
38857  *
38858  * Fork - LGPL
38859  * <script type="text/javascript">
38860  */
38861  
38862 /**
38863  * @class Roo.form.Field
38864  * @extends Roo.BoxComponent
38865  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38866  * @constructor
38867  * Creates a new Field
38868  * @param {Object} config Configuration options
38869  */
38870 Roo.form.Field = function(config){
38871     Roo.form.Field.superclass.constructor.call(this, config);
38872 };
38873
38874 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38875     /**
38876      * @cfg {String} fieldLabel Label to use when rendering a form.
38877      */
38878        /**
38879      * @cfg {String} qtip Mouse over tip
38880      */
38881      
38882     /**
38883      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38884      */
38885     invalidClass : "x-form-invalid",
38886     /**
38887      * @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")
38888      */
38889     invalidText : "The value in this field is invalid",
38890     /**
38891      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38892      */
38893     focusClass : "x-form-focus",
38894     /**
38895      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38896       automatic validation (defaults to "keyup").
38897      */
38898     validationEvent : "keyup",
38899     /**
38900      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38901      */
38902     validateOnBlur : true,
38903     /**
38904      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38905      */
38906     validationDelay : 250,
38907     /**
38908      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38909      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38910      */
38911     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38912     /**
38913      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38914      */
38915     fieldClass : "x-form-field",
38916     /**
38917      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38918      *<pre>
38919 Value         Description
38920 -----------   ----------------------------------------------------------------------
38921 qtip          Display a quick tip when the user hovers over the field
38922 title         Display a default browser title attribute popup
38923 under         Add a block div beneath the field containing the error text
38924 side          Add an error icon to the right of the field with a popup on hover
38925 [element id]  Add the error text directly to the innerHTML of the specified element
38926 </pre>
38927      */
38928     msgTarget : 'qtip',
38929     /**
38930      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38931      */
38932     msgFx : 'normal',
38933
38934     /**
38935      * @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.
38936      */
38937     readOnly : false,
38938
38939     /**
38940      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38941      */
38942     disabled : false,
38943
38944     /**
38945      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38946      */
38947     inputType : undefined,
38948     
38949     /**
38950      * @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).
38951          */
38952         tabIndex : undefined,
38953         
38954     // private
38955     isFormField : true,
38956
38957     // private
38958     hasFocus : false,
38959     /**
38960      * @property {Roo.Element} fieldEl
38961      * Element Containing the rendered Field (with label etc.)
38962      */
38963     /**
38964      * @cfg {Mixed} value A value to initialize this field with.
38965      */
38966     value : undefined,
38967
38968     /**
38969      * @cfg {String} name The field's HTML name attribute.
38970      */
38971     /**
38972      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38973      */
38974     // private
38975     loadedValue : false,
38976      
38977      
38978         // private ??
38979         initComponent : function(){
38980         Roo.form.Field.superclass.initComponent.call(this);
38981         this.addEvents({
38982             /**
38983              * @event focus
38984              * Fires when this field receives input focus.
38985              * @param {Roo.form.Field} this
38986              */
38987             focus : true,
38988             /**
38989              * @event blur
38990              * Fires when this field loses input focus.
38991              * @param {Roo.form.Field} this
38992              */
38993             blur : true,
38994             /**
38995              * @event specialkey
38996              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38997              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38998              * @param {Roo.form.Field} this
38999              * @param {Roo.EventObject} e The event object
39000              */
39001             specialkey : true,
39002             /**
39003              * @event change
39004              * Fires just before the field blurs if the field value has changed.
39005              * @param {Roo.form.Field} this
39006              * @param {Mixed} newValue The new value
39007              * @param {Mixed} oldValue The original value
39008              */
39009             change : true,
39010             /**
39011              * @event invalid
39012              * Fires after the field has been marked as invalid.
39013              * @param {Roo.form.Field} this
39014              * @param {String} msg The validation message
39015              */
39016             invalid : true,
39017             /**
39018              * @event valid
39019              * Fires after the field has been validated with no errors.
39020              * @param {Roo.form.Field} this
39021              */
39022             valid : true,
39023              /**
39024              * @event keyup
39025              * Fires after the key up
39026              * @param {Roo.form.Field} this
39027              * @param {Roo.EventObject}  e The event Object
39028              */
39029             keyup : true
39030         });
39031     },
39032
39033     /**
39034      * Returns the name attribute of the field if available
39035      * @return {String} name The field name
39036      */
39037     getName: function(){
39038          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39039     },
39040
39041     // private
39042     onRender : function(ct, position){
39043         Roo.form.Field.superclass.onRender.call(this, ct, position);
39044         if(!this.el){
39045             var cfg = this.getAutoCreate();
39046             if(!cfg.name){
39047                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39048             }
39049             if (!cfg.name.length) {
39050                 delete cfg.name;
39051             }
39052             if(this.inputType){
39053                 cfg.type = this.inputType;
39054             }
39055             this.el = ct.createChild(cfg, position);
39056         }
39057         var type = this.el.dom.type;
39058         if(type){
39059             if(type == 'password'){
39060                 type = 'text';
39061             }
39062             this.el.addClass('x-form-'+type);
39063         }
39064         if(this.readOnly){
39065             this.el.dom.readOnly = true;
39066         }
39067         if(this.tabIndex !== undefined){
39068             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39069         }
39070
39071         this.el.addClass([this.fieldClass, this.cls]);
39072         this.initValue();
39073     },
39074
39075     /**
39076      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39077      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39078      * @return {Roo.form.Field} this
39079      */
39080     applyTo : function(target){
39081         this.allowDomMove = false;
39082         this.el = Roo.get(target);
39083         this.render(this.el.dom.parentNode);
39084         return this;
39085     },
39086
39087     // private
39088     initValue : function(){
39089         if(this.value !== undefined){
39090             this.setValue(this.value);
39091         }else if(this.el.dom.value.length > 0){
39092             this.setValue(this.el.dom.value);
39093         }
39094     },
39095
39096     /**
39097      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39098      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39099      */
39100     isDirty : function() {
39101         if(this.disabled) {
39102             return false;
39103         }
39104         return String(this.getValue()) !== String(this.originalValue);
39105     },
39106
39107     /**
39108      * stores the current value in loadedValue
39109      */
39110     resetHasChanged : function()
39111     {
39112         this.loadedValue = String(this.getValue());
39113     },
39114     /**
39115      * checks the current value against the 'loaded' value.
39116      * Note - will return false if 'resetHasChanged' has not been called first.
39117      */
39118     hasChanged : function()
39119     {
39120         if(this.disabled || this.readOnly) {
39121             return false;
39122         }
39123         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39124     },
39125     
39126     
39127     
39128     // private
39129     afterRender : function(){
39130         Roo.form.Field.superclass.afterRender.call(this);
39131         this.initEvents();
39132     },
39133
39134     // private
39135     fireKey : function(e){
39136         //Roo.log('field ' + e.getKey());
39137         if(e.isNavKeyPress()){
39138             this.fireEvent("specialkey", this, e);
39139         }
39140     },
39141
39142     /**
39143      * Resets the current field value to the originally loaded value and clears any validation messages
39144      */
39145     reset : function(){
39146         this.setValue(this.resetValue);
39147         this.originalValue = this.getValue();
39148         this.clearInvalid();
39149     },
39150
39151     // private
39152     initEvents : function(){
39153         // safari killled keypress - so keydown is now used..
39154         this.el.on("keydown" , this.fireKey,  this);
39155         this.el.on("focus", this.onFocus,  this);
39156         this.el.on("blur", this.onBlur,  this);
39157         this.el.relayEvent('keyup', this);
39158
39159         // reference to original value for reset
39160         this.originalValue = this.getValue();
39161         this.resetValue =  this.getValue();
39162     },
39163
39164     // private
39165     onFocus : function(){
39166         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39167             this.el.addClass(this.focusClass);
39168         }
39169         if(!this.hasFocus){
39170             this.hasFocus = true;
39171             this.startValue = this.getValue();
39172             this.fireEvent("focus", this);
39173         }
39174     },
39175
39176     beforeBlur : Roo.emptyFn,
39177
39178     // private
39179     onBlur : function(){
39180         this.beforeBlur();
39181         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39182             this.el.removeClass(this.focusClass);
39183         }
39184         this.hasFocus = false;
39185         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39186             this.validate();
39187         }
39188         var v = this.getValue();
39189         if(String(v) !== String(this.startValue)){
39190             this.fireEvent('change', this, v, this.startValue);
39191         }
39192         this.fireEvent("blur", this);
39193     },
39194
39195     /**
39196      * Returns whether or not the field value is currently valid
39197      * @param {Boolean} preventMark True to disable marking the field invalid
39198      * @return {Boolean} True if the value is valid, else false
39199      */
39200     isValid : function(preventMark){
39201         if(this.disabled){
39202             return true;
39203         }
39204         var restore = this.preventMark;
39205         this.preventMark = preventMark === true;
39206         var v = this.validateValue(this.processValue(this.getRawValue()));
39207         this.preventMark = restore;
39208         return v;
39209     },
39210
39211     /**
39212      * Validates the field value
39213      * @return {Boolean} True if the value is valid, else false
39214      */
39215     validate : function(){
39216         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39217             this.clearInvalid();
39218             return true;
39219         }
39220         return false;
39221     },
39222
39223     processValue : function(value){
39224         return value;
39225     },
39226
39227     // private
39228     // Subclasses should provide the validation implementation by overriding this
39229     validateValue : function(value){
39230         return true;
39231     },
39232
39233     /**
39234      * Mark this field as invalid
39235      * @param {String} msg The validation message
39236      */
39237     markInvalid : function(msg){
39238         if(!this.rendered || this.preventMark){ // not rendered
39239             return;
39240         }
39241         
39242         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39243         
39244         obj.el.addClass(this.invalidClass);
39245         msg = msg || this.invalidText;
39246         switch(this.msgTarget){
39247             case 'qtip':
39248                 obj.el.dom.qtip = msg;
39249                 obj.el.dom.qclass = 'x-form-invalid-tip';
39250                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39251                     Roo.QuickTips.enable();
39252                 }
39253                 break;
39254             case 'title':
39255                 this.el.dom.title = msg;
39256                 break;
39257             case 'under':
39258                 if(!this.errorEl){
39259                     var elp = this.el.findParent('.x-form-element', 5, true);
39260                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39261                     this.errorEl.setWidth(elp.getWidth(true)-20);
39262                 }
39263                 this.errorEl.update(msg);
39264                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39265                 break;
39266             case 'side':
39267                 if(!this.errorIcon){
39268                     var elp = this.el.findParent('.x-form-element', 5, true);
39269                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39270                 }
39271                 this.alignErrorIcon();
39272                 this.errorIcon.dom.qtip = msg;
39273                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39274                 this.errorIcon.show();
39275                 this.on('resize', this.alignErrorIcon, this);
39276                 break;
39277             default:
39278                 var t = Roo.getDom(this.msgTarget);
39279                 t.innerHTML = msg;
39280                 t.style.display = this.msgDisplay;
39281                 break;
39282         }
39283         this.fireEvent('invalid', this, msg);
39284     },
39285
39286     // private
39287     alignErrorIcon : function(){
39288         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39289     },
39290
39291     /**
39292      * Clear any invalid styles/messages for this field
39293      */
39294     clearInvalid : function(){
39295         if(!this.rendered || this.preventMark){ // not rendered
39296             return;
39297         }
39298         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39299         
39300         obj.el.removeClass(this.invalidClass);
39301         switch(this.msgTarget){
39302             case 'qtip':
39303                 obj.el.dom.qtip = '';
39304                 break;
39305             case 'title':
39306                 this.el.dom.title = '';
39307                 break;
39308             case 'under':
39309                 if(this.errorEl){
39310                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39311                 }
39312                 break;
39313             case 'side':
39314                 if(this.errorIcon){
39315                     this.errorIcon.dom.qtip = '';
39316                     this.errorIcon.hide();
39317                     this.un('resize', this.alignErrorIcon, this);
39318                 }
39319                 break;
39320             default:
39321                 var t = Roo.getDom(this.msgTarget);
39322                 t.innerHTML = '';
39323                 t.style.display = 'none';
39324                 break;
39325         }
39326         this.fireEvent('valid', this);
39327     },
39328
39329     /**
39330      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39331      * @return {Mixed} value The field value
39332      */
39333     getRawValue : function(){
39334         var v = this.el.getValue();
39335         
39336         return v;
39337     },
39338
39339     /**
39340      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39341      * @return {Mixed} value The field value
39342      */
39343     getValue : function(){
39344         var v = this.el.getValue();
39345          
39346         return v;
39347     },
39348
39349     /**
39350      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39351      * @param {Mixed} value The value to set
39352      */
39353     setRawValue : function(v){
39354         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39355     },
39356
39357     /**
39358      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39359      * @param {Mixed} value The value to set
39360      */
39361     setValue : function(v){
39362         this.value = v;
39363         if(this.rendered){
39364             this.el.dom.value = (v === null || v === undefined ? '' : v);
39365              this.validate();
39366         }
39367     },
39368
39369     adjustSize : function(w, h){
39370         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39371         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39372         return s;
39373     },
39374
39375     adjustWidth : function(tag, w){
39376         tag = tag.toLowerCase();
39377         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39378             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39379                 if(tag == 'input'){
39380                     return w + 2;
39381                 }
39382                 if(tag == 'textarea'){
39383                     return w-2;
39384                 }
39385             }else if(Roo.isOpera){
39386                 if(tag == 'input'){
39387                     return w + 2;
39388                 }
39389                 if(tag == 'textarea'){
39390                     return w-2;
39391                 }
39392             }
39393         }
39394         return w;
39395     }
39396 });
39397
39398
39399 // anything other than normal should be considered experimental
39400 Roo.form.Field.msgFx = {
39401     normal : {
39402         show: function(msgEl, f){
39403             msgEl.setDisplayed('block');
39404         },
39405
39406         hide : function(msgEl, f){
39407             msgEl.setDisplayed(false).update('');
39408         }
39409     },
39410
39411     slide : {
39412         show: function(msgEl, f){
39413             msgEl.slideIn('t', {stopFx:true});
39414         },
39415
39416         hide : function(msgEl, f){
39417             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39418         }
39419     },
39420
39421     slideRight : {
39422         show: function(msgEl, f){
39423             msgEl.fixDisplay();
39424             msgEl.alignTo(f.el, 'tl-tr');
39425             msgEl.slideIn('l', {stopFx:true});
39426         },
39427
39428         hide : function(msgEl, f){
39429             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39430         }
39431     }
39432 };/*
39433  * Based on:
39434  * Ext JS Library 1.1.1
39435  * Copyright(c) 2006-2007, Ext JS, LLC.
39436  *
39437  * Originally Released Under LGPL - original licence link has changed is not relivant.
39438  *
39439  * Fork - LGPL
39440  * <script type="text/javascript">
39441  */
39442  
39443
39444 /**
39445  * @class Roo.form.TextField
39446  * @extends Roo.form.Field
39447  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39448  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39449  * @constructor
39450  * Creates a new TextField
39451  * @param {Object} config Configuration options
39452  */
39453 Roo.form.TextField = function(config){
39454     Roo.form.TextField.superclass.constructor.call(this, config);
39455     this.addEvents({
39456         /**
39457          * @event autosize
39458          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39459          * according to the default logic, but this event provides a hook for the developer to apply additional
39460          * logic at runtime to resize the field if needed.
39461              * @param {Roo.form.Field} this This text field
39462              * @param {Number} width The new field width
39463              */
39464         autosize : true
39465     });
39466 };
39467
39468 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39469     /**
39470      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39471      */
39472     grow : false,
39473     /**
39474      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39475      */
39476     growMin : 30,
39477     /**
39478      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39479      */
39480     growMax : 800,
39481     /**
39482      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39483      */
39484     vtype : null,
39485     /**
39486      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39487      */
39488     maskRe : null,
39489     /**
39490      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39491      */
39492     disableKeyFilter : false,
39493     /**
39494      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39495      */
39496     allowBlank : true,
39497     /**
39498      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39499      */
39500     minLength : 0,
39501     /**
39502      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39503      */
39504     maxLength : Number.MAX_VALUE,
39505     /**
39506      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39507      */
39508     minLengthText : "The minimum length for this field is {0}",
39509     /**
39510      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39511      */
39512     maxLengthText : "The maximum length for this field is {0}",
39513     /**
39514      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39515      */
39516     selectOnFocus : false,
39517     /**
39518      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39519      */    
39520     allowLeadingSpace : false,
39521     /**
39522      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39523      */
39524     blankText : "This field is required",
39525     /**
39526      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39527      * If available, this function will be called only after the basic validators all return true, and will be passed the
39528      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39529      */
39530     validator : null,
39531     /**
39532      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39533      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39534      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39535      */
39536     regex : null,
39537     /**
39538      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39539      */
39540     regexText : "",
39541     /**
39542      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39543      */
39544     emptyText : null,
39545    
39546
39547     // private
39548     initEvents : function()
39549     {
39550         if (this.emptyText) {
39551             this.el.attr('placeholder', this.emptyText);
39552         }
39553         
39554         Roo.form.TextField.superclass.initEvents.call(this);
39555         if(this.validationEvent == 'keyup'){
39556             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39557             this.el.on('keyup', this.filterValidation, this);
39558         }
39559         else if(this.validationEvent !== false){
39560             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39561         }
39562         
39563         if(this.selectOnFocus){
39564             this.on("focus", this.preFocus, this);
39565         }
39566         if (!this.allowLeadingSpace) {
39567             this.on('blur', this.cleanLeadingSpace, this);
39568         }
39569         
39570         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39571             this.el.on("keypress", this.filterKeys, this);
39572         }
39573         if(this.grow){
39574             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39575             this.el.on("click", this.autoSize,  this);
39576         }
39577         if(this.el.is('input[type=password]') && Roo.isSafari){
39578             this.el.on('keydown', this.SafariOnKeyDown, this);
39579         }
39580     },
39581
39582     processValue : function(value){
39583         if(this.stripCharsRe){
39584             var newValue = value.replace(this.stripCharsRe, '');
39585             if(newValue !== value){
39586                 this.setRawValue(newValue);
39587                 return newValue;
39588             }
39589         }
39590         return value;
39591     },
39592
39593     filterValidation : function(e){
39594         if(!e.isNavKeyPress()){
39595             this.validationTask.delay(this.validationDelay);
39596         }
39597     },
39598
39599     // private
39600     onKeyUp : function(e){
39601         if(!e.isNavKeyPress()){
39602             this.autoSize();
39603         }
39604     },
39605     // private - clean the leading white space
39606     cleanLeadingSpace : function(e)
39607     {
39608         if ( this.inputType == 'file') {
39609             return;
39610         }
39611         
39612         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39613     },
39614     /**
39615      * Resets the current field value to the originally-loaded value and clears any validation messages.
39616      *  
39617      */
39618     reset : function(){
39619         Roo.form.TextField.superclass.reset.call(this);
39620        
39621     }, 
39622     // private
39623     preFocus : function(){
39624         
39625         if(this.selectOnFocus){
39626             this.el.dom.select();
39627         }
39628     },
39629
39630     
39631     // private
39632     filterKeys : function(e){
39633         var k = e.getKey();
39634         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39635             return;
39636         }
39637         var c = e.getCharCode(), cc = String.fromCharCode(c);
39638         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39639             return;
39640         }
39641         if(!this.maskRe.test(cc)){
39642             e.stopEvent();
39643         }
39644     },
39645
39646     setValue : function(v){
39647         
39648         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39649         
39650         this.autoSize();
39651     },
39652
39653     /**
39654      * Validates a value according to the field's validation rules and marks the field as invalid
39655      * if the validation fails
39656      * @param {Mixed} value The value to validate
39657      * @return {Boolean} True if the value is valid, else false
39658      */
39659     validateValue : function(value){
39660         if(value.length < 1)  { // if it's blank
39661              if(this.allowBlank){
39662                 this.clearInvalid();
39663                 return true;
39664              }else{
39665                 this.markInvalid(this.blankText);
39666                 return false;
39667              }
39668         }
39669         if(value.length < this.minLength){
39670             this.markInvalid(String.format(this.minLengthText, this.minLength));
39671             return false;
39672         }
39673         if(value.length > this.maxLength){
39674             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39675             return false;
39676         }
39677         if(this.vtype){
39678             var vt = Roo.form.VTypes;
39679             if(!vt[this.vtype](value, this)){
39680                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39681                 return false;
39682             }
39683         }
39684         if(typeof this.validator == "function"){
39685             var msg = this.validator(value);
39686             if(msg !== true){
39687                 this.markInvalid(msg);
39688                 return false;
39689             }
39690         }
39691         if(this.regex && !this.regex.test(value)){
39692             this.markInvalid(this.regexText);
39693             return false;
39694         }
39695         return true;
39696     },
39697
39698     /**
39699      * Selects text in this field
39700      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39701      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39702      */
39703     selectText : function(start, end){
39704         var v = this.getRawValue();
39705         if(v.length > 0){
39706             start = start === undefined ? 0 : start;
39707             end = end === undefined ? v.length : end;
39708             var d = this.el.dom;
39709             if(d.setSelectionRange){
39710                 d.setSelectionRange(start, end);
39711             }else if(d.createTextRange){
39712                 var range = d.createTextRange();
39713                 range.moveStart("character", start);
39714                 range.moveEnd("character", v.length-end);
39715                 range.select();
39716             }
39717         }
39718     },
39719
39720     /**
39721      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39722      * This only takes effect if grow = true, and fires the autosize event.
39723      */
39724     autoSize : function(){
39725         if(!this.grow || !this.rendered){
39726             return;
39727         }
39728         if(!this.metrics){
39729             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39730         }
39731         var el = this.el;
39732         var v = el.dom.value;
39733         var d = document.createElement('div');
39734         d.appendChild(document.createTextNode(v));
39735         v = d.innerHTML;
39736         d = null;
39737         v += "&#160;";
39738         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39739         this.el.setWidth(w);
39740         this.fireEvent("autosize", this, w);
39741     },
39742     
39743     // private
39744     SafariOnKeyDown : function(event)
39745     {
39746         // this is a workaround for a password hang bug on chrome/ webkit.
39747         
39748         var isSelectAll = false;
39749         
39750         if(this.el.dom.selectionEnd > 0){
39751             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39752         }
39753         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39754             event.preventDefault();
39755             this.setValue('');
39756             return;
39757         }
39758         
39759         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39760             
39761             event.preventDefault();
39762             // this is very hacky as keydown always get's upper case.
39763             
39764             var cc = String.fromCharCode(event.getCharCode());
39765             
39766             
39767             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39768             
39769         }
39770         
39771         
39772     }
39773 });/*
39774  * Based on:
39775  * Ext JS Library 1.1.1
39776  * Copyright(c) 2006-2007, Ext JS, LLC.
39777  *
39778  * Originally Released Under LGPL - original licence link has changed is not relivant.
39779  *
39780  * Fork - LGPL
39781  * <script type="text/javascript">
39782  */
39783  
39784 /**
39785  * @class Roo.form.Hidden
39786  * @extends Roo.form.TextField
39787  * Simple Hidden element used on forms 
39788  * 
39789  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39790  * 
39791  * @constructor
39792  * Creates a new Hidden form element.
39793  * @param {Object} config Configuration options
39794  */
39795
39796
39797
39798 // easy hidden field...
39799 Roo.form.Hidden = function(config){
39800     Roo.form.Hidden.superclass.constructor.call(this, config);
39801 };
39802   
39803 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39804     fieldLabel:      '',
39805     inputType:      'hidden',
39806     width:          50,
39807     allowBlank:     true,
39808     labelSeparator: '',
39809     hidden:         true,
39810     itemCls :       'x-form-item-display-none'
39811
39812
39813 });
39814
39815
39816 /*
39817  * Based on:
39818  * Ext JS Library 1.1.1
39819  * Copyright(c) 2006-2007, Ext JS, LLC.
39820  *
39821  * Originally Released Under LGPL - original licence link has changed is not relivant.
39822  *
39823  * Fork - LGPL
39824  * <script type="text/javascript">
39825  */
39826  
39827 /**
39828  * @class Roo.form.TriggerField
39829  * @extends Roo.form.TextField
39830  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39831  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39832  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39833  * for which you can provide a custom implementation.  For example:
39834  * <pre><code>
39835 var trigger = new Roo.form.TriggerField();
39836 trigger.onTriggerClick = myTriggerFn;
39837 trigger.applyTo('my-field');
39838 </code></pre>
39839  *
39840  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39841  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39842  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39843  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39844  * @constructor
39845  * Create a new TriggerField.
39846  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39847  * to the base TextField)
39848  */
39849 Roo.form.TriggerField = function(config){
39850     this.mimicing = false;
39851     Roo.form.TriggerField.superclass.constructor.call(this, config);
39852 };
39853
39854 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39855     /**
39856      * @cfg {String} triggerClass A CSS class to apply to the trigger
39857      */
39858     /**
39859      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39860      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39861      */
39862     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39863     /**
39864      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39865      */
39866     hideTrigger:false,
39867
39868     /** @cfg {Boolean} grow @hide */
39869     /** @cfg {Number} growMin @hide */
39870     /** @cfg {Number} growMax @hide */
39871
39872     /**
39873      * @hide 
39874      * @method
39875      */
39876     autoSize: Roo.emptyFn,
39877     // private
39878     monitorTab : true,
39879     // private
39880     deferHeight : true,
39881
39882     
39883     actionMode : 'wrap',
39884     // private
39885     onResize : function(w, h){
39886         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39887         if(typeof w == 'number'){
39888             var x = w - this.trigger.getWidth();
39889             this.el.setWidth(this.adjustWidth('input', x));
39890             this.trigger.setStyle('left', x+'px');
39891         }
39892     },
39893
39894     // private
39895     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39896
39897     // private
39898     getResizeEl : function(){
39899         return this.wrap;
39900     },
39901
39902     // private
39903     getPositionEl : function(){
39904         return this.wrap;
39905     },
39906
39907     // private
39908     alignErrorIcon : function(){
39909         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39910     },
39911
39912     // private
39913     onRender : function(ct, position){
39914         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39915         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39916         this.trigger = this.wrap.createChild(this.triggerConfig ||
39917                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39918         if(this.hideTrigger){
39919             this.trigger.setDisplayed(false);
39920         }
39921         this.initTrigger();
39922         if(!this.width){
39923             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39924         }
39925     },
39926
39927     // private
39928     initTrigger : function(){
39929         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39930         this.trigger.addClassOnOver('x-form-trigger-over');
39931         this.trigger.addClassOnClick('x-form-trigger-click');
39932     },
39933
39934     // private
39935     onDestroy : function(){
39936         if(this.trigger){
39937             this.trigger.removeAllListeners();
39938             this.trigger.remove();
39939         }
39940         if(this.wrap){
39941             this.wrap.remove();
39942         }
39943         Roo.form.TriggerField.superclass.onDestroy.call(this);
39944     },
39945
39946     // private
39947     onFocus : function(){
39948         Roo.form.TriggerField.superclass.onFocus.call(this);
39949         if(!this.mimicing){
39950             this.wrap.addClass('x-trigger-wrap-focus');
39951             this.mimicing = true;
39952             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39953             if(this.monitorTab){
39954                 this.el.on("keydown", this.checkTab, this);
39955             }
39956         }
39957     },
39958
39959     // private
39960     checkTab : function(e){
39961         if(e.getKey() == e.TAB){
39962             this.triggerBlur();
39963         }
39964     },
39965
39966     // private
39967     onBlur : function(){
39968         // do nothing
39969     },
39970
39971     // private
39972     mimicBlur : function(e, t){
39973         if(!this.wrap.contains(t) && this.validateBlur()){
39974             this.triggerBlur();
39975         }
39976     },
39977
39978     // private
39979     triggerBlur : function(){
39980         this.mimicing = false;
39981         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39982         if(this.monitorTab){
39983             this.el.un("keydown", this.checkTab, this);
39984         }
39985         this.wrap.removeClass('x-trigger-wrap-focus');
39986         Roo.form.TriggerField.superclass.onBlur.call(this);
39987     },
39988
39989     // private
39990     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39991     validateBlur : function(e, t){
39992         return true;
39993     },
39994
39995     // private
39996     onDisable : function(){
39997         Roo.form.TriggerField.superclass.onDisable.call(this);
39998         if(this.wrap){
39999             this.wrap.addClass('x-item-disabled');
40000         }
40001     },
40002
40003     // private
40004     onEnable : function(){
40005         Roo.form.TriggerField.superclass.onEnable.call(this);
40006         if(this.wrap){
40007             this.wrap.removeClass('x-item-disabled');
40008         }
40009     },
40010
40011     // private
40012     onShow : function(){
40013         var ae = this.getActionEl();
40014         
40015         if(ae){
40016             ae.dom.style.display = '';
40017             ae.dom.style.visibility = 'visible';
40018         }
40019     },
40020
40021     // private
40022     
40023     onHide : function(){
40024         var ae = this.getActionEl();
40025         ae.dom.style.display = 'none';
40026     },
40027
40028     /**
40029      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40030      * by an implementing function.
40031      * @method
40032      * @param {EventObject} e
40033      */
40034     onTriggerClick : Roo.emptyFn
40035 });
40036
40037 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40038 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40039 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40040 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40041     initComponent : function(){
40042         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40043
40044         this.triggerConfig = {
40045             tag:'span', cls:'x-form-twin-triggers', cn:[
40046             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40047             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40048         ]};
40049     },
40050
40051     getTrigger : function(index){
40052         return this.triggers[index];
40053     },
40054
40055     initTrigger : function(){
40056         var ts = this.trigger.select('.x-form-trigger', true);
40057         this.wrap.setStyle('overflow', 'hidden');
40058         var triggerField = this;
40059         ts.each(function(t, all, index){
40060             t.hide = function(){
40061                 var w = triggerField.wrap.getWidth();
40062                 this.dom.style.display = 'none';
40063                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40064             };
40065             t.show = function(){
40066                 var w = triggerField.wrap.getWidth();
40067                 this.dom.style.display = '';
40068                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40069             };
40070             var triggerIndex = 'Trigger'+(index+1);
40071
40072             if(this['hide'+triggerIndex]){
40073                 t.dom.style.display = 'none';
40074             }
40075             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40076             t.addClassOnOver('x-form-trigger-over');
40077             t.addClassOnClick('x-form-trigger-click');
40078         }, this);
40079         this.triggers = ts.elements;
40080     },
40081
40082     onTrigger1Click : Roo.emptyFn,
40083     onTrigger2Click : Roo.emptyFn
40084 });/*
40085  * Based on:
40086  * Ext JS Library 1.1.1
40087  * Copyright(c) 2006-2007, Ext JS, LLC.
40088  *
40089  * Originally Released Under LGPL - original licence link has changed is not relivant.
40090  *
40091  * Fork - LGPL
40092  * <script type="text/javascript">
40093  */
40094  
40095 /**
40096  * @class Roo.form.TextArea
40097  * @extends Roo.form.TextField
40098  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40099  * support for auto-sizing.
40100  * @constructor
40101  * Creates a new TextArea
40102  * @param {Object} config Configuration options
40103  */
40104 Roo.form.TextArea = function(config){
40105     Roo.form.TextArea.superclass.constructor.call(this, config);
40106     // these are provided exchanges for backwards compat
40107     // minHeight/maxHeight were replaced by growMin/growMax to be
40108     // compatible with TextField growing config values
40109     if(this.minHeight !== undefined){
40110         this.growMin = this.minHeight;
40111     }
40112     if(this.maxHeight !== undefined){
40113         this.growMax = this.maxHeight;
40114     }
40115 };
40116
40117 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40118     /**
40119      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40120      */
40121     growMin : 60,
40122     /**
40123      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40124      */
40125     growMax: 1000,
40126     /**
40127      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40128      * in the field (equivalent to setting overflow: hidden, defaults to false)
40129      */
40130     preventScrollbars: false,
40131     /**
40132      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40133      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40134      */
40135
40136     // private
40137     onRender : function(ct, position){
40138         if(!this.el){
40139             this.defaultAutoCreate = {
40140                 tag: "textarea",
40141                 style:"width:300px;height:60px;",
40142                 autocomplete: "new-password"
40143             };
40144         }
40145         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40146         if(this.grow){
40147             this.textSizeEl = Roo.DomHelper.append(document.body, {
40148                 tag: "pre", cls: "x-form-grow-sizer"
40149             });
40150             if(this.preventScrollbars){
40151                 this.el.setStyle("overflow", "hidden");
40152             }
40153             this.el.setHeight(this.growMin);
40154         }
40155     },
40156
40157     onDestroy : function(){
40158         if(this.textSizeEl){
40159             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40160         }
40161         Roo.form.TextArea.superclass.onDestroy.call(this);
40162     },
40163
40164     // private
40165     onKeyUp : function(e){
40166         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40167             this.autoSize();
40168         }
40169     },
40170
40171     /**
40172      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40173      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40174      */
40175     autoSize : function(){
40176         if(!this.grow || !this.textSizeEl){
40177             return;
40178         }
40179         var el = this.el;
40180         var v = el.dom.value;
40181         var ts = this.textSizeEl;
40182
40183         ts.innerHTML = '';
40184         ts.appendChild(document.createTextNode(v));
40185         v = ts.innerHTML;
40186
40187         Roo.fly(ts).setWidth(this.el.getWidth());
40188         if(v.length < 1){
40189             v = "&#160;&#160;";
40190         }else{
40191             if(Roo.isIE){
40192                 v = v.replace(/\n/g, '<p>&#160;</p>');
40193             }
40194             v += "&#160;\n&#160;";
40195         }
40196         ts.innerHTML = v;
40197         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40198         if(h != this.lastHeight){
40199             this.lastHeight = h;
40200             this.el.setHeight(h);
40201             this.fireEvent("autosize", this, h);
40202         }
40203     }
40204 });/*
40205  * Based on:
40206  * Ext JS Library 1.1.1
40207  * Copyright(c) 2006-2007, Ext JS, LLC.
40208  *
40209  * Originally Released Under LGPL - original licence link has changed is not relivant.
40210  *
40211  * Fork - LGPL
40212  * <script type="text/javascript">
40213  */
40214  
40215
40216 /**
40217  * @class Roo.form.NumberField
40218  * @extends Roo.form.TextField
40219  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40220  * @constructor
40221  * Creates a new NumberField
40222  * @param {Object} config Configuration options
40223  */
40224 Roo.form.NumberField = function(config){
40225     Roo.form.NumberField.superclass.constructor.call(this, config);
40226 };
40227
40228 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40229     /**
40230      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40231      */
40232     fieldClass: "x-form-field x-form-num-field",
40233     /**
40234      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40235      */
40236     allowDecimals : true,
40237     /**
40238      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40239      */
40240     decimalSeparator : ".",
40241     /**
40242      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40243      */
40244     decimalPrecision : 2,
40245     /**
40246      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40247      */
40248     allowNegative : true,
40249     /**
40250      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40251      */
40252     minValue : Number.NEGATIVE_INFINITY,
40253     /**
40254      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40255      */
40256     maxValue : Number.MAX_VALUE,
40257     /**
40258      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40259      */
40260     minText : "The minimum value for this field is {0}",
40261     /**
40262      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40263      */
40264     maxText : "The maximum value for this field is {0}",
40265     /**
40266      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40267      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40268      */
40269     nanText : "{0} is not a valid number",
40270
40271     // private
40272     initEvents : function(){
40273         Roo.form.NumberField.superclass.initEvents.call(this);
40274         var allowed = "0123456789";
40275         if(this.allowDecimals){
40276             allowed += this.decimalSeparator;
40277         }
40278         if(this.allowNegative){
40279             allowed += "-";
40280         }
40281         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40282         var keyPress = function(e){
40283             var k = e.getKey();
40284             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40285                 return;
40286             }
40287             var c = e.getCharCode();
40288             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40289                 e.stopEvent();
40290             }
40291         };
40292         this.el.on("keypress", keyPress, this);
40293     },
40294
40295     // private
40296     validateValue : function(value){
40297         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40298             return false;
40299         }
40300         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40301              return true;
40302         }
40303         var num = this.parseValue(value);
40304         if(isNaN(num)){
40305             this.markInvalid(String.format(this.nanText, value));
40306             return false;
40307         }
40308         if(num < this.minValue){
40309             this.markInvalid(String.format(this.minText, this.minValue));
40310             return false;
40311         }
40312         if(num > this.maxValue){
40313             this.markInvalid(String.format(this.maxText, this.maxValue));
40314             return false;
40315         }
40316         return true;
40317     },
40318
40319     getValue : function(){
40320         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40321     },
40322
40323     // private
40324     parseValue : function(value){
40325         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40326         return isNaN(value) ? '' : value;
40327     },
40328
40329     // private
40330     fixPrecision : function(value){
40331         var nan = isNaN(value);
40332         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40333             return nan ? '' : value;
40334         }
40335         return parseFloat(value).toFixed(this.decimalPrecision);
40336     },
40337
40338     setValue : function(v){
40339         v = this.fixPrecision(v);
40340         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40341     },
40342
40343     // private
40344     decimalPrecisionFcn : function(v){
40345         return Math.floor(v);
40346     },
40347
40348     beforeBlur : function(){
40349         var v = this.parseValue(this.getRawValue());
40350         if(v){
40351             this.setValue(v);
40352         }
40353     }
40354 });/*
40355  * Based on:
40356  * Ext JS Library 1.1.1
40357  * Copyright(c) 2006-2007, Ext JS, LLC.
40358  *
40359  * Originally Released Under LGPL - original licence link has changed is not relivant.
40360  *
40361  * Fork - LGPL
40362  * <script type="text/javascript">
40363  */
40364  
40365 /**
40366  * @class Roo.form.DateField
40367  * @extends Roo.form.TriggerField
40368  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40369 * @constructor
40370 * Create a new DateField
40371 * @param {Object} config
40372  */
40373 Roo.form.DateField = function(config)
40374 {
40375     Roo.form.DateField.superclass.constructor.call(this, config);
40376     
40377       this.addEvents({
40378          
40379         /**
40380          * @event select
40381          * Fires when a date is selected
40382              * @param {Roo.form.DateField} combo This combo box
40383              * @param {Date} date The date selected
40384              */
40385         'select' : true
40386          
40387     });
40388     
40389     
40390     if(typeof this.minValue == "string") {
40391         this.minValue = this.parseDate(this.minValue);
40392     }
40393     if(typeof this.maxValue == "string") {
40394         this.maxValue = this.parseDate(this.maxValue);
40395     }
40396     this.ddMatch = null;
40397     if(this.disabledDates){
40398         var dd = this.disabledDates;
40399         var re = "(?:";
40400         for(var i = 0; i < dd.length; i++){
40401             re += dd[i];
40402             if(i != dd.length-1) {
40403                 re += "|";
40404             }
40405         }
40406         this.ddMatch = new RegExp(re + ")");
40407     }
40408 };
40409
40410 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40411     /**
40412      * @cfg {String} format
40413      * The default date format string which can be overriden for localization support.  The format must be
40414      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40415      */
40416     format : "m/d/y",
40417     /**
40418      * @cfg {String} altFormats
40419      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40420      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40421      */
40422     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40423     /**
40424      * @cfg {Array} disabledDays
40425      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40426      */
40427     disabledDays : null,
40428     /**
40429      * @cfg {String} disabledDaysText
40430      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40431      */
40432     disabledDaysText : "Disabled",
40433     /**
40434      * @cfg {Array} disabledDates
40435      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40436      * expression so they are very powerful. Some examples:
40437      * <ul>
40438      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40439      * <li>["03/08", "09/16"] would disable those days for every year</li>
40440      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40441      * <li>["03/../2006"] would disable every day in March 2006</li>
40442      * <li>["^03"] would disable every day in every March</li>
40443      * </ul>
40444      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40445      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40446      */
40447     disabledDates : null,
40448     /**
40449      * @cfg {String} disabledDatesText
40450      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40451      */
40452     disabledDatesText : "Disabled",
40453     /**
40454      * @cfg {Date/String} minValue
40455      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40456      * valid format (defaults to null).
40457      */
40458     minValue : null,
40459     /**
40460      * @cfg {Date/String} maxValue
40461      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40462      * valid format (defaults to null).
40463      */
40464     maxValue : null,
40465     /**
40466      * @cfg {String} minText
40467      * The error text to display when the date in the cell is before minValue (defaults to
40468      * 'The date in this field must be after {minValue}').
40469      */
40470     minText : "The date in this field must be equal to or after {0}",
40471     /**
40472      * @cfg {String} maxText
40473      * The error text to display when the date in the cell is after maxValue (defaults to
40474      * 'The date in this field must be before {maxValue}').
40475      */
40476     maxText : "The date in this field must be equal to or before {0}",
40477     /**
40478      * @cfg {String} invalidText
40479      * The error text to display when the date in the field is invalid (defaults to
40480      * '{value} is not a valid date - it must be in the format {format}').
40481      */
40482     invalidText : "{0} is not a valid date - it must be in the format {1}",
40483     /**
40484      * @cfg {String} triggerClass
40485      * An additional CSS class used to style the trigger button.  The trigger will always get the
40486      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40487      * which displays a calendar icon).
40488      */
40489     triggerClass : 'x-form-date-trigger',
40490     
40491
40492     /**
40493      * @cfg {Boolean} useIso
40494      * if enabled, then the date field will use a hidden field to store the 
40495      * real value as iso formated date. default (false)
40496      */ 
40497     useIso : false,
40498     /**
40499      * @cfg {String/Object} autoCreate
40500      * A DomHelper element spec, or true for a default element spec (defaults to
40501      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40502      */ 
40503     // private
40504     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40505     
40506     // private
40507     hiddenField: false,
40508     
40509     onRender : function(ct, position)
40510     {
40511         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40512         if (this.useIso) {
40513             //this.el.dom.removeAttribute('name'); 
40514             Roo.log("Changing name?");
40515             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40516             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40517                     'before', true);
40518             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40519             // prevent input submission
40520             this.hiddenName = this.name;
40521         }
40522             
40523             
40524     },
40525     
40526     // private
40527     validateValue : function(value)
40528     {
40529         value = this.formatDate(value);
40530         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40531             Roo.log('super failed');
40532             return false;
40533         }
40534         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40535              return true;
40536         }
40537         var svalue = value;
40538         value = this.parseDate(value);
40539         if(!value){
40540             Roo.log('parse date failed' + svalue);
40541             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40542             return false;
40543         }
40544         var time = value.getTime();
40545         if(this.minValue && time < this.minValue.getTime()){
40546             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40547             return false;
40548         }
40549         if(this.maxValue && time > this.maxValue.getTime()){
40550             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40551             return false;
40552         }
40553         if(this.disabledDays){
40554             var day = value.getDay();
40555             for(var i = 0; i < this.disabledDays.length; i++) {
40556                 if(day === this.disabledDays[i]){
40557                     this.markInvalid(this.disabledDaysText);
40558                     return false;
40559                 }
40560             }
40561         }
40562         var fvalue = this.formatDate(value);
40563         if(this.ddMatch && this.ddMatch.test(fvalue)){
40564             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40565             return false;
40566         }
40567         return true;
40568     },
40569
40570     // private
40571     // Provides logic to override the default TriggerField.validateBlur which just returns true
40572     validateBlur : function(){
40573         return !this.menu || !this.menu.isVisible();
40574     },
40575     
40576     getName: function()
40577     {
40578         // returns hidden if it's set..
40579         if (!this.rendered) {return ''};
40580         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40581         
40582     },
40583
40584     /**
40585      * Returns the current date value of the date field.
40586      * @return {Date} The date value
40587      */
40588     getValue : function(){
40589         
40590         return  this.hiddenField ?
40591                 this.hiddenField.value :
40592                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40593     },
40594
40595     /**
40596      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40597      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40598      * (the default format used is "m/d/y").
40599      * <br />Usage:
40600      * <pre><code>
40601 //All of these calls set the same date value (May 4, 2006)
40602
40603 //Pass a date object:
40604 var dt = new Date('5/4/06');
40605 dateField.setValue(dt);
40606
40607 //Pass a date string (default format):
40608 dateField.setValue('5/4/06');
40609
40610 //Pass a date string (custom format):
40611 dateField.format = 'Y-m-d';
40612 dateField.setValue('2006-5-4');
40613 </code></pre>
40614      * @param {String/Date} date The date or valid date string
40615      */
40616     setValue : function(date){
40617         if (this.hiddenField) {
40618             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40619         }
40620         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40621         // make sure the value field is always stored as a date..
40622         this.value = this.parseDate(date);
40623         
40624         
40625     },
40626
40627     // private
40628     parseDate : function(value){
40629         if(!value || value instanceof Date){
40630             return value;
40631         }
40632         var v = Date.parseDate(value, this.format);
40633          if (!v && this.useIso) {
40634             v = Date.parseDate(value, 'Y-m-d');
40635         }
40636         if(!v && this.altFormats){
40637             if(!this.altFormatsArray){
40638                 this.altFormatsArray = this.altFormats.split("|");
40639             }
40640             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40641                 v = Date.parseDate(value, this.altFormatsArray[i]);
40642             }
40643         }
40644         return v;
40645     },
40646
40647     // private
40648     formatDate : function(date, fmt){
40649         return (!date || !(date instanceof Date)) ?
40650                date : date.dateFormat(fmt || this.format);
40651     },
40652
40653     // private
40654     menuListeners : {
40655         select: function(m, d){
40656             
40657             this.setValue(d);
40658             this.fireEvent('select', this, d);
40659         },
40660         show : function(){ // retain focus styling
40661             this.onFocus();
40662         },
40663         hide : function(){
40664             this.focus.defer(10, this);
40665             var ml = this.menuListeners;
40666             this.menu.un("select", ml.select,  this);
40667             this.menu.un("show", ml.show,  this);
40668             this.menu.un("hide", ml.hide,  this);
40669         }
40670     },
40671
40672     // private
40673     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40674     onTriggerClick : function(){
40675         if(this.disabled){
40676             return;
40677         }
40678         if(this.menu == null){
40679             this.menu = new Roo.menu.DateMenu();
40680         }
40681         Roo.apply(this.menu.picker,  {
40682             showClear: this.allowBlank,
40683             minDate : this.minValue,
40684             maxDate : this.maxValue,
40685             disabledDatesRE : this.ddMatch,
40686             disabledDatesText : this.disabledDatesText,
40687             disabledDays : this.disabledDays,
40688             disabledDaysText : this.disabledDaysText,
40689             format : this.useIso ? 'Y-m-d' : this.format,
40690             minText : String.format(this.minText, this.formatDate(this.minValue)),
40691             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40692         });
40693         this.menu.on(Roo.apply({}, this.menuListeners, {
40694             scope:this
40695         }));
40696         this.menu.picker.setValue(this.getValue() || new Date());
40697         this.menu.show(this.el, "tl-bl?");
40698     },
40699
40700     beforeBlur : function(){
40701         var v = this.parseDate(this.getRawValue());
40702         if(v){
40703             this.setValue(v);
40704         }
40705     },
40706
40707     /*@
40708      * overide
40709      * 
40710      */
40711     isDirty : function() {
40712         if(this.disabled) {
40713             return false;
40714         }
40715         
40716         if(typeof(this.startValue) === 'undefined'){
40717             return false;
40718         }
40719         
40720         return String(this.getValue()) !== String(this.startValue);
40721         
40722     },
40723     // @overide
40724     cleanLeadingSpace : function(e)
40725     {
40726        return;
40727     }
40728     
40729 });/*
40730  * Based on:
40731  * Ext JS Library 1.1.1
40732  * Copyright(c) 2006-2007, Ext JS, LLC.
40733  *
40734  * Originally Released Under LGPL - original licence link has changed is not relivant.
40735  *
40736  * Fork - LGPL
40737  * <script type="text/javascript">
40738  */
40739  
40740 /**
40741  * @class Roo.form.MonthField
40742  * @extends Roo.form.TriggerField
40743  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40744 * @constructor
40745 * Create a new MonthField
40746 * @param {Object} config
40747  */
40748 Roo.form.MonthField = function(config){
40749     
40750     Roo.form.MonthField.superclass.constructor.call(this, config);
40751     
40752       this.addEvents({
40753          
40754         /**
40755          * @event select
40756          * Fires when a date is selected
40757              * @param {Roo.form.MonthFieeld} combo This combo box
40758              * @param {Date} date The date selected
40759              */
40760         'select' : true
40761          
40762     });
40763     
40764     
40765     if(typeof this.minValue == "string") {
40766         this.minValue = this.parseDate(this.minValue);
40767     }
40768     if(typeof this.maxValue == "string") {
40769         this.maxValue = this.parseDate(this.maxValue);
40770     }
40771     this.ddMatch = null;
40772     if(this.disabledDates){
40773         var dd = this.disabledDates;
40774         var re = "(?:";
40775         for(var i = 0; i < dd.length; i++){
40776             re += dd[i];
40777             if(i != dd.length-1) {
40778                 re += "|";
40779             }
40780         }
40781         this.ddMatch = new RegExp(re + ")");
40782     }
40783 };
40784
40785 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40786     /**
40787      * @cfg {String} format
40788      * The default date format string which can be overriden for localization support.  The format must be
40789      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40790      */
40791     format : "M Y",
40792     /**
40793      * @cfg {String} altFormats
40794      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40795      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40796      */
40797     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40798     /**
40799      * @cfg {Array} disabledDays
40800      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40801      */
40802     disabledDays : [0,1,2,3,4,5,6],
40803     /**
40804      * @cfg {String} disabledDaysText
40805      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40806      */
40807     disabledDaysText : "Disabled",
40808     /**
40809      * @cfg {Array} disabledDates
40810      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40811      * expression so they are very powerful. Some examples:
40812      * <ul>
40813      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40814      * <li>["03/08", "09/16"] would disable those days for every year</li>
40815      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40816      * <li>["03/../2006"] would disable every day in March 2006</li>
40817      * <li>["^03"] would disable every day in every March</li>
40818      * </ul>
40819      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40820      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40821      */
40822     disabledDates : null,
40823     /**
40824      * @cfg {String} disabledDatesText
40825      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40826      */
40827     disabledDatesText : "Disabled",
40828     /**
40829      * @cfg {Date/String} minValue
40830      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40831      * valid format (defaults to null).
40832      */
40833     minValue : null,
40834     /**
40835      * @cfg {Date/String} maxValue
40836      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40837      * valid format (defaults to null).
40838      */
40839     maxValue : null,
40840     /**
40841      * @cfg {String} minText
40842      * The error text to display when the date in the cell is before minValue (defaults to
40843      * 'The date in this field must be after {minValue}').
40844      */
40845     minText : "The date in this field must be equal to or after {0}",
40846     /**
40847      * @cfg {String} maxTextf
40848      * The error text to display when the date in the cell is after maxValue (defaults to
40849      * 'The date in this field must be before {maxValue}').
40850      */
40851     maxText : "The date in this field must be equal to or before {0}",
40852     /**
40853      * @cfg {String} invalidText
40854      * The error text to display when the date in the field is invalid (defaults to
40855      * '{value} is not a valid date - it must be in the format {format}').
40856      */
40857     invalidText : "{0} is not a valid date - it must be in the format {1}",
40858     /**
40859      * @cfg {String} triggerClass
40860      * An additional CSS class used to style the trigger button.  The trigger will always get the
40861      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40862      * which displays a calendar icon).
40863      */
40864     triggerClass : 'x-form-date-trigger',
40865     
40866
40867     /**
40868      * @cfg {Boolean} useIso
40869      * if enabled, then the date field will use a hidden field to store the 
40870      * real value as iso formated date. default (true)
40871      */ 
40872     useIso : true,
40873     /**
40874      * @cfg {String/Object} autoCreate
40875      * A DomHelper element spec, or true for a default element spec (defaults to
40876      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40877      */ 
40878     // private
40879     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40880     
40881     // private
40882     hiddenField: false,
40883     
40884     hideMonthPicker : false,
40885     
40886     onRender : function(ct, position)
40887     {
40888         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40889         if (this.useIso) {
40890             this.el.dom.removeAttribute('name'); 
40891             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40892                     'before', true);
40893             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40894             // prevent input submission
40895             this.hiddenName = this.name;
40896         }
40897             
40898             
40899     },
40900     
40901     // private
40902     validateValue : function(value)
40903     {
40904         value = this.formatDate(value);
40905         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40906             return false;
40907         }
40908         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40909              return true;
40910         }
40911         var svalue = value;
40912         value = this.parseDate(value);
40913         if(!value){
40914             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40915             return false;
40916         }
40917         var time = value.getTime();
40918         if(this.minValue && time < this.minValue.getTime()){
40919             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40920             return false;
40921         }
40922         if(this.maxValue && time > this.maxValue.getTime()){
40923             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40924             return false;
40925         }
40926         /*if(this.disabledDays){
40927             var day = value.getDay();
40928             for(var i = 0; i < this.disabledDays.length; i++) {
40929                 if(day === this.disabledDays[i]){
40930                     this.markInvalid(this.disabledDaysText);
40931                     return false;
40932                 }
40933             }
40934         }
40935         */
40936         var fvalue = this.formatDate(value);
40937         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40938             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40939             return false;
40940         }
40941         */
40942         return true;
40943     },
40944
40945     // private
40946     // Provides logic to override the default TriggerField.validateBlur which just returns true
40947     validateBlur : function(){
40948         return !this.menu || !this.menu.isVisible();
40949     },
40950
40951     /**
40952      * Returns the current date value of the date field.
40953      * @return {Date} The date value
40954      */
40955     getValue : function(){
40956         
40957         
40958         
40959         return  this.hiddenField ?
40960                 this.hiddenField.value :
40961                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40962     },
40963
40964     /**
40965      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40966      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40967      * (the default format used is "m/d/y").
40968      * <br />Usage:
40969      * <pre><code>
40970 //All of these calls set the same date value (May 4, 2006)
40971
40972 //Pass a date object:
40973 var dt = new Date('5/4/06');
40974 monthField.setValue(dt);
40975
40976 //Pass a date string (default format):
40977 monthField.setValue('5/4/06');
40978
40979 //Pass a date string (custom format):
40980 monthField.format = 'Y-m-d';
40981 monthField.setValue('2006-5-4');
40982 </code></pre>
40983      * @param {String/Date} date The date or valid date string
40984      */
40985     setValue : function(date){
40986         Roo.log('month setValue' + date);
40987         // can only be first of month..
40988         
40989         var val = this.parseDate(date);
40990         
40991         if (this.hiddenField) {
40992             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40993         }
40994         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40995         this.value = this.parseDate(date);
40996     },
40997
40998     // private
40999     parseDate : function(value){
41000         if(!value || value instanceof Date){
41001             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41002             return value;
41003         }
41004         var v = Date.parseDate(value, this.format);
41005         if (!v && this.useIso) {
41006             v = Date.parseDate(value, 'Y-m-d');
41007         }
41008         if (v) {
41009             // 
41010             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41011         }
41012         
41013         
41014         if(!v && this.altFormats){
41015             if(!this.altFormatsArray){
41016                 this.altFormatsArray = this.altFormats.split("|");
41017             }
41018             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41019                 v = Date.parseDate(value, this.altFormatsArray[i]);
41020             }
41021         }
41022         return v;
41023     },
41024
41025     // private
41026     formatDate : function(date, fmt){
41027         return (!date || !(date instanceof Date)) ?
41028                date : date.dateFormat(fmt || this.format);
41029     },
41030
41031     // private
41032     menuListeners : {
41033         select: function(m, d){
41034             this.setValue(d);
41035             this.fireEvent('select', this, d);
41036         },
41037         show : function(){ // retain focus styling
41038             this.onFocus();
41039         },
41040         hide : function(){
41041             this.focus.defer(10, this);
41042             var ml = this.menuListeners;
41043             this.menu.un("select", ml.select,  this);
41044             this.menu.un("show", ml.show,  this);
41045             this.menu.un("hide", ml.hide,  this);
41046         }
41047     },
41048     // private
41049     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41050     onTriggerClick : function(){
41051         if(this.disabled){
41052             return;
41053         }
41054         if(this.menu == null){
41055             this.menu = new Roo.menu.DateMenu();
41056            
41057         }
41058         
41059         Roo.apply(this.menu.picker,  {
41060             
41061             showClear: this.allowBlank,
41062             minDate : this.minValue,
41063             maxDate : this.maxValue,
41064             disabledDatesRE : this.ddMatch,
41065             disabledDatesText : this.disabledDatesText,
41066             
41067             format : this.useIso ? 'Y-m-d' : this.format,
41068             minText : String.format(this.minText, this.formatDate(this.minValue)),
41069             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41070             
41071         });
41072          this.menu.on(Roo.apply({}, this.menuListeners, {
41073             scope:this
41074         }));
41075        
41076         
41077         var m = this.menu;
41078         var p = m.picker;
41079         
41080         // hide month picker get's called when we called by 'before hide';
41081         
41082         var ignorehide = true;
41083         p.hideMonthPicker  = function(disableAnim){
41084             if (ignorehide) {
41085                 return;
41086             }
41087              if(this.monthPicker){
41088                 Roo.log("hideMonthPicker called");
41089                 if(disableAnim === true){
41090                     this.monthPicker.hide();
41091                 }else{
41092                     this.monthPicker.slideOut('t', {duration:.2});
41093                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41094                     p.fireEvent("select", this, this.value);
41095                     m.hide();
41096                 }
41097             }
41098         }
41099         
41100         Roo.log('picker set value');
41101         Roo.log(this.getValue());
41102         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41103         m.show(this.el, 'tl-bl?');
41104         ignorehide  = false;
41105         // this will trigger hideMonthPicker..
41106         
41107         
41108         // hidden the day picker
41109         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41110         
41111         
41112         
41113       
41114         
41115         p.showMonthPicker.defer(100, p);
41116     
41117         
41118        
41119     },
41120
41121     beforeBlur : function(){
41122         var v = this.parseDate(this.getRawValue());
41123         if(v){
41124             this.setValue(v);
41125         }
41126     }
41127
41128     /** @cfg {Boolean} grow @hide */
41129     /** @cfg {Number} growMin @hide */
41130     /** @cfg {Number} growMax @hide */
41131     /**
41132      * @hide
41133      * @method autoSize
41134      */
41135 });/*
41136  * Based on:
41137  * Ext JS Library 1.1.1
41138  * Copyright(c) 2006-2007, Ext JS, LLC.
41139  *
41140  * Originally Released Under LGPL - original licence link has changed is not relivant.
41141  *
41142  * Fork - LGPL
41143  * <script type="text/javascript">
41144  */
41145  
41146
41147 /**
41148  * @class Roo.form.ComboBox
41149  * @extends Roo.form.TriggerField
41150  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41151  * @constructor
41152  * Create a new ComboBox.
41153  * @param {Object} config Configuration options
41154  */
41155 Roo.form.ComboBox = function(config){
41156     Roo.form.ComboBox.superclass.constructor.call(this, config);
41157     this.addEvents({
41158         /**
41159          * @event expand
41160          * Fires when the dropdown list is expanded
41161              * @param {Roo.form.ComboBox} combo This combo box
41162              */
41163         'expand' : true,
41164         /**
41165          * @event collapse
41166          * Fires when the dropdown list is collapsed
41167              * @param {Roo.form.ComboBox} combo This combo box
41168              */
41169         'collapse' : true,
41170         /**
41171          * @event beforeselect
41172          * Fires before a list item is selected. Return false to cancel the selection.
41173              * @param {Roo.form.ComboBox} combo This combo box
41174              * @param {Roo.data.Record} record The data record returned from the underlying store
41175              * @param {Number} index The index of the selected item in the dropdown list
41176              */
41177         'beforeselect' : true,
41178         /**
41179          * @event select
41180          * Fires when a list item is selected
41181              * @param {Roo.form.ComboBox} combo This combo box
41182              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41183              * @param {Number} index The index of the selected item in the dropdown list
41184              */
41185         'select' : true,
41186         /**
41187          * @event beforequery
41188          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41189          * The event object passed has these properties:
41190              * @param {Roo.form.ComboBox} combo This combo box
41191              * @param {String} query The query
41192              * @param {Boolean} forceAll true to force "all" query
41193              * @param {Boolean} cancel true to cancel the query
41194              * @param {Object} e The query event object
41195              */
41196         'beforequery': true,
41197          /**
41198          * @event add
41199          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41200              * @param {Roo.form.ComboBox} combo This combo box
41201              */
41202         'add' : true,
41203         /**
41204          * @event edit
41205          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41206              * @param {Roo.form.ComboBox} combo This combo box
41207              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41208              */
41209         'edit' : true
41210         
41211         
41212     });
41213     if(this.transform){
41214         this.allowDomMove = false;
41215         var s = Roo.getDom(this.transform);
41216         if(!this.hiddenName){
41217             this.hiddenName = s.name;
41218         }
41219         if(!this.store){
41220             this.mode = 'local';
41221             var d = [], opts = s.options;
41222             for(var i = 0, len = opts.length;i < len; i++){
41223                 var o = opts[i];
41224                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41225                 if(o.selected) {
41226                     this.value = value;
41227                 }
41228                 d.push([value, o.text]);
41229             }
41230             this.store = new Roo.data.SimpleStore({
41231                 'id': 0,
41232                 fields: ['value', 'text'],
41233                 data : d
41234             });
41235             this.valueField = 'value';
41236             this.displayField = 'text';
41237         }
41238         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41239         if(!this.lazyRender){
41240             this.target = true;
41241             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41242             s.parentNode.removeChild(s); // remove it
41243             this.render(this.el.parentNode);
41244         }else{
41245             s.parentNode.removeChild(s); // remove it
41246         }
41247
41248     }
41249     if (this.store) {
41250         this.store = Roo.factory(this.store, Roo.data);
41251     }
41252     
41253     this.selectedIndex = -1;
41254     if(this.mode == 'local'){
41255         if(config.queryDelay === undefined){
41256             this.queryDelay = 10;
41257         }
41258         if(config.minChars === undefined){
41259             this.minChars = 0;
41260         }
41261     }
41262 };
41263
41264 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41265     /**
41266      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41267      */
41268     /**
41269      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41270      * rendering into an Roo.Editor, defaults to false)
41271      */
41272     /**
41273      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41274      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41275      */
41276     /**
41277      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41278      */
41279     /**
41280      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41281      * the dropdown list (defaults to undefined, with no header element)
41282      */
41283
41284      /**
41285      * @cfg {String/Roo.Template} tpl The template to use to render the output
41286      */
41287      
41288     // private
41289     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41290     /**
41291      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41292      */
41293     listWidth: undefined,
41294     /**
41295      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41296      * mode = 'remote' or 'text' if mode = 'local')
41297      */
41298     displayField: undefined,
41299     /**
41300      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41301      * mode = 'remote' or 'value' if mode = 'local'). 
41302      * Note: use of a valueField requires the user make a selection
41303      * in order for a value to be mapped.
41304      */
41305     valueField: undefined,
41306     
41307     
41308     /**
41309      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41310      * field's data value (defaults to the underlying DOM element's name)
41311      */
41312     hiddenName: undefined,
41313     /**
41314      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41315      */
41316     listClass: '',
41317     /**
41318      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41319      */
41320     selectedClass: 'x-combo-selected',
41321     /**
41322      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41323      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41324      * which displays a downward arrow icon).
41325      */
41326     triggerClass : 'x-form-arrow-trigger',
41327     /**
41328      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41329      */
41330     shadow:'sides',
41331     /**
41332      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41333      * anchor positions (defaults to 'tl-bl')
41334      */
41335     listAlign: 'tl-bl?',
41336     /**
41337      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41338      */
41339     maxHeight: 300,
41340     /**
41341      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41342      * query specified by the allQuery config option (defaults to 'query')
41343      */
41344     triggerAction: 'query',
41345     /**
41346      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41347      * (defaults to 4, does not apply if editable = false)
41348      */
41349     minChars : 4,
41350     /**
41351      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41352      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41353      */
41354     typeAhead: false,
41355     /**
41356      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41357      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41358      */
41359     queryDelay: 500,
41360     /**
41361      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41362      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41363      */
41364     pageSize: 0,
41365     /**
41366      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41367      * when editable = true (defaults to false)
41368      */
41369     selectOnFocus:false,
41370     /**
41371      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41372      */
41373     queryParam: 'query',
41374     /**
41375      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41376      * when mode = 'remote' (defaults to 'Loading...')
41377      */
41378     loadingText: 'Loading...',
41379     /**
41380      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41381      */
41382     resizable: false,
41383     /**
41384      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41385      */
41386     handleHeight : 8,
41387     /**
41388      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41389      * traditional select (defaults to true)
41390      */
41391     editable: true,
41392     /**
41393      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41394      */
41395     allQuery: '',
41396     /**
41397      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41398      */
41399     mode: 'remote',
41400     /**
41401      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41402      * listWidth has a higher value)
41403      */
41404     minListWidth : 70,
41405     /**
41406      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41407      * allow the user to set arbitrary text into the field (defaults to false)
41408      */
41409     forceSelection:false,
41410     /**
41411      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41412      * if typeAhead = true (defaults to 250)
41413      */
41414     typeAheadDelay : 250,
41415     /**
41416      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41417      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41418      */
41419     valueNotFoundText : undefined,
41420     /**
41421      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41422      */
41423     blockFocus : false,
41424     
41425     /**
41426      * @cfg {Boolean} disableClear Disable showing of clear button.
41427      */
41428     disableClear : false,
41429     /**
41430      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41431      */
41432     alwaysQuery : false,
41433     
41434     //private
41435     addicon : false,
41436     editicon: false,
41437     
41438     // element that contains real text value.. (when hidden is used..)
41439      
41440     // private
41441     onRender : function(ct, position)
41442     {
41443         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41444         
41445         if(this.hiddenName){
41446             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41447                     'before', true);
41448             this.hiddenField.value =
41449                 this.hiddenValue !== undefined ? this.hiddenValue :
41450                 this.value !== undefined ? this.value : '';
41451
41452             // prevent input submission
41453             this.el.dom.removeAttribute('name');
41454              
41455              
41456         }
41457         
41458         if(Roo.isGecko){
41459             this.el.dom.setAttribute('autocomplete', 'off');
41460         }
41461
41462         var cls = 'x-combo-list';
41463
41464         this.list = new Roo.Layer({
41465             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41466         });
41467
41468         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41469         this.list.setWidth(lw);
41470         this.list.swallowEvent('mousewheel');
41471         this.assetHeight = 0;
41472
41473         if(this.title){
41474             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41475             this.assetHeight += this.header.getHeight();
41476         }
41477
41478         this.innerList = this.list.createChild({cls:cls+'-inner'});
41479         this.innerList.on('mouseover', this.onViewOver, this);
41480         this.innerList.on('mousemove', this.onViewMove, this);
41481         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41482         
41483         if(this.allowBlank && !this.pageSize && !this.disableClear){
41484             this.footer = this.list.createChild({cls:cls+'-ft'});
41485             this.pageTb = new Roo.Toolbar(this.footer);
41486            
41487         }
41488         if(this.pageSize){
41489             this.footer = this.list.createChild({cls:cls+'-ft'});
41490             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41491                     {pageSize: this.pageSize});
41492             
41493         }
41494         
41495         if (this.pageTb && this.allowBlank && !this.disableClear) {
41496             var _this = this;
41497             this.pageTb.add(new Roo.Toolbar.Fill(), {
41498                 cls: 'x-btn-icon x-btn-clear',
41499                 text: '&#160;',
41500                 handler: function()
41501                 {
41502                     _this.collapse();
41503                     _this.clearValue();
41504                     _this.onSelect(false, -1);
41505                 }
41506             });
41507         }
41508         if (this.footer) {
41509             this.assetHeight += this.footer.getHeight();
41510         }
41511         
41512
41513         if(!this.tpl){
41514             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41515         }
41516
41517         this.view = new Roo.View(this.innerList, this.tpl, {
41518             singleSelect:true,
41519             store: this.store,
41520             selectedClass: this.selectedClass
41521         });
41522
41523         this.view.on('click', this.onViewClick, this);
41524
41525         this.store.on('beforeload', this.onBeforeLoad, this);
41526         this.store.on('load', this.onLoad, this);
41527         this.store.on('loadexception', this.onLoadException, this);
41528
41529         if(this.resizable){
41530             this.resizer = new Roo.Resizable(this.list,  {
41531                pinned:true, handles:'se'
41532             });
41533             this.resizer.on('resize', function(r, w, h){
41534                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41535                 this.listWidth = w;
41536                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41537                 this.restrictHeight();
41538             }, this);
41539             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41540         }
41541         if(!this.editable){
41542             this.editable = true;
41543             this.setEditable(false);
41544         }  
41545         
41546         
41547         if (typeof(this.events.add.listeners) != 'undefined') {
41548             
41549             this.addicon = this.wrap.createChild(
41550                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41551        
41552             this.addicon.on('click', function(e) {
41553                 this.fireEvent('add', this);
41554             }, this);
41555         }
41556         if (typeof(this.events.edit.listeners) != 'undefined') {
41557             
41558             this.editicon = this.wrap.createChild(
41559                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41560             if (this.addicon) {
41561                 this.editicon.setStyle('margin-left', '40px');
41562             }
41563             this.editicon.on('click', function(e) {
41564                 
41565                 // we fire even  if inothing is selected..
41566                 this.fireEvent('edit', this, this.lastData );
41567                 
41568             }, this);
41569         }
41570         
41571         
41572         
41573     },
41574
41575     // private
41576     initEvents : function(){
41577         Roo.form.ComboBox.superclass.initEvents.call(this);
41578
41579         this.keyNav = new Roo.KeyNav(this.el, {
41580             "up" : function(e){
41581                 this.inKeyMode = true;
41582                 this.selectPrev();
41583             },
41584
41585             "down" : function(e){
41586                 if(!this.isExpanded()){
41587                     this.onTriggerClick();
41588                 }else{
41589                     this.inKeyMode = true;
41590                     this.selectNext();
41591                 }
41592             },
41593
41594             "enter" : function(e){
41595                 this.onViewClick();
41596                 //return true;
41597             },
41598
41599             "esc" : function(e){
41600                 this.collapse();
41601             },
41602
41603             "tab" : function(e){
41604                 this.onViewClick(false);
41605                 this.fireEvent("specialkey", this, e);
41606                 return true;
41607             },
41608
41609             scope : this,
41610
41611             doRelay : function(foo, bar, hname){
41612                 if(hname == 'down' || this.scope.isExpanded()){
41613                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41614                 }
41615                 return true;
41616             },
41617
41618             forceKeyDown: true
41619         });
41620         this.queryDelay = Math.max(this.queryDelay || 10,
41621                 this.mode == 'local' ? 10 : 250);
41622         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41623         if(this.typeAhead){
41624             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41625         }
41626         if(this.editable !== false){
41627             this.el.on("keyup", this.onKeyUp, this);
41628         }
41629         if(this.forceSelection){
41630             this.on('blur', this.doForce, this);
41631         }
41632     },
41633
41634     onDestroy : function(){
41635         if(this.view){
41636             this.view.setStore(null);
41637             this.view.el.removeAllListeners();
41638             this.view.el.remove();
41639             this.view.purgeListeners();
41640         }
41641         if(this.list){
41642             this.list.destroy();
41643         }
41644         if(this.store){
41645             this.store.un('beforeload', this.onBeforeLoad, this);
41646             this.store.un('load', this.onLoad, this);
41647             this.store.un('loadexception', this.onLoadException, this);
41648         }
41649         Roo.form.ComboBox.superclass.onDestroy.call(this);
41650     },
41651
41652     // private
41653     fireKey : function(e){
41654         if(e.isNavKeyPress() && !this.list.isVisible()){
41655             this.fireEvent("specialkey", this, e);
41656         }
41657     },
41658
41659     // private
41660     onResize: function(w, h){
41661         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41662         
41663         if(typeof w != 'number'){
41664             // we do not handle it!?!?
41665             return;
41666         }
41667         var tw = this.trigger.getWidth();
41668         tw += this.addicon ? this.addicon.getWidth() : 0;
41669         tw += this.editicon ? this.editicon.getWidth() : 0;
41670         var x = w - tw;
41671         this.el.setWidth( this.adjustWidth('input', x));
41672             
41673         this.trigger.setStyle('left', x+'px');
41674         
41675         if(this.list && this.listWidth === undefined){
41676             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41677             this.list.setWidth(lw);
41678             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41679         }
41680         
41681     
41682         
41683     },
41684
41685     /**
41686      * Allow or prevent the user from directly editing the field text.  If false is passed,
41687      * the user will only be able to select from the items defined in the dropdown list.  This method
41688      * is the runtime equivalent of setting the 'editable' config option at config time.
41689      * @param {Boolean} value True to allow the user to directly edit the field text
41690      */
41691     setEditable : function(value){
41692         if(value == this.editable){
41693             return;
41694         }
41695         this.editable = value;
41696         if(!value){
41697             this.el.dom.setAttribute('readOnly', true);
41698             this.el.on('mousedown', this.onTriggerClick,  this);
41699             this.el.addClass('x-combo-noedit');
41700         }else{
41701             this.el.dom.setAttribute('readOnly', false);
41702             this.el.un('mousedown', this.onTriggerClick,  this);
41703             this.el.removeClass('x-combo-noedit');
41704         }
41705     },
41706
41707     // private
41708     onBeforeLoad : function(){
41709         if(!this.hasFocus){
41710             return;
41711         }
41712         this.innerList.update(this.loadingText ?
41713                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41714         this.restrictHeight();
41715         this.selectedIndex = -1;
41716     },
41717
41718     // private
41719     onLoad : function(){
41720         if(!this.hasFocus){
41721             return;
41722         }
41723         if(this.store.getCount() > 0){
41724             this.expand();
41725             this.restrictHeight();
41726             if(this.lastQuery == this.allQuery){
41727                 if(this.editable){
41728                     this.el.dom.select();
41729                 }
41730                 if(!this.selectByValue(this.value, true)){
41731                     this.select(0, true);
41732                 }
41733             }else{
41734                 this.selectNext();
41735                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41736                     this.taTask.delay(this.typeAheadDelay);
41737                 }
41738             }
41739         }else{
41740             this.onEmptyResults();
41741         }
41742         //this.el.focus();
41743     },
41744     // private
41745     onLoadException : function()
41746     {
41747         this.collapse();
41748         Roo.log(this.store.reader.jsonData);
41749         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41750             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41751         }
41752         
41753         
41754     },
41755     // private
41756     onTypeAhead : function(){
41757         if(this.store.getCount() > 0){
41758             var r = this.store.getAt(0);
41759             var newValue = r.data[this.displayField];
41760             var len = newValue.length;
41761             var selStart = this.getRawValue().length;
41762             if(selStart != len){
41763                 this.setRawValue(newValue);
41764                 this.selectText(selStart, newValue.length);
41765             }
41766         }
41767     },
41768
41769     // private
41770     onSelect : function(record, index){
41771         if(this.fireEvent('beforeselect', this, record, index) !== false){
41772             this.setFromData(index > -1 ? record.data : false);
41773             this.collapse();
41774             this.fireEvent('select', this, record, index);
41775         }
41776     },
41777
41778     /**
41779      * Returns the currently selected field value or empty string if no value is set.
41780      * @return {String} value The selected value
41781      */
41782     getValue : function(){
41783         if(this.valueField){
41784             return typeof this.value != 'undefined' ? this.value : '';
41785         }
41786         return Roo.form.ComboBox.superclass.getValue.call(this);
41787     },
41788
41789     /**
41790      * Clears any text/value currently set in the field
41791      */
41792     clearValue : function(){
41793         if(this.hiddenField){
41794             this.hiddenField.value = '';
41795         }
41796         this.value = '';
41797         this.setRawValue('');
41798         this.lastSelectionText = '';
41799         
41800     },
41801
41802     /**
41803      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41804      * will be displayed in the field.  If the value does not match the data value of an existing item,
41805      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41806      * Otherwise the field will be blank (although the value will still be set).
41807      * @param {String} value The value to match
41808      */
41809     setValue : function(v){
41810         var text = v;
41811         if(this.valueField){
41812             var r = this.findRecord(this.valueField, v);
41813             if(r){
41814                 text = r.data[this.displayField];
41815             }else if(this.valueNotFoundText !== undefined){
41816                 text = this.valueNotFoundText;
41817             }
41818         }
41819         this.lastSelectionText = text;
41820         if(this.hiddenField){
41821             this.hiddenField.value = v;
41822         }
41823         Roo.form.ComboBox.superclass.setValue.call(this, text);
41824         this.value = v;
41825     },
41826     /**
41827      * @property {Object} the last set data for the element
41828      */
41829     
41830     lastData : false,
41831     /**
41832      * Sets the value of the field based on a object which is related to the record format for the store.
41833      * @param {Object} value the value to set as. or false on reset?
41834      */
41835     setFromData : function(o){
41836         var dv = ''; // display value
41837         var vv = ''; // value value..
41838         this.lastData = o;
41839         if (this.displayField) {
41840             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41841         } else {
41842             // this is an error condition!!!
41843             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41844         }
41845         
41846         if(this.valueField){
41847             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41848         }
41849         if(this.hiddenField){
41850             this.hiddenField.value = vv;
41851             
41852             this.lastSelectionText = dv;
41853             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41854             this.value = vv;
41855             return;
41856         }
41857         // no hidden field.. - we store the value in 'value', but still display
41858         // display field!!!!
41859         this.lastSelectionText = dv;
41860         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41861         this.value = vv;
41862         
41863         
41864     },
41865     // private
41866     reset : function(){
41867         // overridden so that last data is reset..
41868         this.setValue(this.resetValue);
41869         this.originalValue = this.getValue();
41870         this.clearInvalid();
41871         this.lastData = false;
41872         if (this.view) {
41873             this.view.clearSelections();
41874         }
41875     },
41876     // private
41877     findRecord : function(prop, value){
41878         var record;
41879         if(this.store.getCount() > 0){
41880             this.store.each(function(r){
41881                 if(r.data[prop] == value){
41882                     record = r;
41883                     return false;
41884                 }
41885                 return true;
41886             });
41887         }
41888         return record;
41889     },
41890     
41891     getName: function()
41892     {
41893         // returns hidden if it's set..
41894         if (!this.rendered) {return ''};
41895         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41896         
41897     },
41898     // private
41899     onViewMove : function(e, t){
41900         this.inKeyMode = false;
41901     },
41902
41903     // private
41904     onViewOver : function(e, t){
41905         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41906             return;
41907         }
41908         var item = this.view.findItemFromChild(t);
41909         if(item){
41910             var index = this.view.indexOf(item);
41911             this.select(index, false);
41912         }
41913     },
41914
41915     // private
41916     onViewClick : function(doFocus)
41917     {
41918         var index = this.view.getSelectedIndexes()[0];
41919         var r = this.store.getAt(index);
41920         if(r){
41921             this.onSelect(r, index);
41922         }
41923         if(doFocus !== false && !this.blockFocus){
41924             this.el.focus();
41925         }
41926     },
41927
41928     // private
41929     restrictHeight : function(){
41930         this.innerList.dom.style.height = '';
41931         var inner = this.innerList.dom;
41932         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41933         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41934         this.list.beginUpdate();
41935         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41936         this.list.alignTo(this.el, this.listAlign);
41937         this.list.endUpdate();
41938     },
41939
41940     // private
41941     onEmptyResults : function(){
41942         this.collapse();
41943     },
41944
41945     /**
41946      * Returns true if the dropdown list is expanded, else false.
41947      */
41948     isExpanded : function(){
41949         return this.list.isVisible();
41950     },
41951
41952     /**
41953      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41954      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41955      * @param {String} value The data value of the item to select
41956      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41957      * selected item if it is not currently in view (defaults to true)
41958      * @return {Boolean} True if the value matched an item in the list, else false
41959      */
41960     selectByValue : function(v, scrollIntoView){
41961         if(v !== undefined && v !== null){
41962             var r = this.findRecord(this.valueField || this.displayField, v);
41963             if(r){
41964                 this.select(this.store.indexOf(r), scrollIntoView);
41965                 return true;
41966             }
41967         }
41968         return false;
41969     },
41970
41971     /**
41972      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41973      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41974      * @param {Number} index The zero-based index of the list item to select
41975      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41976      * selected item if it is not currently in view (defaults to true)
41977      */
41978     select : function(index, scrollIntoView){
41979         this.selectedIndex = index;
41980         this.view.select(index);
41981         if(scrollIntoView !== false){
41982             var el = this.view.getNode(index);
41983             if(el){
41984                 this.innerList.scrollChildIntoView(el, false);
41985             }
41986         }
41987     },
41988
41989     // private
41990     selectNext : function(){
41991         var ct = this.store.getCount();
41992         if(ct > 0){
41993             if(this.selectedIndex == -1){
41994                 this.select(0);
41995             }else if(this.selectedIndex < ct-1){
41996                 this.select(this.selectedIndex+1);
41997             }
41998         }
41999     },
42000
42001     // private
42002     selectPrev : function(){
42003         var ct = this.store.getCount();
42004         if(ct > 0){
42005             if(this.selectedIndex == -1){
42006                 this.select(0);
42007             }else if(this.selectedIndex != 0){
42008                 this.select(this.selectedIndex-1);
42009             }
42010         }
42011     },
42012
42013     // private
42014     onKeyUp : function(e){
42015         if(this.editable !== false && !e.isSpecialKey()){
42016             this.lastKey = e.getKey();
42017             this.dqTask.delay(this.queryDelay);
42018         }
42019     },
42020
42021     // private
42022     validateBlur : function(){
42023         return !this.list || !this.list.isVisible();   
42024     },
42025
42026     // private
42027     initQuery : function(){
42028         this.doQuery(this.getRawValue());
42029     },
42030
42031     // private
42032     doForce : function(){
42033         if(this.el.dom.value.length > 0){
42034             this.el.dom.value =
42035                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42036              
42037         }
42038     },
42039
42040     /**
42041      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42042      * query allowing the query action to be canceled if needed.
42043      * @param {String} query The SQL query to execute
42044      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42045      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42046      * saved in the current store (defaults to false)
42047      */
42048     doQuery : function(q, forceAll){
42049         if(q === undefined || q === null){
42050             q = '';
42051         }
42052         var qe = {
42053             query: q,
42054             forceAll: forceAll,
42055             combo: this,
42056             cancel:false
42057         };
42058         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42059             return false;
42060         }
42061         q = qe.query;
42062         forceAll = qe.forceAll;
42063         if(forceAll === true || (q.length >= this.minChars)){
42064             if(this.lastQuery != q || this.alwaysQuery){
42065                 this.lastQuery = q;
42066                 if(this.mode == 'local'){
42067                     this.selectedIndex = -1;
42068                     if(forceAll){
42069                         this.store.clearFilter();
42070                     }else{
42071                         this.store.filter(this.displayField, q);
42072                     }
42073                     this.onLoad();
42074                 }else{
42075                     this.store.baseParams[this.queryParam] = q;
42076                     this.store.load({
42077                         params: this.getParams(q)
42078                     });
42079                     this.expand();
42080                 }
42081             }else{
42082                 this.selectedIndex = -1;
42083                 this.onLoad();   
42084             }
42085         }
42086     },
42087
42088     // private
42089     getParams : function(q){
42090         var p = {};
42091         //p[this.queryParam] = q;
42092         if(this.pageSize){
42093             p.start = 0;
42094             p.limit = this.pageSize;
42095         }
42096         return p;
42097     },
42098
42099     /**
42100      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42101      */
42102     collapse : function(){
42103         if(!this.isExpanded()){
42104             return;
42105         }
42106         this.list.hide();
42107         Roo.get(document).un('mousedown', this.collapseIf, this);
42108         Roo.get(document).un('mousewheel', this.collapseIf, this);
42109         if (!this.editable) {
42110             Roo.get(document).un('keydown', this.listKeyPress, this);
42111         }
42112         this.fireEvent('collapse', this);
42113     },
42114
42115     // private
42116     collapseIf : function(e){
42117         if(!e.within(this.wrap) && !e.within(this.list)){
42118             this.collapse();
42119         }
42120     },
42121
42122     /**
42123      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42124      */
42125     expand : function(){
42126         if(this.isExpanded() || !this.hasFocus){
42127             return;
42128         }
42129         this.list.alignTo(this.el, this.listAlign);
42130         this.list.show();
42131         Roo.get(document).on('mousedown', this.collapseIf, this);
42132         Roo.get(document).on('mousewheel', this.collapseIf, this);
42133         if (!this.editable) {
42134             Roo.get(document).on('keydown', this.listKeyPress, this);
42135         }
42136         
42137         this.fireEvent('expand', this);
42138     },
42139
42140     // private
42141     // Implements the default empty TriggerField.onTriggerClick function
42142     onTriggerClick : function(){
42143         if(this.disabled){
42144             return;
42145         }
42146         if(this.isExpanded()){
42147             this.collapse();
42148             if (!this.blockFocus) {
42149                 this.el.focus();
42150             }
42151             
42152         }else {
42153             this.hasFocus = true;
42154             if(this.triggerAction == 'all') {
42155                 this.doQuery(this.allQuery, true);
42156             } else {
42157                 this.doQuery(this.getRawValue());
42158             }
42159             if (!this.blockFocus) {
42160                 this.el.focus();
42161             }
42162         }
42163     },
42164     listKeyPress : function(e)
42165     {
42166         //Roo.log('listkeypress');
42167         // scroll to first matching element based on key pres..
42168         if (e.isSpecialKey()) {
42169             return false;
42170         }
42171         var k = String.fromCharCode(e.getKey()).toUpperCase();
42172         //Roo.log(k);
42173         var match  = false;
42174         var csel = this.view.getSelectedNodes();
42175         var cselitem = false;
42176         if (csel.length) {
42177             var ix = this.view.indexOf(csel[0]);
42178             cselitem  = this.store.getAt(ix);
42179             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42180                 cselitem = false;
42181             }
42182             
42183         }
42184         
42185         this.store.each(function(v) { 
42186             if (cselitem) {
42187                 // start at existing selection.
42188                 if (cselitem.id == v.id) {
42189                     cselitem = false;
42190                 }
42191                 return;
42192             }
42193                 
42194             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42195                 match = this.store.indexOf(v);
42196                 return false;
42197             }
42198         }, this);
42199         
42200         if (match === false) {
42201             return true; // no more action?
42202         }
42203         // scroll to?
42204         this.view.select(match);
42205         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42206         sn.scrollIntoView(sn.dom.parentNode, false);
42207     } 
42208
42209     /** 
42210     * @cfg {Boolean} grow 
42211     * @hide 
42212     */
42213     /** 
42214     * @cfg {Number} growMin 
42215     * @hide 
42216     */
42217     /** 
42218     * @cfg {Number} growMax 
42219     * @hide 
42220     */
42221     /**
42222      * @hide
42223      * @method autoSize
42224      */
42225 });/*
42226  * Copyright(c) 2010-2012, Roo J Solutions Limited
42227  *
42228  * Licence LGPL
42229  *
42230  */
42231
42232 /**
42233  * @class Roo.form.ComboBoxArray
42234  * @extends Roo.form.TextField
42235  * A facebook style adder... for lists of email / people / countries  etc...
42236  * pick multiple items from a combo box, and shows each one.
42237  *
42238  *  Fred [x]  Brian [x]  [Pick another |v]
42239  *
42240  *
42241  *  For this to work: it needs various extra information
42242  *    - normal combo problay has
42243  *      name, hiddenName
42244  *    + displayField, valueField
42245  *
42246  *    For our purpose...
42247  *
42248  *
42249  *   If we change from 'extends' to wrapping...
42250  *   
42251  *  
42252  *
42253  
42254  
42255  * @constructor
42256  * Create a new ComboBoxArray.
42257  * @param {Object} config Configuration options
42258  */
42259  
42260
42261 Roo.form.ComboBoxArray = function(config)
42262 {
42263     this.addEvents({
42264         /**
42265          * @event beforeremove
42266          * Fires before remove the value from the list
42267              * @param {Roo.form.ComboBoxArray} _self This combo box array
42268              * @param {Roo.form.ComboBoxArray.Item} item removed item
42269              */
42270         'beforeremove' : true,
42271         /**
42272          * @event remove
42273          * Fires when remove the value from the list
42274              * @param {Roo.form.ComboBoxArray} _self This combo box array
42275              * @param {Roo.form.ComboBoxArray.Item} item removed item
42276              */
42277         'remove' : true
42278         
42279         
42280     });
42281     
42282     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42283     
42284     this.items = new Roo.util.MixedCollection(false);
42285     
42286     // construct the child combo...
42287     
42288     
42289     
42290     
42291    
42292     
42293 }
42294
42295  
42296 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42297
42298     /**
42299      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42300      */
42301     
42302     lastData : false,
42303     
42304     // behavies liek a hiddne field
42305     inputType:      'hidden',
42306     /**
42307      * @cfg {Number} width The width of the box that displays the selected element
42308      */ 
42309     width:          300,
42310
42311     
42312     
42313     /**
42314      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42315      */
42316     name : false,
42317     /**
42318      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42319      */
42320     hiddenName : false,
42321     
42322     
42323     // private the array of items that are displayed..
42324     items  : false,
42325     // private - the hidden field el.
42326     hiddenEl : false,
42327     // private - the filed el..
42328     el : false,
42329     
42330     //validateValue : function() { return true; }, // all values are ok!
42331     //onAddClick: function() { },
42332     
42333     onRender : function(ct, position) 
42334     {
42335         
42336         // create the standard hidden element
42337         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42338         
42339         
42340         // give fake names to child combo;
42341         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42342         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42343         
42344         this.combo = Roo.factory(this.combo, Roo.form);
42345         this.combo.onRender(ct, position);
42346         if (typeof(this.combo.width) != 'undefined') {
42347             this.combo.onResize(this.combo.width,0);
42348         }
42349         
42350         this.combo.initEvents();
42351         
42352         // assigned so form know we need to do this..
42353         this.store          = this.combo.store;
42354         this.valueField     = this.combo.valueField;
42355         this.displayField   = this.combo.displayField ;
42356         
42357         
42358         this.combo.wrap.addClass('x-cbarray-grp');
42359         
42360         var cbwrap = this.combo.wrap.createChild(
42361             {tag: 'div', cls: 'x-cbarray-cb'},
42362             this.combo.el.dom
42363         );
42364         
42365              
42366         this.hiddenEl = this.combo.wrap.createChild({
42367             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42368         });
42369         this.el = this.combo.wrap.createChild({
42370             tag: 'input',  type:'hidden' , name: this.name, value : ''
42371         });
42372          //   this.el.dom.removeAttribute("name");
42373         
42374         
42375         this.outerWrap = this.combo.wrap;
42376         this.wrap = cbwrap;
42377         
42378         this.outerWrap.setWidth(this.width);
42379         this.outerWrap.dom.removeChild(this.el.dom);
42380         
42381         this.wrap.dom.appendChild(this.el.dom);
42382         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42383         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42384         
42385         this.combo.trigger.setStyle('position','relative');
42386         this.combo.trigger.setStyle('left', '0px');
42387         this.combo.trigger.setStyle('top', '2px');
42388         
42389         this.combo.el.setStyle('vertical-align', 'text-bottom');
42390         
42391         //this.trigger.setStyle('vertical-align', 'top');
42392         
42393         // this should use the code from combo really... on('add' ....)
42394         if (this.adder) {
42395             
42396         
42397             this.adder = this.outerWrap.createChild(
42398                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42399             var _t = this;
42400             this.adder.on('click', function(e) {
42401                 _t.fireEvent('adderclick', this, e);
42402             }, _t);
42403         }
42404         //var _t = this;
42405         //this.adder.on('click', this.onAddClick, _t);
42406         
42407         
42408         this.combo.on('select', function(cb, rec, ix) {
42409             this.addItem(rec.data);
42410             
42411             cb.setValue('');
42412             cb.el.dom.value = '';
42413             //cb.lastData = rec.data;
42414             // add to list
42415             
42416         }, this);
42417         
42418         
42419     },
42420     
42421     
42422     getName: function()
42423     {
42424         // returns hidden if it's set..
42425         if (!this.rendered) {return ''};
42426         return  this.hiddenName ? this.hiddenName : this.name;
42427         
42428     },
42429     
42430     
42431     onResize: function(w, h){
42432         
42433         return;
42434         // not sure if this is needed..
42435         //this.combo.onResize(w,h);
42436         
42437         if(typeof w != 'number'){
42438             // we do not handle it!?!?
42439             return;
42440         }
42441         var tw = this.combo.trigger.getWidth();
42442         tw += this.addicon ? this.addicon.getWidth() : 0;
42443         tw += this.editicon ? this.editicon.getWidth() : 0;
42444         var x = w - tw;
42445         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42446             
42447         this.combo.trigger.setStyle('left', '0px');
42448         
42449         if(this.list && this.listWidth === undefined){
42450             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42451             this.list.setWidth(lw);
42452             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42453         }
42454         
42455     
42456         
42457     },
42458     
42459     addItem: function(rec)
42460     {
42461         var valueField = this.combo.valueField;
42462         var displayField = this.combo.displayField;
42463         
42464         if (this.items.indexOfKey(rec[valueField]) > -1) {
42465             //console.log("GOT " + rec.data.id);
42466             return;
42467         }
42468         
42469         var x = new Roo.form.ComboBoxArray.Item({
42470             //id : rec[this.idField],
42471             data : rec,
42472             displayField : displayField ,
42473             tipField : displayField ,
42474             cb : this
42475         });
42476         // use the 
42477         this.items.add(rec[valueField],x);
42478         // add it before the element..
42479         this.updateHiddenEl();
42480         x.render(this.outerWrap, this.wrap.dom);
42481         // add the image handler..
42482     },
42483     
42484     updateHiddenEl : function()
42485     {
42486         this.validate();
42487         if (!this.hiddenEl) {
42488             return;
42489         }
42490         var ar = [];
42491         var idField = this.combo.valueField;
42492         
42493         this.items.each(function(f) {
42494             ar.push(f.data[idField]);
42495         });
42496         this.hiddenEl.dom.value = ar.join(',');
42497         this.validate();
42498     },
42499     
42500     reset : function()
42501     {
42502         this.items.clear();
42503         
42504         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42505            el.remove();
42506         });
42507         
42508         this.el.dom.value = '';
42509         if (this.hiddenEl) {
42510             this.hiddenEl.dom.value = '';
42511         }
42512         
42513     },
42514     getValue: function()
42515     {
42516         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42517     },
42518     setValue: function(v) // not a valid action - must use addItems..
42519     {
42520         
42521         this.reset();
42522          
42523         if (this.store.isLocal && (typeof(v) == 'string')) {
42524             // then we can use the store to find the values..
42525             // comma seperated at present.. this needs to allow JSON based encoding..
42526             this.hiddenEl.value  = v;
42527             var v_ar = [];
42528             Roo.each(v.split(','), function(k) {
42529                 Roo.log("CHECK " + this.valueField + ',' + k);
42530                 var li = this.store.query(this.valueField, k);
42531                 if (!li.length) {
42532                     return;
42533                 }
42534                 var add = {};
42535                 add[this.valueField] = k;
42536                 add[this.displayField] = li.item(0).data[this.displayField];
42537                 
42538                 this.addItem(add);
42539             }, this) 
42540              
42541         }
42542         if (typeof(v) == 'object' ) {
42543             // then let's assume it's an array of objects..
42544             Roo.each(v, function(l) {
42545                 this.addItem(l);
42546             }, this);
42547              
42548         }
42549         
42550         
42551     },
42552     setFromData: function(v)
42553     {
42554         // this recieves an object, if setValues is called.
42555         this.reset();
42556         this.el.dom.value = v[this.displayField];
42557         this.hiddenEl.dom.value = v[this.valueField];
42558         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42559             return;
42560         }
42561         var kv = v[this.valueField];
42562         var dv = v[this.displayField];
42563         kv = typeof(kv) != 'string' ? '' : kv;
42564         dv = typeof(dv) != 'string' ? '' : dv;
42565         
42566         
42567         var keys = kv.split(',');
42568         var display = dv.split(',');
42569         for (var i = 0 ; i < keys.length; i++) {
42570             
42571             add = {};
42572             add[this.valueField] = keys[i];
42573             add[this.displayField] = display[i];
42574             this.addItem(add);
42575         }
42576       
42577         
42578     },
42579     
42580     /**
42581      * Validates the combox array value
42582      * @return {Boolean} True if the value is valid, else false
42583      */
42584     validate : function(){
42585         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42586             this.clearInvalid();
42587             return true;
42588         }
42589         return false;
42590     },
42591     
42592     validateValue : function(value){
42593         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42594         
42595     },
42596     
42597     /*@
42598      * overide
42599      * 
42600      */
42601     isDirty : function() {
42602         if(this.disabled) {
42603             return false;
42604         }
42605         
42606         try {
42607             var d = Roo.decode(String(this.originalValue));
42608         } catch (e) {
42609             return String(this.getValue()) !== String(this.originalValue);
42610         }
42611         
42612         var originalValue = [];
42613         
42614         for (var i = 0; i < d.length; i++){
42615             originalValue.push(d[i][this.valueField]);
42616         }
42617         
42618         return String(this.getValue()) !== String(originalValue.join(','));
42619         
42620     }
42621     
42622 });
42623
42624
42625
42626 /**
42627  * @class Roo.form.ComboBoxArray.Item
42628  * @extends Roo.BoxComponent
42629  * A selected item in the list
42630  *  Fred [x]  Brian [x]  [Pick another |v]
42631  * 
42632  * @constructor
42633  * Create a new item.
42634  * @param {Object} config Configuration options
42635  */
42636  
42637 Roo.form.ComboBoxArray.Item = function(config) {
42638     config.id = Roo.id();
42639     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42640 }
42641
42642 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42643     data : {},
42644     cb: false,
42645     displayField : false,
42646     tipField : false,
42647     
42648     
42649     defaultAutoCreate : {
42650         tag: 'div',
42651         cls: 'x-cbarray-item',
42652         cn : [ 
42653             { tag: 'div' },
42654             {
42655                 tag: 'img',
42656                 width:16,
42657                 height : 16,
42658                 src : Roo.BLANK_IMAGE_URL ,
42659                 align: 'center'
42660             }
42661         ]
42662         
42663     },
42664     
42665  
42666     onRender : function(ct, position)
42667     {
42668         Roo.form.Field.superclass.onRender.call(this, ct, position);
42669         
42670         if(!this.el){
42671             var cfg = this.getAutoCreate();
42672             this.el = ct.createChild(cfg, position);
42673         }
42674         
42675         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42676         
42677         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42678             this.cb.renderer(this.data) :
42679             String.format('{0}',this.data[this.displayField]);
42680         
42681             
42682         this.el.child('div').dom.setAttribute('qtip',
42683                         String.format('{0}',this.data[this.tipField])
42684         );
42685         
42686         this.el.child('img').on('click', this.remove, this);
42687         
42688     },
42689    
42690     remove : function()
42691     {
42692         if(this.cb.disabled){
42693             return;
42694         }
42695         
42696         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42697             this.cb.items.remove(this);
42698             this.el.child('img').un('click', this.remove, this);
42699             this.el.remove();
42700             this.cb.updateHiddenEl();
42701
42702             this.cb.fireEvent('remove', this.cb, this);
42703         }
42704         
42705     }
42706 });/*
42707  * RooJS Library 1.1.1
42708  * Copyright(c) 2008-2011  Alan Knowles
42709  *
42710  * License - LGPL
42711  */
42712  
42713
42714 /**
42715  * @class Roo.form.ComboNested
42716  * @extends Roo.form.ComboBox
42717  * A combobox for that allows selection of nested items in a list,
42718  * eg.
42719  *
42720  *  Book
42721  *    -> red
42722  *    -> green
42723  *  Table
42724  *    -> square
42725  *      ->red
42726  *      ->green
42727  *    -> rectangle
42728  *      ->green
42729  *      
42730  * 
42731  * @constructor
42732  * Create a new ComboNested
42733  * @param {Object} config Configuration options
42734  */
42735 Roo.form.ComboNested = function(config){
42736     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42737     // should verify some data...
42738     // like
42739     // hiddenName = required..
42740     // displayField = required
42741     // valudField == required
42742     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42743     var _t = this;
42744     Roo.each(req, function(e) {
42745         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42746             throw "Roo.form.ComboNested : missing value for: " + e;
42747         }
42748     });
42749      
42750     
42751 };
42752
42753 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42754    
42755     /*
42756      * @config {Number} max Number of columns to show
42757      */
42758     
42759     maxColumns : 3,
42760    
42761     list : null, // the outermost div..
42762     innerLists : null, // the
42763     views : null,
42764     stores : null,
42765     // private
42766     loadingChildren : false,
42767     
42768     onRender : function(ct, position)
42769     {
42770         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42771         
42772         if(this.hiddenName){
42773             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42774                     'before', true);
42775             this.hiddenField.value =
42776                 this.hiddenValue !== undefined ? this.hiddenValue :
42777                 this.value !== undefined ? this.value : '';
42778
42779             // prevent input submission
42780             this.el.dom.removeAttribute('name');
42781              
42782              
42783         }
42784         
42785         if(Roo.isGecko){
42786             this.el.dom.setAttribute('autocomplete', 'off');
42787         }
42788
42789         var cls = 'x-combo-list';
42790
42791         this.list = new Roo.Layer({
42792             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42793         });
42794
42795         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42796         this.list.setWidth(lw);
42797         this.list.swallowEvent('mousewheel');
42798         this.assetHeight = 0;
42799
42800         if(this.title){
42801             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42802             this.assetHeight += this.header.getHeight();
42803         }
42804         this.innerLists = [];
42805         this.views = [];
42806         this.stores = [];
42807         for (var i =0 ; i < this.maxColumns; i++) {
42808             this.onRenderList( cls, i);
42809         }
42810         
42811         // always needs footer, as we are going to have an 'OK' button.
42812         this.footer = this.list.createChild({cls:cls+'-ft'});
42813         this.pageTb = new Roo.Toolbar(this.footer);  
42814         var _this = this;
42815         this.pageTb.add(  {
42816             
42817             text: 'Done',
42818             handler: function()
42819             {
42820                 _this.collapse();
42821             }
42822         });
42823         
42824         if ( this.allowBlank && !this.disableClear) {
42825             
42826             this.pageTb.add(new Roo.Toolbar.Fill(), {
42827                 cls: 'x-btn-icon x-btn-clear',
42828                 text: '&#160;',
42829                 handler: function()
42830                 {
42831                     _this.collapse();
42832                     _this.clearValue();
42833                     _this.onSelect(false, -1);
42834                 }
42835             });
42836         }
42837         if (this.footer) {
42838             this.assetHeight += this.footer.getHeight();
42839         }
42840         
42841     },
42842     onRenderList : function (  cls, i)
42843     {
42844         
42845         var lw = Math.floor(
42846                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42847         );
42848         
42849         this.list.setWidth(lw); // default to '1'
42850
42851         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42852         //il.on('mouseover', this.onViewOver, this, { list:  i });
42853         //il.on('mousemove', this.onViewMove, this, { list:  i });
42854         il.setWidth(lw);
42855         il.setStyle({ 'overflow-x' : 'hidden'});
42856
42857         if(!this.tpl){
42858             this.tpl = new Roo.Template({
42859                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42860                 isEmpty: function (value, allValues) {
42861                     //Roo.log(value);
42862                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42863                     return dl ? 'has-children' : 'no-children'
42864                 }
42865             });
42866         }
42867         
42868         var store  = this.store;
42869         if (i > 0) {
42870             store  = new Roo.data.SimpleStore({
42871                 //fields : this.store.reader.meta.fields,
42872                 reader : this.store.reader,
42873                 data : [ ]
42874             });
42875         }
42876         this.stores[i]  = store;
42877                   
42878         var view = this.views[i] = new Roo.View(
42879             il,
42880             this.tpl,
42881             {
42882                 singleSelect:true,
42883                 store: store,
42884                 selectedClass: this.selectedClass
42885             }
42886         );
42887         view.getEl().setWidth(lw);
42888         view.getEl().setStyle({
42889             position: i < 1 ? 'relative' : 'absolute',
42890             top: 0,
42891             left: (i * lw ) + 'px',
42892             display : i > 0 ? 'none' : 'block'
42893         });
42894         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42895         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42896         //view.on('click', this.onViewClick, this, { list : i });
42897
42898         store.on('beforeload', this.onBeforeLoad, this);
42899         store.on('load',  this.onLoad, this, { list  : i});
42900         store.on('loadexception', this.onLoadException, this);
42901
42902         // hide the other vies..
42903         
42904         
42905         
42906     },
42907       
42908     restrictHeight : function()
42909     {
42910         var mh = 0;
42911         Roo.each(this.innerLists, function(il,i) {
42912             var el = this.views[i].getEl();
42913             el.dom.style.height = '';
42914             var inner = el.dom;
42915             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42916             // only adjust heights on other ones..
42917             mh = Math.max(h, mh);
42918             if (i < 1) {
42919                 
42920                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42921                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42922                
42923             }
42924             
42925             
42926         }, this);
42927         
42928         this.list.beginUpdate();
42929         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42930         this.list.alignTo(this.el, this.listAlign);
42931         this.list.endUpdate();
42932         
42933     },
42934      
42935     
42936     // -- store handlers..
42937     // private
42938     onBeforeLoad : function()
42939     {
42940         if(!this.hasFocus){
42941             return;
42942         }
42943         this.innerLists[0].update(this.loadingText ?
42944                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42945         this.restrictHeight();
42946         this.selectedIndex = -1;
42947     },
42948     // private
42949     onLoad : function(a,b,c,d)
42950     {
42951         if (!this.loadingChildren) {
42952             // then we are loading the top level. - hide the children
42953             for (var i = 1;i < this.views.length; i++) {
42954                 this.views[i].getEl().setStyle({ display : 'none' });
42955             }
42956             var lw = Math.floor(
42957                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42958             );
42959         
42960              this.list.setWidth(lw); // default to '1'
42961
42962             
42963         }
42964         if(!this.hasFocus){
42965             return;
42966         }
42967         
42968         if(this.store.getCount() > 0) {
42969             this.expand();
42970             this.restrictHeight();   
42971         } else {
42972             this.onEmptyResults();
42973         }
42974         
42975         if (!this.loadingChildren) {
42976             this.selectActive();
42977         }
42978         /*
42979         this.stores[1].loadData([]);
42980         this.stores[2].loadData([]);
42981         this.views
42982         */    
42983     
42984         //this.el.focus();
42985     },
42986     
42987     
42988     // private
42989     onLoadException : function()
42990     {
42991         this.collapse();
42992         Roo.log(this.store.reader.jsonData);
42993         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42994             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42995         }
42996         
42997         
42998     },
42999     // no cleaning of leading spaces on blur here.
43000     cleanLeadingSpace : function(e) { },
43001     
43002
43003     onSelectChange : function (view, sels, opts )
43004     {
43005         var ix = view.getSelectedIndexes();
43006          
43007         if (opts.list > this.maxColumns - 2) {
43008             if (view.store.getCount()<  1) {
43009                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43010
43011             } else  {
43012                 if (ix.length) {
43013                     // used to clear ?? but if we are loading unselected 
43014                     this.setFromData(view.store.getAt(ix[0]).data);
43015                 }
43016                 
43017             }
43018             
43019             return;
43020         }
43021         
43022         if (!ix.length) {
43023             // this get's fired when trigger opens..
43024            // this.setFromData({});
43025             var str = this.stores[opts.list+1];
43026             str.data.clear(); // removeall wihtout the fire events..
43027             return;
43028         }
43029         
43030         var rec = view.store.getAt(ix[0]);
43031          
43032         this.setFromData(rec.data);
43033         this.fireEvent('select', this, rec, ix[0]);
43034         
43035         var lw = Math.floor(
43036              (
43037                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43038              ) / this.maxColumns
43039         );
43040         this.loadingChildren = true;
43041         this.stores[opts.list+1].loadDataFromChildren( rec );
43042         this.loadingChildren = false;
43043         var dl = this.stores[opts.list+1]. getTotalCount();
43044         
43045         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43046         
43047         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43048         for (var i = opts.list+2; i < this.views.length;i++) {
43049             this.views[i].getEl().setStyle({ display : 'none' });
43050         }
43051         
43052         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43053         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43054         
43055         if (this.isLoading) {
43056            // this.selectActive(opts.list);
43057         }
43058          
43059     },
43060     
43061     
43062     
43063     
43064     onDoubleClick : function()
43065     {
43066         this.collapse(); //??
43067     },
43068     
43069      
43070     
43071     
43072     
43073     // private
43074     recordToStack : function(store, prop, value, stack)
43075     {
43076         var cstore = new Roo.data.SimpleStore({
43077             //fields : this.store.reader.meta.fields, // we need array reader.. for
43078             reader : this.store.reader,
43079             data : [ ]
43080         });
43081         var _this = this;
43082         var record  = false;
43083         var srec = false;
43084         if(store.getCount() < 1){
43085             return false;
43086         }
43087         store.each(function(r){
43088             if(r.data[prop] == value){
43089                 record = r;
43090             srec = r;
43091                 return false;
43092             }
43093             if (r.data.cn && r.data.cn.length) {
43094                 cstore.loadDataFromChildren( r);
43095                 var cret = _this.recordToStack(cstore, prop, value, stack);
43096                 if (cret !== false) {
43097                     record = cret;
43098                     srec = r;
43099                     return false;
43100                 }
43101             }
43102              
43103             return true;
43104         });
43105         if (record == false) {
43106             return false
43107         }
43108         stack.unshift(srec);
43109         return record;
43110     },
43111     
43112     /*
43113      * find the stack of stores that match our value.
43114      *
43115      * 
43116      */
43117     
43118     selectActive : function ()
43119     {
43120         // if store is not loaded, then we will need to wait for that to happen first.
43121         var stack = [];
43122         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43123         for (var i = 0; i < stack.length; i++ ) {
43124             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43125         }
43126         
43127     }
43128         
43129          
43130     
43131     
43132     
43133     
43134 });/*
43135  * Based on:
43136  * Ext JS Library 1.1.1
43137  * Copyright(c) 2006-2007, Ext JS, LLC.
43138  *
43139  * Originally Released Under LGPL - original licence link has changed is not relivant.
43140  *
43141  * Fork - LGPL
43142  * <script type="text/javascript">
43143  */
43144 /**
43145  * @class Roo.form.Checkbox
43146  * @extends Roo.form.Field
43147  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43148  * @constructor
43149  * Creates a new Checkbox
43150  * @param {Object} config Configuration options
43151  */
43152 Roo.form.Checkbox = function(config){
43153     Roo.form.Checkbox.superclass.constructor.call(this, config);
43154     this.addEvents({
43155         /**
43156          * @event check
43157          * Fires when the checkbox is checked or unchecked.
43158              * @param {Roo.form.Checkbox} this This checkbox
43159              * @param {Boolean} checked The new checked value
43160              */
43161         check : true
43162     });
43163 };
43164
43165 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43166     /**
43167      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43168      */
43169     focusClass : undefined,
43170     /**
43171      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43172      */
43173     fieldClass: "x-form-field",
43174     /**
43175      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43176      */
43177     checked: false,
43178     /**
43179      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43180      * {tag: "input", type: "checkbox", autocomplete: "off"})
43181      */
43182     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43183     /**
43184      * @cfg {String} boxLabel The text that appears beside the checkbox
43185      */
43186     boxLabel : "",
43187     /**
43188      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43189      */  
43190     inputValue : '1',
43191     /**
43192      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43193      */
43194      valueOff: '0', // value when not checked..
43195
43196     actionMode : 'viewEl', 
43197     //
43198     // private
43199     itemCls : 'x-menu-check-item x-form-item',
43200     groupClass : 'x-menu-group-item',
43201     inputType : 'hidden',
43202     
43203     
43204     inSetChecked: false, // check that we are not calling self...
43205     
43206     inputElement: false, // real input element?
43207     basedOn: false, // ????
43208     
43209     isFormField: true, // not sure where this is needed!!!!
43210
43211     onResize : function(){
43212         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43213         if(!this.boxLabel){
43214             this.el.alignTo(this.wrap, 'c-c');
43215         }
43216     },
43217
43218     initEvents : function(){
43219         Roo.form.Checkbox.superclass.initEvents.call(this);
43220         this.el.on("click", this.onClick,  this);
43221         this.el.on("change", this.onClick,  this);
43222     },
43223
43224
43225     getResizeEl : function(){
43226         return this.wrap;
43227     },
43228
43229     getPositionEl : function(){
43230         return this.wrap;
43231     },
43232
43233     // private
43234     onRender : function(ct, position){
43235         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43236         /*
43237         if(this.inputValue !== undefined){
43238             this.el.dom.value = this.inputValue;
43239         }
43240         */
43241         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43242         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43243         var viewEl = this.wrap.createChild({ 
43244             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43245         this.viewEl = viewEl;   
43246         this.wrap.on('click', this.onClick,  this); 
43247         
43248         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43249         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43250         
43251         
43252         
43253         if(this.boxLabel){
43254             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43255         //    viewEl.on('click', this.onClick,  this); 
43256         }
43257         //if(this.checked){
43258             this.setChecked(this.checked);
43259         //}else{
43260             //this.checked = this.el.dom;
43261         //}
43262
43263     },
43264
43265     // private
43266     initValue : Roo.emptyFn,
43267
43268     /**
43269      * Returns the checked state of the checkbox.
43270      * @return {Boolean} True if checked, else false
43271      */
43272     getValue : function(){
43273         if(this.el){
43274             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43275         }
43276         return this.valueOff;
43277         
43278     },
43279
43280         // private
43281     onClick : function(){ 
43282         if (this.disabled) {
43283             return;
43284         }
43285         this.setChecked(!this.checked);
43286
43287         //if(this.el.dom.checked != this.checked){
43288         //    this.setValue(this.el.dom.checked);
43289        // }
43290     },
43291
43292     /**
43293      * Sets the checked state of the checkbox.
43294      * On is always based on a string comparison between inputValue and the param.
43295      * @param {Boolean/String} value - the value to set 
43296      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43297      */
43298     setValue : function(v,suppressEvent){
43299         
43300         
43301         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43302         //if(this.el && this.el.dom){
43303         //    this.el.dom.checked = this.checked;
43304         //    this.el.dom.defaultChecked = this.checked;
43305         //}
43306         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43307         //this.fireEvent("check", this, this.checked);
43308     },
43309     // private..
43310     setChecked : function(state,suppressEvent)
43311     {
43312         if (this.inSetChecked) {
43313             this.checked = state;
43314             return;
43315         }
43316         
43317     
43318         if(this.wrap){
43319             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43320         }
43321         this.checked = state;
43322         if(suppressEvent !== true){
43323             this.fireEvent('check', this, state);
43324         }
43325         this.inSetChecked = true;
43326         this.el.dom.value = state ? this.inputValue : this.valueOff;
43327         this.inSetChecked = false;
43328         
43329     },
43330     // handle setting of hidden value by some other method!!?!?
43331     setFromHidden: function()
43332     {
43333         if(!this.el){
43334             return;
43335         }
43336         //console.log("SET FROM HIDDEN");
43337         //alert('setFrom hidden');
43338         this.setValue(this.el.dom.value);
43339     },
43340     
43341     onDestroy : function()
43342     {
43343         if(this.viewEl){
43344             Roo.get(this.viewEl).remove();
43345         }
43346          
43347         Roo.form.Checkbox.superclass.onDestroy.call(this);
43348     },
43349     
43350     setBoxLabel : function(str)
43351     {
43352         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43353     }
43354
43355 });/*
43356  * Based on:
43357  * Ext JS Library 1.1.1
43358  * Copyright(c) 2006-2007, Ext JS, LLC.
43359  *
43360  * Originally Released Under LGPL - original licence link has changed is not relivant.
43361  *
43362  * Fork - LGPL
43363  * <script type="text/javascript">
43364  */
43365  
43366 /**
43367  * @class Roo.form.Radio
43368  * @extends Roo.form.Checkbox
43369  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43370  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43371  * @constructor
43372  * Creates a new Radio
43373  * @param {Object} config Configuration options
43374  */
43375 Roo.form.Radio = function(){
43376     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43377 };
43378 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43379     inputType: 'radio',
43380
43381     /**
43382      * If this radio is part of a group, it will return the selected value
43383      * @return {String}
43384      */
43385     getGroupValue : function(){
43386         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43387     },
43388     
43389     
43390     onRender : function(ct, position){
43391         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43392         
43393         if(this.inputValue !== undefined){
43394             this.el.dom.value = this.inputValue;
43395         }
43396          
43397         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43398         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43399         //var viewEl = this.wrap.createChild({ 
43400         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43401         //this.viewEl = viewEl;   
43402         //this.wrap.on('click', this.onClick,  this); 
43403         
43404         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43405         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43406         
43407         
43408         
43409         if(this.boxLabel){
43410             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43411         //    viewEl.on('click', this.onClick,  this); 
43412         }
43413          if(this.checked){
43414             this.el.dom.checked =   'checked' ;
43415         }
43416          
43417     } 
43418     
43419     
43420 });//<script type="text/javascript">
43421
43422 /*
43423  * Based  Ext JS Library 1.1.1
43424  * Copyright(c) 2006-2007, Ext JS, LLC.
43425  * LGPL
43426  *
43427  */
43428  
43429 /**
43430  * @class Roo.HtmlEditorCore
43431  * @extends Roo.Component
43432  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43433  *
43434  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43435  */
43436
43437 Roo.HtmlEditorCore = function(config){
43438     
43439     
43440     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43441     
43442     
43443     this.addEvents({
43444         /**
43445          * @event initialize
43446          * Fires when the editor is fully initialized (including the iframe)
43447          * @param {Roo.HtmlEditorCore} this
43448          */
43449         initialize: true,
43450         /**
43451          * @event activate
43452          * Fires when the editor is first receives the focus. Any insertion must wait
43453          * until after this event.
43454          * @param {Roo.HtmlEditorCore} this
43455          */
43456         activate: true,
43457          /**
43458          * @event beforesync
43459          * Fires before the textarea is updated with content from the editor iframe. Return false
43460          * to cancel the sync.
43461          * @param {Roo.HtmlEditorCore} this
43462          * @param {String} html
43463          */
43464         beforesync: true,
43465          /**
43466          * @event beforepush
43467          * Fires before the iframe editor is updated with content from the textarea. Return false
43468          * to cancel the push.
43469          * @param {Roo.HtmlEditorCore} this
43470          * @param {String} html
43471          */
43472         beforepush: true,
43473          /**
43474          * @event sync
43475          * Fires when the textarea is updated with content from the editor iframe.
43476          * @param {Roo.HtmlEditorCore} this
43477          * @param {String} html
43478          */
43479         sync: true,
43480          /**
43481          * @event push
43482          * Fires when the iframe editor is updated with content from the textarea.
43483          * @param {Roo.HtmlEditorCore} this
43484          * @param {String} html
43485          */
43486         push: true,
43487         
43488         /**
43489          * @event editorevent
43490          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43491          * @param {Roo.HtmlEditorCore} this
43492          */
43493         editorevent: true
43494         
43495     });
43496     
43497     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43498     
43499     // defaults : white / black...
43500     this.applyBlacklists();
43501     
43502     
43503     
43504 };
43505
43506
43507 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43508
43509
43510      /**
43511      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43512      */
43513     
43514     owner : false,
43515     
43516      /**
43517      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43518      *                        Roo.resizable.
43519      */
43520     resizable : false,
43521      /**
43522      * @cfg {Number} height (in pixels)
43523      */   
43524     height: 300,
43525    /**
43526      * @cfg {Number} width (in pixels)
43527      */   
43528     width: 500,
43529     
43530     /**
43531      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43532      * 
43533      */
43534     stylesheets: false,
43535     
43536     // id of frame..
43537     frameId: false,
43538     
43539     // private properties
43540     validationEvent : false,
43541     deferHeight: true,
43542     initialized : false,
43543     activated : false,
43544     sourceEditMode : false,
43545     onFocus : Roo.emptyFn,
43546     iframePad:3,
43547     hideMode:'offsets',
43548     
43549     clearUp: true,
43550     
43551     // blacklist + whitelisted elements..
43552     black: false,
43553     white: false,
43554      
43555     bodyCls : '',
43556
43557     /**
43558      * Protected method that will not generally be called directly. It
43559      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43560      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43561      */
43562     getDocMarkup : function(){
43563         // body styles..
43564         var st = '';
43565         
43566         // inherit styels from page...?? 
43567         if (this.stylesheets === false) {
43568             
43569             Roo.get(document.head).select('style').each(function(node) {
43570                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43571             });
43572             
43573             Roo.get(document.head).select('link').each(function(node) { 
43574                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43575             });
43576             
43577         } else if (!this.stylesheets.length) {
43578                 // simple..
43579                 st = '<style type="text/css">' +
43580                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43581                    '</style>';
43582         } else { 
43583             st = '<style type="text/css">' +
43584                     this.stylesheets +
43585                 '</style>';
43586         }
43587         
43588         st +=  '<style type="text/css">' +
43589             'IMG { cursor: pointer } ' +
43590         '</style>';
43591
43592         var cls = 'roo-htmleditor-body';
43593         
43594         if(this.bodyCls.length){
43595             cls += ' ' + this.bodyCls;
43596         }
43597         
43598         return '<html><head>' + st  +
43599             //<style type="text/css">' +
43600             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43601             //'</style>' +
43602             ' </head><body class="' +  cls + '"></body></html>';
43603     },
43604
43605     // private
43606     onRender : function(ct, position)
43607     {
43608         var _t = this;
43609         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43610         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43611         
43612         
43613         this.el.dom.style.border = '0 none';
43614         this.el.dom.setAttribute('tabIndex', -1);
43615         this.el.addClass('x-hidden hide');
43616         
43617         
43618         
43619         if(Roo.isIE){ // fix IE 1px bogus margin
43620             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43621         }
43622        
43623         
43624         this.frameId = Roo.id();
43625         
43626          
43627         
43628         var iframe = this.owner.wrap.createChild({
43629             tag: 'iframe',
43630             cls: 'form-control', // bootstrap..
43631             id: this.frameId,
43632             name: this.frameId,
43633             frameBorder : 'no',
43634             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43635         }, this.el
43636         );
43637         
43638         
43639         this.iframe = iframe.dom;
43640
43641          this.assignDocWin();
43642         
43643         this.doc.designMode = 'on';
43644        
43645         this.doc.open();
43646         this.doc.write(this.getDocMarkup());
43647         this.doc.close();
43648
43649         
43650         var task = { // must defer to wait for browser to be ready
43651             run : function(){
43652                 //console.log("run task?" + this.doc.readyState);
43653                 this.assignDocWin();
43654                 if(this.doc.body || this.doc.readyState == 'complete'){
43655                     try {
43656                         this.doc.designMode="on";
43657                     } catch (e) {
43658                         return;
43659                     }
43660                     Roo.TaskMgr.stop(task);
43661                     this.initEditor.defer(10, this);
43662                 }
43663             },
43664             interval : 10,
43665             duration: 10000,
43666             scope: this
43667         };
43668         Roo.TaskMgr.start(task);
43669
43670     },
43671
43672     // private
43673     onResize : function(w, h)
43674     {
43675          Roo.log('resize: ' +w + ',' + h );
43676         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43677         if(!this.iframe){
43678             return;
43679         }
43680         if(typeof w == 'number'){
43681             
43682             this.iframe.style.width = w + 'px';
43683         }
43684         if(typeof h == 'number'){
43685             
43686             this.iframe.style.height = h + 'px';
43687             if(this.doc){
43688                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43689             }
43690         }
43691         
43692     },
43693
43694     /**
43695      * Toggles the editor between standard and source edit mode.
43696      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43697      */
43698     toggleSourceEdit : function(sourceEditMode){
43699         
43700         this.sourceEditMode = sourceEditMode === true;
43701         
43702         if(this.sourceEditMode){
43703  
43704             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43705             
43706         }else{
43707             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43708             //this.iframe.className = '';
43709             this.deferFocus();
43710         }
43711         //this.setSize(this.owner.wrap.getSize());
43712         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43713     },
43714
43715     
43716   
43717
43718     /**
43719      * Protected method that will not generally be called directly. If you need/want
43720      * custom HTML cleanup, this is the method you should override.
43721      * @param {String} html The HTML to be cleaned
43722      * return {String} The cleaned HTML
43723      */
43724     cleanHtml : function(html){
43725         html = String(html);
43726         if(html.length > 5){
43727             if(Roo.isSafari){ // strip safari nonsense
43728                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43729             }
43730         }
43731         if(html == '&nbsp;'){
43732             html = '';
43733         }
43734         return html;
43735     },
43736
43737     /**
43738      * HTML Editor -> Textarea
43739      * Protected method that will not generally be called directly. Syncs the contents
43740      * of the editor iframe with the textarea.
43741      */
43742     syncValue : function(){
43743         if(this.initialized){
43744             var bd = (this.doc.body || this.doc.documentElement);
43745             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43746             var html = bd.innerHTML;
43747             if(Roo.isSafari){
43748                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43749                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43750                 if(m && m[1]){
43751                     html = '<div style="'+m[0]+'">' + html + '</div>';
43752                 }
43753             }
43754             html = this.cleanHtml(html);
43755             // fix up the special chars.. normaly like back quotes in word...
43756             // however we do not want to do this with chinese..
43757             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43758                 
43759                 var cc = match.charCodeAt();
43760
43761                 // Get the character value, handling surrogate pairs
43762                 if (match.length == 2) {
43763                     // It's a surrogate pair, calculate the Unicode code point
43764                     var high = match.charCodeAt(0) - 0xD800;
43765                     var low  = match.charCodeAt(1) - 0xDC00;
43766                     cc = (high * 0x400) + low + 0x10000;
43767                 }  else if (
43768                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43769                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43770                     (cc >= 0xf900 && cc < 0xfb00 )
43771                 ) {
43772                         return match;
43773                 }  
43774          
43775                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43776                 return "&#" + cc + ";";
43777                 
43778                 
43779             });
43780             
43781             
43782              
43783             if(this.owner.fireEvent('beforesync', this, html) !== false){
43784                 this.el.dom.value = html;
43785                 this.owner.fireEvent('sync', this, html);
43786             }
43787         }
43788     },
43789
43790     /**
43791      * Protected method that will not generally be called directly. Pushes the value of the textarea
43792      * into the iframe editor.
43793      */
43794     pushValue : function(){
43795         if(this.initialized){
43796             var v = this.el.dom.value.trim();
43797             
43798 //            if(v.length < 1){
43799 //                v = '&#160;';
43800 //            }
43801             
43802             if(this.owner.fireEvent('beforepush', this, v) !== false){
43803                 var d = (this.doc.body || this.doc.documentElement);
43804                 d.innerHTML = v;
43805                 this.cleanUpPaste();
43806                 this.el.dom.value = d.innerHTML;
43807                 this.owner.fireEvent('push', this, v);
43808             }
43809         }
43810     },
43811
43812     // private
43813     deferFocus : function(){
43814         this.focus.defer(10, this);
43815     },
43816
43817     // doc'ed in Field
43818     focus : function(){
43819         if(this.win && !this.sourceEditMode){
43820             this.win.focus();
43821         }else{
43822             this.el.focus();
43823         }
43824     },
43825     
43826     assignDocWin: function()
43827     {
43828         var iframe = this.iframe;
43829         
43830          if(Roo.isIE){
43831             this.doc = iframe.contentWindow.document;
43832             this.win = iframe.contentWindow;
43833         } else {
43834 //            if (!Roo.get(this.frameId)) {
43835 //                return;
43836 //            }
43837 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43838 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43839             
43840             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43841                 return;
43842             }
43843             
43844             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43845             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43846         }
43847     },
43848     
43849     // private
43850     initEditor : function(){
43851         //console.log("INIT EDITOR");
43852         this.assignDocWin();
43853         
43854         
43855         
43856         this.doc.designMode="on";
43857         this.doc.open();
43858         this.doc.write(this.getDocMarkup());
43859         this.doc.close();
43860         
43861         var dbody = (this.doc.body || this.doc.documentElement);
43862         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43863         // this copies styles from the containing element into thsi one..
43864         // not sure why we need all of this..
43865         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43866         
43867         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43868         //ss['background-attachment'] = 'fixed'; // w3c
43869         dbody.bgProperties = 'fixed'; // ie
43870         //Roo.DomHelper.applyStyles(dbody, ss);
43871         Roo.EventManager.on(this.doc, {
43872             //'mousedown': this.onEditorEvent,
43873             'mouseup': this.onEditorEvent,
43874             'dblclick': this.onEditorEvent,
43875             'click': this.onEditorEvent,
43876             'keyup': this.onEditorEvent,
43877             buffer:100,
43878             scope: this
43879         });
43880         if(Roo.isGecko){
43881             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43882         }
43883         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43884             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43885         }
43886         this.initialized = true;
43887
43888         this.owner.fireEvent('initialize', this);
43889         this.pushValue();
43890     },
43891
43892     // private
43893     onDestroy : function(){
43894         
43895         
43896         
43897         if(this.rendered){
43898             
43899             //for (var i =0; i < this.toolbars.length;i++) {
43900             //    // fixme - ask toolbars for heights?
43901             //    this.toolbars[i].onDestroy();
43902            // }
43903             
43904             //this.wrap.dom.innerHTML = '';
43905             //this.wrap.remove();
43906         }
43907     },
43908
43909     // private
43910     onFirstFocus : function(){
43911         
43912         this.assignDocWin();
43913         
43914         
43915         this.activated = true;
43916          
43917     
43918         if(Roo.isGecko){ // prevent silly gecko errors
43919             this.win.focus();
43920             var s = this.win.getSelection();
43921             if(!s.focusNode || s.focusNode.nodeType != 3){
43922                 var r = s.getRangeAt(0);
43923                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43924                 r.collapse(true);
43925                 this.deferFocus();
43926             }
43927             try{
43928                 this.execCmd('useCSS', true);
43929                 this.execCmd('styleWithCSS', false);
43930             }catch(e){}
43931         }
43932         this.owner.fireEvent('activate', this);
43933     },
43934
43935     // private
43936     adjustFont: function(btn){
43937         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43938         //if(Roo.isSafari){ // safari
43939         //    adjust *= 2;
43940        // }
43941         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43942         if(Roo.isSafari){ // safari
43943             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43944             v =  (v < 10) ? 10 : v;
43945             v =  (v > 48) ? 48 : v;
43946             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43947             
43948         }
43949         
43950         
43951         v = Math.max(1, v+adjust);
43952         
43953         this.execCmd('FontSize', v  );
43954     },
43955
43956     onEditorEvent : function(e)
43957     {
43958         this.owner.fireEvent('editorevent', this, e);
43959       //  this.updateToolbar();
43960         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43961     },
43962
43963     insertTag : function(tg)
43964     {
43965         // could be a bit smarter... -> wrap the current selected tRoo..
43966         if (tg.toLowerCase() == 'span' ||
43967             tg.toLowerCase() == 'code' ||
43968             tg.toLowerCase() == 'sup' ||
43969             tg.toLowerCase() == 'sub' 
43970             ) {
43971             
43972             range = this.createRange(this.getSelection());
43973             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43974             wrappingNode.appendChild(range.extractContents());
43975             range.insertNode(wrappingNode);
43976
43977             return;
43978             
43979             
43980             
43981         }
43982         this.execCmd("formatblock",   tg);
43983         
43984     },
43985     
43986     insertText : function(txt)
43987     {
43988         
43989         
43990         var range = this.createRange();
43991         range.deleteContents();
43992                //alert(Sender.getAttribute('label'));
43993                
43994         range.insertNode(this.doc.createTextNode(txt));
43995     } ,
43996     
43997      
43998
43999     /**
44000      * Executes a Midas editor command on the editor document and performs necessary focus and
44001      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44002      * @param {String} cmd The Midas command
44003      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44004      */
44005     relayCmd : function(cmd, value){
44006         this.win.focus();
44007         this.execCmd(cmd, value);
44008         this.owner.fireEvent('editorevent', this);
44009         //this.updateToolbar();
44010         this.owner.deferFocus();
44011     },
44012
44013     /**
44014      * Executes a Midas editor command directly on the editor document.
44015      * For visual commands, you should use {@link #relayCmd} instead.
44016      * <b>This should only be called after the editor is initialized.</b>
44017      * @param {String} cmd The Midas command
44018      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44019      */
44020     execCmd : function(cmd, value){
44021         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44022         this.syncValue();
44023     },
44024  
44025  
44026    
44027     /**
44028      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44029      * to insert tRoo.
44030      * @param {String} text | dom node.. 
44031      */
44032     insertAtCursor : function(text)
44033     {
44034         
44035         if(!this.activated){
44036             return;
44037         }
44038         /*
44039         if(Roo.isIE){
44040             this.win.focus();
44041             var r = this.doc.selection.createRange();
44042             if(r){
44043                 r.collapse(true);
44044                 r.pasteHTML(text);
44045                 this.syncValue();
44046                 this.deferFocus();
44047             
44048             }
44049             return;
44050         }
44051         */
44052         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44053             this.win.focus();
44054             
44055             
44056             // from jquery ui (MIT licenced)
44057             var range, node;
44058             var win = this.win;
44059             
44060             if (win.getSelection && win.getSelection().getRangeAt) {
44061                 range = win.getSelection().getRangeAt(0);
44062                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44063                 range.insertNode(node);
44064             } else if (win.document.selection && win.document.selection.createRange) {
44065                 // no firefox support
44066                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44067                 win.document.selection.createRange().pasteHTML(txt);
44068             } else {
44069                 // no firefox support
44070                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44071                 this.execCmd('InsertHTML', txt);
44072             } 
44073             
44074             this.syncValue();
44075             
44076             this.deferFocus();
44077         }
44078     },
44079  // private
44080     mozKeyPress : function(e){
44081         if(e.ctrlKey){
44082             var c = e.getCharCode(), cmd;
44083           
44084             if(c > 0){
44085                 c = String.fromCharCode(c).toLowerCase();
44086                 switch(c){
44087                     case 'b':
44088                         cmd = 'bold';
44089                         break;
44090                     case 'i':
44091                         cmd = 'italic';
44092                         break;
44093                     
44094                     case 'u':
44095                         cmd = 'underline';
44096                         break;
44097                     
44098                     case 'v':
44099                         this.cleanUpPaste.defer(100, this);
44100                         return;
44101                         
44102                 }
44103                 if(cmd){
44104                     this.win.focus();
44105                     this.execCmd(cmd);
44106                     this.deferFocus();
44107                     e.preventDefault();
44108                 }
44109                 
44110             }
44111         }
44112     },
44113
44114     // private
44115     fixKeys : function(){ // load time branching for fastest keydown performance
44116         if(Roo.isIE){
44117             return function(e){
44118                 var k = e.getKey(), r;
44119                 if(k == e.TAB){
44120                     e.stopEvent();
44121                     r = this.doc.selection.createRange();
44122                     if(r){
44123                         r.collapse(true);
44124                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44125                         this.deferFocus();
44126                     }
44127                     return;
44128                 }
44129                 
44130                 if(k == e.ENTER){
44131                     r = this.doc.selection.createRange();
44132                     if(r){
44133                         var target = r.parentElement();
44134                         if(!target || target.tagName.toLowerCase() != 'li'){
44135                             e.stopEvent();
44136                             r.pasteHTML('<br />');
44137                             r.collapse(false);
44138                             r.select();
44139                         }
44140                     }
44141                 }
44142                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44143                     this.cleanUpPaste.defer(100, this);
44144                     return;
44145                 }
44146                 
44147                 
44148             };
44149         }else if(Roo.isOpera){
44150             return function(e){
44151                 var k = e.getKey();
44152                 if(k == e.TAB){
44153                     e.stopEvent();
44154                     this.win.focus();
44155                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44156                     this.deferFocus();
44157                 }
44158                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44159                     this.cleanUpPaste.defer(100, this);
44160                     return;
44161                 }
44162                 
44163             };
44164         }else if(Roo.isSafari){
44165             return function(e){
44166                 var k = e.getKey();
44167                 
44168                 if(k == e.TAB){
44169                     e.stopEvent();
44170                     this.execCmd('InsertText','\t');
44171                     this.deferFocus();
44172                     return;
44173                 }
44174                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44175                     this.cleanUpPaste.defer(100, this);
44176                     return;
44177                 }
44178                 
44179              };
44180         }
44181     }(),
44182     
44183     getAllAncestors: function()
44184     {
44185         var p = this.getSelectedNode();
44186         var a = [];
44187         if (!p) {
44188             a.push(p); // push blank onto stack..
44189             p = this.getParentElement();
44190         }
44191         
44192         
44193         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44194             a.push(p);
44195             p = p.parentNode;
44196         }
44197         a.push(this.doc.body);
44198         return a;
44199     },
44200     lastSel : false,
44201     lastSelNode : false,
44202     
44203     
44204     getSelection : function() 
44205     {
44206         this.assignDocWin();
44207         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44208     },
44209     
44210     getSelectedNode: function() 
44211     {
44212         // this may only work on Gecko!!!
44213         
44214         // should we cache this!!!!
44215         
44216         
44217         
44218          
44219         var range = this.createRange(this.getSelection()).cloneRange();
44220         
44221         if (Roo.isIE) {
44222             var parent = range.parentElement();
44223             while (true) {
44224                 var testRange = range.duplicate();
44225                 testRange.moveToElementText(parent);
44226                 if (testRange.inRange(range)) {
44227                     break;
44228                 }
44229                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44230                     break;
44231                 }
44232                 parent = parent.parentElement;
44233             }
44234             return parent;
44235         }
44236         
44237         // is ancestor a text element.
44238         var ac =  range.commonAncestorContainer;
44239         if (ac.nodeType == 3) {
44240             ac = ac.parentNode;
44241         }
44242         
44243         var ar = ac.childNodes;
44244          
44245         var nodes = [];
44246         var other_nodes = [];
44247         var has_other_nodes = false;
44248         for (var i=0;i<ar.length;i++) {
44249             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44250                 continue;
44251             }
44252             // fullly contained node.
44253             
44254             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44255                 nodes.push(ar[i]);
44256                 continue;
44257             }
44258             
44259             // probably selected..
44260             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44261                 other_nodes.push(ar[i]);
44262                 continue;
44263             }
44264             // outer..
44265             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44266                 continue;
44267             }
44268             
44269             
44270             has_other_nodes = true;
44271         }
44272         if (!nodes.length && other_nodes.length) {
44273             nodes= other_nodes;
44274         }
44275         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44276             return false;
44277         }
44278         
44279         return nodes[0];
44280     },
44281     createRange: function(sel)
44282     {
44283         // this has strange effects when using with 
44284         // top toolbar - not sure if it's a great idea.
44285         //this.editor.contentWindow.focus();
44286         if (typeof sel != "undefined") {
44287             try {
44288                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44289             } catch(e) {
44290                 return this.doc.createRange();
44291             }
44292         } else {
44293             return this.doc.createRange();
44294         }
44295     },
44296     getParentElement: function()
44297     {
44298         
44299         this.assignDocWin();
44300         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44301         
44302         var range = this.createRange(sel);
44303          
44304         try {
44305             var p = range.commonAncestorContainer;
44306             while (p.nodeType == 3) { // text node
44307                 p = p.parentNode;
44308             }
44309             return p;
44310         } catch (e) {
44311             return null;
44312         }
44313     
44314     },
44315     /***
44316      *
44317      * Range intersection.. the hard stuff...
44318      *  '-1' = before
44319      *  '0' = hits..
44320      *  '1' = after.
44321      *         [ -- selected range --- ]
44322      *   [fail]                        [fail]
44323      *
44324      *    basically..
44325      *      if end is before start or  hits it. fail.
44326      *      if start is after end or hits it fail.
44327      *
44328      *   if either hits (but other is outside. - then it's not 
44329      *   
44330      *    
44331      **/
44332     
44333     
44334     // @see http://www.thismuchiknow.co.uk/?p=64.
44335     rangeIntersectsNode : function(range, node)
44336     {
44337         var nodeRange = node.ownerDocument.createRange();
44338         try {
44339             nodeRange.selectNode(node);
44340         } catch (e) {
44341             nodeRange.selectNodeContents(node);
44342         }
44343     
44344         var rangeStartRange = range.cloneRange();
44345         rangeStartRange.collapse(true);
44346     
44347         var rangeEndRange = range.cloneRange();
44348         rangeEndRange.collapse(false);
44349     
44350         var nodeStartRange = nodeRange.cloneRange();
44351         nodeStartRange.collapse(true);
44352     
44353         var nodeEndRange = nodeRange.cloneRange();
44354         nodeEndRange.collapse(false);
44355     
44356         return rangeStartRange.compareBoundaryPoints(
44357                  Range.START_TO_START, nodeEndRange) == -1 &&
44358                rangeEndRange.compareBoundaryPoints(
44359                  Range.START_TO_START, nodeStartRange) == 1;
44360         
44361          
44362     },
44363     rangeCompareNode : function(range, node)
44364     {
44365         var nodeRange = node.ownerDocument.createRange();
44366         try {
44367             nodeRange.selectNode(node);
44368         } catch (e) {
44369             nodeRange.selectNodeContents(node);
44370         }
44371         
44372         
44373         range.collapse(true);
44374     
44375         nodeRange.collapse(true);
44376      
44377         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44378         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44379          
44380         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44381         
44382         var nodeIsBefore   =  ss == 1;
44383         var nodeIsAfter    = ee == -1;
44384         
44385         if (nodeIsBefore && nodeIsAfter) {
44386             return 0; // outer
44387         }
44388         if (!nodeIsBefore && nodeIsAfter) {
44389             return 1; //right trailed.
44390         }
44391         
44392         if (nodeIsBefore && !nodeIsAfter) {
44393             return 2;  // left trailed.
44394         }
44395         // fully contined.
44396         return 3;
44397     },
44398
44399     // private? - in a new class?
44400     cleanUpPaste :  function()
44401     {
44402         // cleans up the whole document..
44403         Roo.log('cleanuppaste');
44404         
44405         this.cleanUpChildren(this.doc.body);
44406         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44407         if (clean != this.doc.body.innerHTML) {
44408             this.doc.body.innerHTML = clean;
44409         }
44410         
44411     },
44412     
44413     cleanWordChars : function(input) {// change the chars to hex code
44414         var he = Roo.HtmlEditorCore;
44415         
44416         var output = input;
44417         Roo.each(he.swapCodes, function(sw) { 
44418             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44419             
44420             output = output.replace(swapper, sw[1]);
44421         });
44422         
44423         return output;
44424     },
44425     
44426     
44427     cleanUpChildren : function (n)
44428     {
44429         if (!n.childNodes.length) {
44430             return;
44431         }
44432         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44433            this.cleanUpChild(n.childNodes[i]);
44434         }
44435     },
44436     
44437     
44438         
44439     
44440     cleanUpChild : function (node)
44441     {
44442         var ed = this;
44443         //console.log(node);
44444         if (node.nodeName == "#text") {
44445             // clean up silly Windows -- stuff?
44446             return; 
44447         }
44448         if (node.nodeName == "#comment") {
44449             node.parentNode.removeChild(node);
44450             // clean up silly Windows -- stuff?
44451             return; 
44452         }
44453         var lcname = node.tagName.toLowerCase();
44454         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44455         // whitelist of tags..
44456         
44457         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44458             // remove node.
44459             node.parentNode.removeChild(node);
44460             return;
44461             
44462         }
44463         
44464         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44465         
44466         // spans with no attributes - just remove them..
44467         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44468             remove_keep_children = true;
44469         }
44470         
44471         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44472         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44473         
44474         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44475         //    remove_keep_children = true;
44476         //}
44477         
44478         if (remove_keep_children) {
44479             this.cleanUpChildren(node);
44480             // inserts everything just before this node...
44481             while (node.childNodes.length) {
44482                 var cn = node.childNodes[0];
44483                 node.removeChild(cn);
44484                 node.parentNode.insertBefore(cn, node);
44485             }
44486             node.parentNode.removeChild(node);
44487             return;
44488         }
44489         
44490         if (!node.attributes || !node.attributes.length) {
44491             
44492           
44493             
44494             
44495             this.cleanUpChildren(node);
44496             return;
44497         }
44498         
44499         function cleanAttr(n,v)
44500         {
44501             
44502             if (v.match(/^\./) || v.match(/^\//)) {
44503                 return;
44504             }
44505             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44506                 return;
44507             }
44508             if (v.match(/^#/)) {
44509                 return;
44510             }
44511 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44512             node.removeAttribute(n);
44513             
44514         }
44515         
44516         var cwhite = this.cwhite;
44517         var cblack = this.cblack;
44518             
44519         function cleanStyle(n,v)
44520         {
44521             if (v.match(/expression/)) { //XSS?? should we even bother..
44522                 node.removeAttribute(n);
44523                 return;
44524             }
44525             
44526             var parts = v.split(/;/);
44527             var clean = [];
44528             
44529             Roo.each(parts, function(p) {
44530                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44531                 if (!p.length) {
44532                     return true;
44533                 }
44534                 var l = p.split(':').shift().replace(/\s+/g,'');
44535                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44536                 
44537                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44538 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44539                     //node.removeAttribute(n);
44540                     return true;
44541                 }
44542                 //Roo.log()
44543                 // only allow 'c whitelisted system attributes'
44544                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44545 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44546                     //node.removeAttribute(n);
44547                     return true;
44548                 }
44549                 
44550                 
44551                  
44552                 
44553                 clean.push(p);
44554                 return true;
44555             });
44556             if (clean.length) { 
44557                 node.setAttribute(n, clean.join(';'));
44558             } else {
44559                 node.removeAttribute(n);
44560             }
44561             
44562         }
44563         
44564         
44565         for (var i = node.attributes.length-1; i > -1 ; i--) {
44566             var a = node.attributes[i];
44567             //console.log(a);
44568             
44569             if (a.name.toLowerCase().substr(0,2)=='on')  {
44570                 node.removeAttribute(a.name);
44571                 continue;
44572             }
44573             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44574                 node.removeAttribute(a.name);
44575                 continue;
44576             }
44577             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44578                 cleanAttr(a.name,a.value); // fixme..
44579                 continue;
44580             }
44581             if (a.name == 'style') {
44582                 cleanStyle(a.name,a.value);
44583                 continue;
44584             }
44585             /// clean up MS crap..
44586             // tecnically this should be a list of valid class'es..
44587             
44588             
44589             if (a.name == 'class') {
44590                 if (a.value.match(/^Mso/)) {
44591                     node.removeAttribute('class');
44592                 }
44593                 
44594                 if (a.value.match(/^body$/)) {
44595                     node.removeAttribute('class');
44596                 }
44597                 continue;
44598             }
44599             
44600             // style cleanup!?
44601             // class cleanup?
44602             
44603         }
44604         
44605         
44606         this.cleanUpChildren(node);
44607         
44608         
44609     },
44610     
44611     /**
44612      * Clean up MS wordisms...
44613      */
44614     cleanWord : function(node)
44615     {
44616         if (!node) {
44617             this.cleanWord(this.doc.body);
44618             return;
44619         }
44620         
44621         if(
44622                 node.nodeName == 'SPAN' &&
44623                 !node.hasAttributes() &&
44624                 node.childNodes.length == 1 &&
44625                 node.firstChild.nodeName == "#text"  
44626         ) {
44627             var textNode = node.firstChild;
44628             node.removeChild(textNode);
44629             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44630                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44631             }
44632             node.parentNode.insertBefore(textNode, node);
44633             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44634                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44635             }
44636             node.parentNode.removeChild(node);
44637         }
44638         
44639         if (node.nodeName == "#text") {
44640             // clean up silly Windows -- stuff?
44641             return; 
44642         }
44643         if (node.nodeName == "#comment") {
44644             node.parentNode.removeChild(node);
44645             // clean up silly Windows -- stuff?
44646             return; 
44647         }
44648         
44649         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44650             node.parentNode.removeChild(node);
44651             return;
44652         }
44653         //Roo.log(node.tagName);
44654         // remove - but keep children..
44655         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44656             //Roo.log('-- removed');
44657             while (node.childNodes.length) {
44658                 var cn = node.childNodes[0];
44659                 node.removeChild(cn);
44660                 node.parentNode.insertBefore(cn, node);
44661                 // move node to parent - and clean it..
44662                 this.cleanWord(cn);
44663             }
44664             node.parentNode.removeChild(node);
44665             /// no need to iterate chidlren = it's got none..
44666             //this.iterateChildren(node, this.cleanWord);
44667             return;
44668         }
44669         // clean styles
44670         if (node.className.length) {
44671             
44672             var cn = node.className.split(/\W+/);
44673             var cna = [];
44674             Roo.each(cn, function(cls) {
44675                 if (cls.match(/Mso[a-zA-Z]+/)) {
44676                     return;
44677                 }
44678                 cna.push(cls);
44679             });
44680             node.className = cna.length ? cna.join(' ') : '';
44681             if (!cna.length) {
44682                 node.removeAttribute("class");
44683             }
44684         }
44685         
44686         if (node.hasAttribute("lang")) {
44687             node.removeAttribute("lang");
44688         }
44689         
44690         if (node.hasAttribute("style")) {
44691             
44692             var styles = node.getAttribute("style").split(";");
44693             var nstyle = [];
44694             Roo.each(styles, function(s) {
44695                 if (!s.match(/:/)) {
44696                     return;
44697                 }
44698                 var kv = s.split(":");
44699                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44700                     return;
44701                 }
44702                 // what ever is left... we allow.
44703                 nstyle.push(s);
44704             });
44705             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44706             if (!nstyle.length) {
44707                 node.removeAttribute('style');
44708             }
44709         }
44710         this.iterateChildren(node, this.cleanWord);
44711         
44712         
44713         
44714     },
44715     /**
44716      * iterateChildren of a Node, calling fn each time, using this as the scole..
44717      * @param {DomNode} node node to iterate children of.
44718      * @param {Function} fn method of this class to call on each item.
44719      */
44720     iterateChildren : function(node, fn)
44721     {
44722         if (!node.childNodes.length) {
44723                 return;
44724         }
44725         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44726            fn.call(this, node.childNodes[i])
44727         }
44728     },
44729     
44730     
44731     /**
44732      * cleanTableWidths.
44733      *
44734      * Quite often pasting from word etc.. results in tables with column and widths.
44735      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44736      *
44737      */
44738     cleanTableWidths : function(node)
44739     {
44740          
44741          
44742         if (!node) {
44743             this.cleanTableWidths(this.doc.body);
44744             return;
44745         }
44746         
44747         // ignore list...
44748         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44749             return; 
44750         }
44751         Roo.log(node.tagName);
44752         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44753             this.iterateChildren(node, this.cleanTableWidths);
44754             return;
44755         }
44756         if (node.hasAttribute('width')) {
44757             node.removeAttribute('width');
44758         }
44759         
44760          
44761         if (node.hasAttribute("style")) {
44762             // pretty basic...
44763             
44764             var styles = node.getAttribute("style").split(";");
44765             var nstyle = [];
44766             Roo.each(styles, function(s) {
44767                 if (!s.match(/:/)) {
44768                     return;
44769                 }
44770                 var kv = s.split(":");
44771                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44772                     return;
44773                 }
44774                 // what ever is left... we allow.
44775                 nstyle.push(s);
44776             });
44777             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44778             if (!nstyle.length) {
44779                 node.removeAttribute('style');
44780             }
44781         }
44782         
44783         this.iterateChildren(node, this.cleanTableWidths);
44784         
44785         
44786     },
44787     
44788     
44789     
44790     
44791     domToHTML : function(currentElement, depth, nopadtext) {
44792         
44793         depth = depth || 0;
44794         nopadtext = nopadtext || false;
44795     
44796         if (!currentElement) {
44797             return this.domToHTML(this.doc.body);
44798         }
44799         
44800         //Roo.log(currentElement);
44801         var j;
44802         var allText = false;
44803         var nodeName = currentElement.nodeName;
44804         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44805         
44806         if  (nodeName == '#text') {
44807             
44808             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44809         }
44810         
44811         
44812         var ret = '';
44813         if (nodeName != 'BODY') {
44814              
44815             var i = 0;
44816             // Prints the node tagName, such as <A>, <IMG>, etc
44817             if (tagName) {
44818                 var attr = [];
44819                 for(i = 0; i < currentElement.attributes.length;i++) {
44820                     // quoting?
44821                     var aname = currentElement.attributes.item(i).name;
44822                     if (!currentElement.attributes.item(i).value.length) {
44823                         continue;
44824                     }
44825                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44826                 }
44827                 
44828                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44829             } 
44830             else {
44831                 
44832                 // eack
44833             }
44834         } else {
44835             tagName = false;
44836         }
44837         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44838             return ret;
44839         }
44840         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44841             nopadtext = true;
44842         }
44843         
44844         
44845         // Traverse the tree
44846         i = 0;
44847         var currentElementChild = currentElement.childNodes.item(i);
44848         var allText = true;
44849         var innerHTML  = '';
44850         lastnode = '';
44851         while (currentElementChild) {
44852             // Formatting code (indent the tree so it looks nice on the screen)
44853             var nopad = nopadtext;
44854             if (lastnode == 'SPAN') {
44855                 nopad  = true;
44856             }
44857             // text
44858             if  (currentElementChild.nodeName == '#text') {
44859                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44860                 toadd = nopadtext ? toadd : toadd.trim();
44861                 if (!nopad && toadd.length > 80) {
44862                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44863                 }
44864                 innerHTML  += toadd;
44865                 
44866                 i++;
44867                 currentElementChild = currentElement.childNodes.item(i);
44868                 lastNode = '';
44869                 continue;
44870             }
44871             allText = false;
44872             
44873             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44874                 
44875             // Recursively traverse the tree structure of the child node
44876             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44877             lastnode = currentElementChild.nodeName;
44878             i++;
44879             currentElementChild=currentElement.childNodes.item(i);
44880         }
44881         
44882         ret += innerHTML;
44883         
44884         if (!allText) {
44885                 // The remaining code is mostly for formatting the tree
44886             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44887         }
44888         
44889         
44890         if (tagName) {
44891             ret+= "</"+tagName+">";
44892         }
44893         return ret;
44894         
44895     },
44896         
44897     applyBlacklists : function()
44898     {
44899         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44900         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44901         
44902         this.white = [];
44903         this.black = [];
44904         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44905             if (b.indexOf(tag) > -1) {
44906                 return;
44907             }
44908             this.white.push(tag);
44909             
44910         }, this);
44911         
44912         Roo.each(w, function(tag) {
44913             if (b.indexOf(tag) > -1) {
44914                 return;
44915             }
44916             if (this.white.indexOf(tag) > -1) {
44917                 return;
44918             }
44919             this.white.push(tag);
44920             
44921         }, this);
44922         
44923         
44924         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44925             if (w.indexOf(tag) > -1) {
44926                 return;
44927             }
44928             this.black.push(tag);
44929             
44930         }, this);
44931         
44932         Roo.each(b, function(tag) {
44933             if (w.indexOf(tag) > -1) {
44934                 return;
44935             }
44936             if (this.black.indexOf(tag) > -1) {
44937                 return;
44938             }
44939             this.black.push(tag);
44940             
44941         }, this);
44942         
44943         
44944         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44945         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44946         
44947         this.cwhite = [];
44948         this.cblack = [];
44949         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44950             if (b.indexOf(tag) > -1) {
44951                 return;
44952             }
44953             this.cwhite.push(tag);
44954             
44955         }, this);
44956         
44957         Roo.each(w, function(tag) {
44958             if (b.indexOf(tag) > -1) {
44959                 return;
44960             }
44961             if (this.cwhite.indexOf(tag) > -1) {
44962                 return;
44963             }
44964             this.cwhite.push(tag);
44965             
44966         }, this);
44967         
44968         
44969         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44970             if (w.indexOf(tag) > -1) {
44971                 return;
44972             }
44973             this.cblack.push(tag);
44974             
44975         }, this);
44976         
44977         Roo.each(b, function(tag) {
44978             if (w.indexOf(tag) > -1) {
44979                 return;
44980             }
44981             if (this.cblack.indexOf(tag) > -1) {
44982                 return;
44983             }
44984             this.cblack.push(tag);
44985             
44986         }, this);
44987     },
44988     
44989     setStylesheets : function(stylesheets)
44990     {
44991         if(typeof(stylesheets) == 'string'){
44992             Roo.get(this.iframe.contentDocument.head).createChild({
44993                 tag : 'link',
44994                 rel : 'stylesheet',
44995                 type : 'text/css',
44996                 href : stylesheets
44997             });
44998             
44999             return;
45000         }
45001         var _this = this;
45002      
45003         Roo.each(stylesheets, function(s) {
45004             if(!s.length){
45005                 return;
45006             }
45007             
45008             Roo.get(_this.iframe.contentDocument.head).createChild({
45009                 tag : 'link',
45010                 rel : 'stylesheet',
45011                 type : 'text/css',
45012                 href : s
45013             });
45014         });
45015
45016         
45017     },
45018     
45019     removeStylesheets : function()
45020     {
45021         var _this = this;
45022         
45023         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45024             s.remove();
45025         });
45026     },
45027     
45028     setStyle : function(style)
45029     {
45030         Roo.get(this.iframe.contentDocument.head).createChild({
45031             tag : 'style',
45032             type : 'text/css',
45033             html : style
45034         });
45035
45036         return;
45037     }
45038     
45039     // hide stuff that is not compatible
45040     /**
45041      * @event blur
45042      * @hide
45043      */
45044     /**
45045      * @event change
45046      * @hide
45047      */
45048     /**
45049      * @event focus
45050      * @hide
45051      */
45052     /**
45053      * @event specialkey
45054      * @hide
45055      */
45056     /**
45057      * @cfg {String} fieldClass @hide
45058      */
45059     /**
45060      * @cfg {String} focusClass @hide
45061      */
45062     /**
45063      * @cfg {String} autoCreate @hide
45064      */
45065     /**
45066      * @cfg {String} inputType @hide
45067      */
45068     /**
45069      * @cfg {String} invalidClass @hide
45070      */
45071     /**
45072      * @cfg {String} invalidText @hide
45073      */
45074     /**
45075      * @cfg {String} msgFx @hide
45076      */
45077     /**
45078      * @cfg {String} validateOnBlur @hide
45079      */
45080 });
45081
45082 Roo.HtmlEditorCore.white = [
45083         'area', 'br', 'img', 'input', 'hr', 'wbr',
45084         
45085        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45086        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45087        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45088        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45089        'table',   'ul',         'xmp', 
45090        
45091        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45092       'thead',   'tr', 
45093      
45094       'dir', 'menu', 'ol', 'ul', 'dl',
45095        
45096       'embed',  'object'
45097 ];
45098
45099
45100 Roo.HtmlEditorCore.black = [
45101     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45102         'applet', // 
45103         'base',   'basefont', 'bgsound', 'blink',  'body', 
45104         'frame',  'frameset', 'head',    'html',   'ilayer', 
45105         'iframe', 'layer',  'link',     'meta',    'object',   
45106         'script', 'style' ,'title',  'xml' // clean later..
45107 ];
45108 Roo.HtmlEditorCore.clean = [
45109     'script', 'style', 'title', 'xml'
45110 ];
45111 Roo.HtmlEditorCore.remove = [
45112     'font'
45113 ];
45114 // attributes..
45115
45116 Roo.HtmlEditorCore.ablack = [
45117     'on'
45118 ];
45119     
45120 Roo.HtmlEditorCore.aclean = [ 
45121     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45122 ];
45123
45124 // protocols..
45125 Roo.HtmlEditorCore.pwhite= [
45126         'http',  'https',  'mailto'
45127 ];
45128
45129 // white listed style attributes.
45130 Roo.HtmlEditorCore.cwhite= [
45131       //  'text-align', /// default is to allow most things..
45132       
45133          
45134 //        'font-size'//??
45135 ];
45136
45137 // black listed style attributes.
45138 Roo.HtmlEditorCore.cblack= [
45139       //  'font-size' -- this can be set by the project 
45140 ];
45141
45142
45143 Roo.HtmlEditorCore.swapCodes   =[ 
45144     [    8211, "--" ], 
45145     [    8212, "--" ], 
45146     [    8216,  "'" ],  
45147     [    8217, "'" ],  
45148     [    8220, '"' ],  
45149     [    8221, '"' ],  
45150     [    8226, "*" ],  
45151     [    8230, "..." ]
45152 ]; 
45153
45154     //<script type="text/javascript">
45155
45156 /*
45157  * Ext JS Library 1.1.1
45158  * Copyright(c) 2006-2007, Ext JS, LLC.
45159  * Licence LGPL
45160  * 
45161  */
45162  
45163  
45164 Roo.form.HtmlEditor = function(config){
45165     
45166     
45167     
45168     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45169     
45170     if (!this.toolbars) {
45171         this.toolbars = [];
45172     }
45173     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45174     
45175     
45176 };
45177
45178 /**
45179  * @class Roo.form.HtmlEditor
45180  * @extends Roo.form.Field
45181  * Provides a lightweight HTML Editor component.
45182  *
45183  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45184  * 
45185  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45186  * supported by this editor.</b><br/><br/>
45187  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45188  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45189  */
45190 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45191     /**
45192      * @cfg {Boolean} clearUp
45193      */
45194     clearUp : true,
45195       /**
45196      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45197      */
45198     toolbars : false,
45199    
45200      /**
45201      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45202      *                        Roo.resizable.
45203      */
45204     resizable : false,
45205      /**
45206      * @cfg {Number} height (in pixels)
45207      */   
45208     height: 300,
45209    /**
45210      * @cfg {Number} width (in pixels)
45211      */   
45212     width: 500,
45213     
45214     /**
45215      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45216      * 
45217      */
45218     stylesheets: false,
45219     
45220     
45221      /**
45222      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45223      * 
45224      */
45225     cblack: false,
45226     /**
45227      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45228      * 
45229      */
45230     cwhite: false,
45231     
45232      /**
45233      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45234      * 
45235      */
45236     black: false,
45237     /**
45238      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45239      * 
45240      */
45241     white: false,
45242     
45243     // id of frame..
45244     frameId: false,
45245     
45246     // private properties
45247     validationEvent : false,
45248     deferHeight: true,
45249     initialized : false,
45250     activated : false,
45251     
45252     onFocus : Roo.emptyFn,
45253     iframePad:3,
45254     hideMode:'offsets',
45255     
45256     actionMode : 'container', // defaults to hiding it...
45257     
45258     defaultAutoCreate : { // modified by initCompnoent..
45259         tag: "textarea",
45260         style:"width:500px;height:300px;",
45261         autocomplete: "new-password"
45262     },
45263
45264     // private
45265     initComponent : function(){
45266         this.addEvents({
45267             /**
45268              * @event initialize
45269              * Fires when the editor is fully initialized (including the iframe)
45270              * @param {HtmlEditor} this
45271              */
45272             initialize: true,
45273             /**
45274              * @event activate
45275              * Fires when the editor is first receives the focus. Any insertion must wait
45276              * until after this event.
45277              * @param {HtmlEditor} this
45278              */
45279             activate: true,
45280              /**
45281              * @event beforesync
45282              * Fires before the textarea is updated with content from the editor iframe. Return false
45283              * to cancel the sync.
45284              * @param {HtmlEditor} this
45285              * @param {String} html
45286              */
45287             beforesync: true,
45288              /**
45289              * @event beforepush
45290              * Fires before the iframe editor is updated with content from the textarea. Return false
45291              * to cancel the push.
45292              * @param {HtmlEditor} this
45293              * @param {String} html
45294              */
45295             beforepush: true,
45296              /**
45297              * @event sync
45298              * Fires when the textarea is updated with content from the editor iframe.
45299              * @param {HtmlEditor} this
45300              * @param {String} html
45301              */
45302             sync: true,
45303              /**
45304              * @event push
45305              * Fires when the iframe editor is updated with content from the textarea.
45306              * @param {HtmlEditor} this
45307              * @param {String} html
45308              */
45309             push: true,
45310              /**
45311              * @event editmodechange
45312              * Fires when the editor switches edit modes
45313              * @param {HtmlEditor} this
45314              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45315              */
45316             editmodechange: true,
45317             /**
45318              * @event editorevent
45319              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45320              * @param {HtmlEditor} this
45321              */
45322             editorevent: true,
45323             /**
45324              * @event firstfocus
45325              * Fires when on first focus - needed by toolbars..
45326              * @param {HtmlEditor} this
45327              */
45328             firstfocus: true,
45329             /**
45330              * @event autosave
45331              * Auto save the htmlEditor value as a file into Events
45332              * @param {HtmlEditor} this
45333              */
45334             autosave: true,
45335             /**
45336              * @event savedpreview
45337              * preview the saved version of htmlEditor
45338              * @param {HtmlEditor} this
45339              */
45340             savedpreview: true,
45341             
45342             /**
45343             * @event stylesheetsclick
45344             * Fires when press the Sytlesheets button
45345             * @param {Roo.HtmlEditorCore} this
45346             */
45347             stylesheetsclick: true
45348         });
45349         this.defaultAutoCreate =  {
45350             tag: "textarea",
45351             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45352             autocomplete: "new-password"
45353         };
45354     },
45355
45356     /**
45357      * Protected method that will not generally be called directly. It
45358      * is called when the editor creates its toolbar. Override this method if you need to
45359      * add custom toolbar buttons.
45360      * @param {HtmlEditor} editor
45361      */
45362     createToolbar : function(editor){
45363         Roo.log("create toolbars");
45364         if (!editor.toolbars || !editor.toolbars.length) {
45365             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45366         }
45367         
45368         for (var i =0 ; i < editor.toolbars.length;i++) {
45369             editor.toolbars[i] = Roo.factory(
45370                     typeof(editor.toolbars[i]) == 'string' ?
45371                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45372                 Roo.form.HtmlEditor);
45373             editor.toolbars[i].init(editor);
45374         }
45375          
45376         
45377     },
45378
45379      
45380     // private
45381     onRender : function(ct, position)
45382     {
45383         var _t = this;
45384         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45385         
45386         this.wrap = this.el.wrap({
45387             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45388         });
45389         
45390         this.editorcore.onRender(ct, position);
45391          
45392         if (this.resizable) {
45393             this.resizeEl = new Roo.Resizable(this.wrap, {
45394                 pinned : true,
45395                 wrap: true,
45396                 dynamic : true,
45397                 minHeight : this.height,
45398                 height: this.height,
45399                 handles : this.resizable,
45400                 width: this.width,
45401                 listeners : {
45402                     resize : function(r, w, h) {
45403                         _t.onResize(w,h); // -something
45404                     }
45405                 }
45406             });
45407             
45408         }
45409         this.createToolbar(this);
45410        
45411         
45412         if(!this.width){
45413             this.setSize(this.wrap.getSize());
45414         }
45415         if (this.resizeEl) {
45416             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45417             // should trigger onReize..
45418         }
45419         
45420         this.keyNav = new Roo.KeyNav(this.el, {
45421             
45422             "tab" : function(e){
45423                 e.preventDefault();
45424                 
45425                 var value = this.getValue();
45426                 
45427                 var start = this.el.dom.selectionStart;
45428                 var end = this.el.dom.selectionEnd;
45429                 
45430                 if(!e.shiftKey){
45431                     
45432                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45433                     this.el.dom.setSelectionRange(end + 1, end + 1);
45434                     return;
45435                 }
45436                 
45437                 var f = value.substring(0, start).split("\t");
45438                 
45439                 if(f.pop().length != 0){
45440                     return;
45441                 }
45442                 
45443                 this.setValue(f.join("\t") + value.substring(end));
45444                 this.el.dom.setSelectionRange(start - 1, start - 1);
45445                 
45446             },
45447             
45448             "home" : function(e){
45449                 e.preventDefault();
45450                 
45451                 var curr = this.el.dom.selectionStart;
45452                 var lines = this.getValue().split("\n");
45453                 
45454                 if(!lines.length){
45455                     return;
45456                 }
45457                 
45458                 if(e.ctrlKey){
45459                     this.el.dom.setSelectionRange(0, 0);
45460                     return;
45461                 }
45462                 
45463                 var pos = 0;
45464                 
45465                 for (var i = 0; i < lines.length;i++) {
45466                     pos += lines[i].length;
45467                     
45468                     if(i != 0){
45469                         pos += 1;
45470                     }
45471                     
45472                     if(pos < curr){
45473                         continue;
45474                     }
45475                     
45476                     pos -= lines[i].length;
45477                     
45478                     break;
45479                 }
45480                 
45481                 if(!e.shiftKey){
45482                     this.el.dom.setSelectionRange(pos, pos);
45483                     return;
45484                 }
45485                 
45486                 this.el.dom.selectionStart = pos;
45487                 this.el.dom.selectionEnd = curr;
45488             },
45489             
45490             "end" : function(e){
45491                 e.preventDefault();
45492                 
45493                 var curr = this.el.dom.selectionStart;
45494                 var lines = this.getValue().split("\n");
45495                 
45496                 if(!lines.length){
45497                     return;
45498                 }
45499                 
45500                 if(e.ctrlKey){
45501                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45502                     return;
45503                 }
45504                 
45505                 var pos = 0;
45506                 
45507                 for (var i = 0; i < lines.length;i++) {
45508                     
45509                     pos += lines[i].length;
45510                     
45511                     if(i != 0){
45512                         pos += 1;
45513                     }
45514                     
45515                     if(pos < curr){
45516                         continue;
45517                     }
45518                     
45519                     break;
45520                 }
45521                 
45522                 if(!e.shiftKey){
45523                     this.el.dom.setSelectionRange(pos, pos);
45524                     return;
45525                 }
45526                 
45527                 this.el.dom.selectionStart = curr;
45528                 this.el.dom.selectionEnd = pos;
45529             },
45530
45531             scope : this,
45532
45533             doRelay : function(foo, bar, hname){
45534                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45535             },
45536
45537             forceKeyDown: true
45538         });
45539         
45540 //        if(this.autosave && this.w){
45541 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45542 //        }
45543     },
45544
45545     // private
45546     onResize : function(w, h)
45547     {
45548         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45549         var ew = false;
45550         var eh = false;
45551         
45552         if(this.el ){
45553             if(typeof w == 'number'){
45554                 var aw = w - this.wrap.getFrameWidth('lr');
45555                 this.el.setWidth(this.adjustWidth('textarea', aw));
45556                 ew = aw;
45557             }
45558             if(typeof h == 'number'){
45559                 var tbh = 0;
45560                 for (var i =0; i < this.toolbars.length;i++) {
45561                     // fixme - ask toolbars for heights?
45562                     tbh += this.toolbars[i].tb.el.getHeight();
45563                     if (this.toolbars[i].footer) {
45564                         tbh += this.toolbars[i].footer.el.getHeight();
45565                     }
45566                 }
45567                 
45568                 
45569                 
45570                 
45571                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45572                 ah -= 5; // knock a few pixes off for look..
45573 //                Roo.log(ah);
45574                 this.el.setHeight(this.adjustWidth('textarea', ah));
45575                 var eh = ah;
45576             }
45577         }
45578         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45579         this.editorcore.onResize(ew,eh);
45580         
45581     },
45582
45583     /**
45584      * Toggles the editor between standard and source edit mode.
45585      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45586      */
45587     toggleSourceEdit : function(sourceEditMode)
45588     {
45589         this.editorcore.toggleSourceEdit(sourceEditMode);
45590         
45591         if(this.editorcore.sourceEditMode){
45592             Roo.log('editor - showing textarea');
45593             
45594 //            Roo.log('in');
45595 //            Roo.log(this.syncValue());
45596             this.editorcore.syncValue();
45597             this.el.removeClass('x-hidden');
45598             this.el.dom.removeAttribute('tabIndex');
45599             this.el.focus();
45600             
45601             for (var i = 0; i < this.toolbars.length; i++) {
45602                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45603                     this.toolbars[i].tb.hide();
45604                     this.toolbars[i].footer.hide();
45605                 }
45606             }
45607             
45608         }else{
45609             Roo.log('editor - hiding textarea');
45610 //            Roo.log('out')
45611 //            Roo.log(this.pushValue()); 
45612             this.editorcore.pushValue();
45613             
45614             this.el.addClass('x-hidden');
45615             this.el.dom.setAttribute('tabIndex', -1);
45616             
45617             for (var i = 0; i < this.toolbars.length; i++) {
45618                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45619                     this.toolbars[i].tb.show();
45620                     this.toolbars[i].footer.show();
45621                 }
45622             }
45623             
45624             //this.deferFocus();
45625         }
45626         
45627         this.setSize(this.wrap.getSize());
45628         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45629         
45630         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45631     },
45632  
45633     // private (for BoxComponent)
45634     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45635
45636     // private (for BoxComponent)
45637     getResizeEl : function(){
45638         return this.wrap;
45639     },
45640
45641     // private (for BoxComponent)
45642     getPositionEl : function(){
45643         return this.wrap;
45644     },
45645
45646     // private
45647     initEvents : function(){
45648         this.originalValue = this.getValue();
45649     },
45650
45651     /**
45652      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45653      * @method
45654      */
45655     markInvalid : Roo.emptyFn,
45656     /**
45657      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45658      * @method
45659      */
45660     clearInvalid : Roo.emptyFn,
45661
45662     setValue : function(v){
45663         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45664         this.editorcore.pushValue();
45665     },
45666
45667      
45668     // private
45669     deferFocus : function(){
45670         this.focus.defer(10, this);
45671     },
45672
45673     // doc'ed in Field
45674     focus : function(){
45675         this.editorcore.focus();
45676         
45677     },
45678       
45679
45680     // private
45681     onDestroy : function(){
45682         
45683         
45684         
45685         if(this.rendered){
45686             
45687             for (var i =0; i < this.toolbars.length;i++) {
45688                 // fixme - ask toolbars for heights?
45689                 this.toolbars[i].onDestroy();
45690             }
45691             
45692             this.wrap.dom.innerHTML = '';
45693             this.wrap.remove();
45694         }
45695     },
45696
45697     // private
45698     onFirstFocus : function(){
45699         //Roo.log("onFirstFocus");
45700         this.editorcore.onFirstFocus();
45701          for (var i =0; i < this.toolbars.length;i++) {
45702             this.toolbars[i].onFirstFocus();
45703         }
45704         
45705     },
45706     
45707     // private
45708     syncValue : function()
45709     {
45710         this.editorcore.syncValue();
45711     },
45712     
45713     pushValue : function()
45714     {
45715         this.editorcore.pushValue();
45716     },
45717     
45718     setStylesheets : function(stylesheets)
45719     {
45720         this.editorcore.setStylesheets(stylesheets);
45721     },
45722     
45723     removeStylesheets : function()
45724     {
45725         this.editorcore.removeStylesheets();
45726     }
45727      
45728     
45729     // hide stuff that is not compatible
45730     /**
45731      * @event blur
45732      * @hide
45733      */
45734     /**
45735      * @event change
45736      * @hide
45737      */
45738     /**
45739      * @event focus
45740      * @hide
45741      */
45742     /**
45743      * @event specialkey
45744      * @hide
45745      */
45746     /**
45747      * @cfg {String} fieldClass @hide
45748      */
45749     /**
45750      * @cfg {String} focusClass @hide
45751      */
45752     /**
45753      * @cfg {String} autoCreate @hide
45754      */
45755     /**
45756      * @cfg {String} inputType @hide
45757      */
45758     /**
45759      * @cfg {String} invalidClass @hide
45760      */
45761     /**
45762      * @cfg {String} invalidText @hide
45763      */
45764     /**
45765      * @cfg {String} msgFx @hide
45766      */
45767     /**
45768      * @cfg {String} validateOnBlur @hide
45769      */
45770 });
45771  
45772     // <script type="text/javascript">
45773 /*
45774  * Based on
45775  * Ext JS Library 1.1.1
45776  * Copyright(c) 2006-2007, Ext JS, LLC.
45777  *  
45778  
45779  */
45780
45781 /**
45782  * @class Roo.form.HtmlEditorToolbar1
45783  * Basic Toolbar
45784  * 
45785  * Usage:
45786  *
45787  new Roo.form.HtmlEditor({
45788     ....
45789     toolbars : [
45790         new Roo.form.HtmlEditorToolbar1({
45791             disable : { fonts: 1 , format: 1, ..., ... , ...],
45792             btns : [ .... ]
45793         })
45794     }
45795      
45796  * 
45797  * @cfg {Object} disable List of elements to disable..
45798  * @cfg {Array} btns List of additional buttons.
45799  * 
45800  * 
45801  * NEEDS Extra CSS? 
45802  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45803  */
45804  
45805 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45806 {
45807     
45808     Roo.apply(this, config);
45809     
45810     // default disabled, based on 'good practice'..
45811     this.disable = this.disable || {};
45812     Roo.applyIf(this.disable, {
45813         fontSize : true,
45814         colors : true,
45815         specialElements : true
45816     });
45817     
45818     
45819     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45820     // dont call parent... till later.
45821 }
45822
45823 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45824     
45825     tb: false,
45826     
45827     rendered: false,
45828     
45829     editor : false,
45830     editorcore : false,
45831     /**
45832      * @cfg {Object} disable  List of toolbar elements to disable
45833          
45834      */
45835     disable : false,
45836     
45837     
45838      /**
45839      * @cfg {String} createLinkText The default text for the create link prompt
45840      */
45841     createLinkText : 'Please enter the URL for the link:',
45842     /**
45843      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45844      */
45845     defaultLinkValue : 'http:/'+'/',
45846    
45847     
45848       /**
45849      * @cfg {Array} fontFamilies An array of available font families
45850      */
45851     fontFamilies : [
45852         'Arial',
45853         'Courier New',
45854         'Tahoma',
45855         'Times New Roman',
45856         'Verdana'
45857     ],
45858     
45859     specialChars : [
45860            "&#169;",
45861           "&#174;",     
45862           "&#8482;",    
45863           "&#163;" ,    
45864          // "&#8212;",    
45865           "&#8230;",    
45866           "&#247;" ,    
45867         //  "&#225;" ,     ?? a acute?
45868            "&#8364;"    , //Euro
45869        //   "&#8220;"    ,
45870         //  "&#8221;"    ,
45871         //  "&#8226;"    ,
45872           "&#176;"  //   , // degrees
45873
45874          // "&#233;"     , // e ecute
45875          // "&#250;"     , // u ecute?
45876     ],
45877     
45878     specialElements : [
45879         {
45880             text: "Insert Table",
45881             xtype: 'MenuItem',
45882             xns : Roo.Menu,
45883             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45884                 
45885         },
45886         {    
45887             text: "Insert Image",
45888             xtype: 'MenuItem',
45889             xns : Roo.Menu,
45890             ihtml : '<img src="about:blank"/>'
45891             
45892         }
45893         
45894          
45895     ],
45896     
45897     
45898     inputElements : [ 
45899             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45900             "input:submit", "input:button", "select", "textarea", "label" ],
45901     formats : [
45902         ["p"] ,  
45903         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45904         ["pre"],[ "code"], 
45905         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45906         ['div'],['span'],
45907         ['sup'],['sub']
45908     ],
45909     
45910     cleanStyles : [
45911         "font-size"
45912     ],
45913      /**
45914      * @cfg {String} defaultFont default font to use.
45915      */
45916     defaultFont: 'tahoma',
45917    
45918     fontSelect : false,
45919     
45920     
45921     formatCombo : false,
45922     
45923     init : function(editor)
45924     {
45925         this.editor = editor;
45926         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45927         var editorcore = this.editorcore;
45928         
45929         var _t = this;
45930         
45931         var fid = editorcore.frameId;
45932         var etb = this;
45933         function btn(id, toggle, handler){
45934             var xid = fid + '-'+ id ;
45935             return {
45936                 id : xid,
45937                 cmd : id,
45938                 cls : 'x-btn-icon x-edit-'+id,
45939                 enableToggle:toggle !== false,
45940                 scope: _t, // was editor...
45941                 handler:handler||_t.relayBtnCmd,
45942                 clickEvent:'mousedown',
45943                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45944                 tabIndex:-1
45945             };
45946         }
45947         
45948         
45949         
45950         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45951         this.tb = tb;
45952          // stop form submits
45953         tb.el.on('click', function(e){
45954             e.preventDefault(); // what does this do?
45955         });
45956
45957         if(!this.disable.font) { // && !Roo.isSafari){
45958             /* why no safari for fonts 
45959             editor.fontSelect = tb.el.createChild({
45960                 tag:'select',
45961                 tabIndex: -1,
45962                 cls:'x-font-select',
45963                 html: this.createFontOptions()
45964             });
45965             
45966             editor.fontSelect.on('change', function(){
45967                 var font = editor.fontSelect.dom.value;
45968                 editor.relayCmd('fontname', font);
45969                 editor.deferFocus();
45970             }, editor);
45971             
45972             tb.add(
45973                 editor.fontSelect.dom,
45974                 '-'
45975             );
45976             */
45977             
45978         };
45979         if(!this.disable.formats){
45980             this.formatCombo = new Roo.form.ComboBox({
45981                 store: new Roo.data.SimpleStore({
45982                     id : 'tag',
45983                     fields: ['tag'],
45984                     data : this.formats // from states.js
45985                 }),
45986                 blockFocus : true,
45987                 name : '',
45988                 //autoCreate : {tag: "div",  size: "20"},
45989                 displayField:'tag',
45990                 typeAhead: false,
45991                 mode: 'local',
45992                 editable : false,
45993                 triggerAction: 'all',
45994                 emptyText:'Add tag',
45995                 selectOnFocus:true,
45996                 width:135,
45997                 listeners : {
45998                     'select': function(c, r, i) {
45999                         editorcore.insertTag(r.get('tag'));
46000                         editor.focus();
46001                     }
46002                 }
46003
46004             });
46005             tb.addField(this.formatCombo);
46006             
46007         }
46008         
46009         if(!this.disable.format){
46010             tb.add(
46011                 btn('bold'),
46012                 btn('italic'),
46013                 btn('underline'),
46014                 btn('strikethrough')
46015             );
46016         };
46017         if(!this.disable.fontSize){
46018             tb.add(
46019                 '-',
46020                 
46021                 
46022                 btn('increasefontsize', false, editorcore.adjustFont),
46023                 btn('decreasefontsize', false, editorcore.adjustFont)
46024             );
46025         };
46026         
46027         
46028         if(!this.disable.colors){
46029             tb.add(
46030                 '-', {
46031                     id:editorcore.frameId +'-forecolor',
46032                     cls:'x-btn-icon x-edit-forecolor',
46033                     clickEvent:'mousedown',
46034                     tooltip: this.buttonTips['forecolor'] || undefined,
46035                     tabIndex:-1,
46036                     menu : new Roo.menu.ColorMenu({
46037                         allowReselect: true,
46038                         focus: Roo.emptyFn,
46039                         value:'000000',
46040                         plain:true,
46041                         selectHandler: function(cp, color){
46042                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46043                             editor.deferFocus();
46044                         },
46045                         scope: editorcore,
46046                         clickEvent:'mousedown'
46047                     })
46048                 }, {
46049                     id:editorcore.frameId +'backcolor',
46050                     cls:'x-btn-icon x-edit-backcolor',
46051                     clickEvent:'mousedown',
46052                     tooltip: this.buttonTips['backcolor'] || undefined,
46053                     tabIndex:-1,
46054                     menu : new Roo.menu.ColorMenu({
46055                         focus: Roo.emptyFn,
46056                         value:'FFFFFF',
46057                         plain:true,
46058                         allowReselect: true,
46059                         selectHandler: function(cp, color){
46060                             if(Roo.isGecko){
46061                                 editorcore.execCmd('useCSS', false);
46062                                 editorcore.execCmd('hilitecolor', color);
46063                                 editorcore.execCmd('useCSS', true);
46064                                 editor.deferFocus();
46065                             }else{
46066                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46067                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46068                                 editor.deferFocus();
46069                             }
46070                         },
46071                         scope:editorcore,
46072                         clickEvent:'mousedown'
46073                     })
46074                 }
46075             );
46076         };
46077         // now add all the items...
46078         
46079
46080         if(!this.disable.alignments){
46081             tb.add(
46082                 '-',
46083                 btn('justifyleft'),
46084                 btn('justifycenter'),
46085                 btn('justifyright')
46086             );
46087         };
46088
46089         //if(!Roo.isSafari){
46090             if(!this.disable.links){
46091                 tb.add(
46092                     '-',
46093                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46094                 );
46095             };
46096
46097             if(!this.disable.lists){
46098                 tb.add(
46099                     '-',
46100                     btn('insertorderedlist'),
46101                     btn('insertunorderedlist')
46102                 );
46103             }
46104             if(!this.disable.sourceEdit){
46105                 tb.add(
46106                     '-',
46107                     btn('sourceedit', true, function(btn){
46108                         this.toggleSourceEdit(btn.pressed);
46109                     })
46110                 );
46111             }
46112         //}
46113         
46114         var smenu = { };
46115         // special menu.. - needs to be tidied up..
46116         if (!this.disable.special) {
46117             smenu = {
46118                 text: "&#169;",
46119                 cls: 'x-edit-none',
46120                 
46121                 menu : {
46122                     items : []
46123                 }
46124             };
46125             for (var i =0; i < this.specialChars.length; i++) {
46126                 smenu.menu.items.push({
46127                     
46128                     html: this.specialChars[i],
46129                     handler: function(a,b) {
46130                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46131                         //editor.insertAtCursor(a.html);
46132                         
46133                     },
46134                     tabIndex:-1
46135                 });
46136             }
46137             
46138             
46139             tb.add(smenu);
46140             
46141             
46142         }
46143         
46144         var cmenu = { };
46145         if (!this.disable.cleanStyles) {
46146             cmenu = {
46147                 cls: 'x-btn-icon x-btn-clear',
46148                 
46149                 menu : {
46150                     items : []
46151                 }
46152             };
46153             for (var i =0; i < this.cleanStyles.length; i++) {
46154                 cmenu.menu.items.push({
46155                     actiontype : this.cleanStyles[i],
46156                     html: 'Remove ' + this.cleanStyles[i],
46157                     handler: function(a,b) {
46158 //                        Roo.log(a);
46159 //                        Roo.log(b);
46160                         var c = Roo.get(editorcore.doc.body);
46161                         c.select('[style]').each(function(s) {
46162                             s.dom.style.removeProperty(a.actiontype);
46163                         });
46164                         editorcore.syncValue();
46165                     },
46166                     tabIndex:-1
46167                 });
46168             }
46169              cmenu.menu.items.push({
46170                 actiontype : 'tablewidths',
46171                 html: 'Remove Table Widths',
46172                 handler: function(a,b) {
46173                     editorcore.cleanTableWidths();
46174                     editorcore.syncValue();
46175                 },
46176                 tabIndex:-1
46177             });
46178             cmenu.menu.items.push({
46179                 actiontype : 'word',
46180                 html: 'Remove MS Word Formating',
46181                 handler: function(a,b) {
46182                     editorcore.cleanWord();
46183                     editorcore.syncValue();
46184                 },
46185                 tabIndex:-1
46186             });
46187             
46188             cmenu.menu.items.push({
46189                 actiontype : 'all',
46190                 html: 'Remove All Styles',
46191                 handler: function(a,b) {
46192                     
46193                     var c = Roo.get(editorcore.doc.body);
46194                     c.select('[style]').each(function(s) {
46195                         s.dom.removeAttribute('style');
46196                     });
46197                     editorcore.syncValue();
46198                 },
46199                 tabIndex:-1
46200             });
46201             
46202             cmenu.menu.items.push({
46203                 actiontype : 'all',
46204                 html: 'Remove All CSS Classes',
46205                 handler: function(a,b) {
46206                     
46207                     var c = Roo.get(editorcore.doc.body);
46208                     c.select('[class]').each(function(s) {
46209                         s.dom.removeAttribute('class');
46210                     });
46211                     editorcore.cleanWord();
46212                     editorcore.syncValue();
46213                 },
46214                 tabIndex:-1
46215             });
46216             
46217              cmenu.menu.items.push({
46218                 actiontype : 'tidy',
46219                 html: 'Tidy HTML Source',
46220                 handler: function(a,b) {
46221                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46222                     editorcore.syncValue();
46223                 },
46224                 tabIndex:-1
46225             });
46226             
46227             
46228             tb.add(cmenu);
46229         }
46230          
46231         if (!this.disable.specialElements) {
46232             var semenu = {
46233                 text: "Other;",
46234                 cls: 'x-edit-none',
46235                 menu : {
46236                     items : []
46237                 }
46238             };
46239             for (var i =0; i < this.specialElements.length; i++) {
46240                 semenu.menu.items.push(
46241                     Roo.apply({ 
46242                         handler: function(a,b) {
46243                             editor.insertAtCursor(this.ihtml);
46244                         }
46245                     }, this.specialElements[i])
46246                 );
46247                     
46248             }
46249             
46250             tb.add(semenu);
46251             
46252             
46253         }
46254          
46255         
46256         if (this.btns) {
46257             for(var i =0; i< this.btns.length;i++) {
46258                 var b = Roo.factory(this.btns[i],Roo.form);
46259                 b.cls =  'x-edit-none';
46260                 
46261                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46262                     b.cls += ' x-init-enable';
46263                 }
46264                 
46265                 b.scope = editorcore;
46266                 tb.add(b);
46267             }
46268         
46269         }
46270         
46271         
46272         
46273         // disable everything...
46274         
46275         this.tb.items.each(function(item){
46276             
46277            if(
46278                 item.id != editorcore.frameId+ '-sourceedit' && 
46279                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46280             ){
46281                 
46282                 item.disable();
46283             }
46284         });
46285         this.rendered = true;
46286         
46287         // the all the btns;
46288         editor.on('editorevent', this.updateToolbar, this);
46289         // other toolbars need to implement this..
46290         //editor.on('editmodechange', this.updateToolbar, this);
46291     },
46292     
46293     
46294     relayBtnCmd : function(btn) {
46295         this.editorcore.relayCmd(btn.cmd);
46296     },
46297     // private used internally
46298     createLink : function(){
46299         Roo.log("create link?");
46300         var url = prompt(this.createLinkText, this.defaultLinkValue);
46301         if(url && url != 'http:/'+'/'){
46302             this.editorcore.relayCmd('createlink', url);
46303         }
46304     },
46305
46306     
46307     /**
46308      * Protected method that will not generally be called directly. It triggers
46309      * a toolbar update by reading the markup state of the current selection in the editor.
46310      */
46311     updateToolbar: function(){
46312
46313         if(!this.editorcore.activated){
46314             this.editor.onFirstFocus();
46315             return;
46316         }
46317
46318         var btns = this.tb.items.map, 
46319             doc = this.editorcore.doc,
46320             frameId = this.editorcore.frameId;
46321
46322         if(!this.disable.font && !Roo.isSafari){
46323             /*
46324             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46325             if(name != this.fontSelect.dom.value){
46326                 this.fontSelect.dom.value = name;
46327             }
46328             */
46329         }
46330         if(!this.disable.format){
46331             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46332             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46333             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46334             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46335         }
46336         if(!this.disable.alignments){
46337             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46338             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46339             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46340         }
46341         if(!Roo.isSafari && !this.disable.lists){
46342             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46343             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46344         }
46345         
46346         var ans = this.editorcore.getAllAncestors();
46347         if (this.formatCombo) {
46348             
46349             
46350             var store = this.formatCombo.store;
46351             this.formatCombo.setValue("");
46352             for (var i =0; i < ans.length;i++) {
46353                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46354                     // select it..
46355                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46356                     break;
46357                 }
46358             }
46359         }
46360         
46361         
46362         
46363         // hides menus... - so this cant be on a menu...
46364         Roo.menu.MenuMgr.hideAll();
46365
46366         //this.editorsyncValue();
46367     },
46368    
46369     
46370     createFontOptions : function(){
46371         var buf = [], fs = this.fontFamilies, ff, lc;
46372         
46373         
46374         
46375         for(var i = 0, len = fs.length; i< len; i++){
46376             ff = fs[i];
46377             lc = ff.toLowerCase();
46378             buf.push(
46379                 '<option value="',lc,'" style="font-family:',ff,';"',
46380                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46381                     ff,
46382                 '</option>'
46383             );
46384         }
46385         return buf.join('');
46386     },
46387     
46388     toggleSourceEdit : function(sourceEditMode){
46389         
46390         Roo.log("toolbar toogle");
46391         if(sourceEditMode === undefined){
46392             sourceEditMode = !this.sourceEditMode;
46393         }
46394         this.sourceEditMode = sourceEditMode === true;
46395         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46396         // just toggle the button?
46397         if(btn.pressed !== this.sourceEditMode){
46398             btn.toggle(this.sourceEditMode);
46399             return;
46400         }
46401         
46402         if(sourceEditMode){
46403             Roo.log("disabling buttons");
46404             this.tb.items.each(function(item){
46405                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46406                     item.disable();
46407                 }
46408             });
46409           
46410         }else{
46411             Roo.log("enabling buttons");
46412             if(this.editorcore.initialized){
46413                 this.tb.items.each(function(item){
46414                     item.enable();
46415                 });
46416             }
46417             
46418         }
46419         Roo.log("calling toggole on editor");
46420         // tell the editor that it's been pressed..
46421         this.editor.toggleSourceEdit(sourceEditMode);
46422        
46423     },
46424      /**
46425      * Object collection of toolbar tooltips for the buttons in the editor. The key
46426      * is the command id associated with that button and the value is a valid QuickTips object.
46427      * For example:
46428 <pre><code>
46429 {
46430     bold : {
46431         title: 'Bold (Ctrl+B)',
46432         text: 'Make the selected text bold.',
46433         cls: 'x-html-editor-tip'
46434     },
46435     italic : {
46436         title: 'Italic (Ctrl+I)',
46437         text: 'Make the selected text italic.',
46438         cls: 'x-html-editor-tip'
46439     },
46440     ...
46441 </code></pre>
46442     * @type Object
46443      */
46444     buttonTips : {
46445         bold : {
46446             title: 'Bold (Ctrl+B)',
46447             text: 'Make the selected text bold.',
46448             cls: 'x-html-editor-tip'
46449         },
46450         italic : {
46451             title: 'Italic (Ctrl+I)',
46452             text: 'Make the selected text italic.',
46453             cls: 'x-html-editor-tip'
46454         },
46455         underline : {
46456             title: 'Underline (Ctrl+U)',
46457             text: 'Underline the selected text.',
46458             cls: 'x-html-editor-tip'
46459         },
46460         strikethrough : {
46461             title: 'Strikethrough',
46462             text: 'Strikethrough the selected text.',
46463             cls: 'x-html-editor-tip'
46464         },
46465         increasefontsize : {
46466             title: 'Grow Text',
46467             text: 'Increase the font size.',
46468             cls: 'x-html-editor-tip'
46469         },
46470         decreasefontsize : {
46471             title: 'Shrink Text',
46472             text: 'Decrease the font size.',
46473             cls: 'x-html-editor-tip'
46474         },
46475         backcolor : {
46476             title: 'Text Highlight Color',
46477             text: 'Change the background color of the selected text.',
46478             cls: 'x-html-editor-tip'
46479         },
46480         forecolor : {
46481             title: 'Font Color',
46482             text: 'Change the color of the selected text.',
46483             cls: 'x-html-editor-tip'
46484         },
46485         justifyleft : {
46486             title: 'Align Text Left',
46487             text: 'Align text to the left.',
46488             cls: 'x-html-editor-tip'
46489         },
46490         justifycenter : {
46491             title: 'Center Text',
46492             text: 'Center text in the editor.',
46493             cls: 'x-html-editor-tip'
46494         },
46495         justifyright : {
46496             title: 'Align Text Right',
46497             text: 'Align text to the right.',
46498             cls: 'x-html-editor-tip'
46499         },
46500         insertunorderedlist : {
46501             title: 'Bullet List',
46502             text: 'Start a bulleted list.',
46503             cls: 'x-html-editor-tip'
46504         },
46505         insertorderedlist : {
46506             title: 'Numbered List',
46507             text: 'Start a numbered list.',
46508             cls: 'x-html-editor-tip'
46509         },
46510         createlink : {
46511             title: 'Hyperlink',
46512             text: 'Make the selected text a hyperlink.',
46513             cls: 'x-html-editor-tip'
46514         },
46515         sourceedit : {
46516             title: 'Source Edit',
46517             text: 'Switch to source editing mode.',
46518             cls: 'x-html-editor-tip'
46519         }
46520     },
46521     // private
46522     onDestroy : function(){
46523         if(this.rendered){
46524             
46525             this.tb.items.each(function(item){
46526                 if(item.menu){
46527                     item.menu.removeAll();
46528                     if(item.menu.el){
46529                         item.menu.el.destroy();
46530                     }
46531                 }
46532                 item.destroy();
46533             });
46534              
46535         }
46536     },
46537     onFirstFocus: function() {
46538         this.tb.items.each(function(item){
46539            item.enable();
46540         });
46541     }
46542 });
46543
46544
46545
46546
46547 // <script type="text/javascript">
46548 /*
46549  * Based on
46550  * Ext JS Library 1.1.1
46551  * Copyright(c) 2006-2007, Ext JS, LLC.
46552  *  
46553  
46554  */
46555
46556  
46557 /**
46558  * @class Roo.form.HtmlEditor.ToolbarContext
46559  * Context Toolbar
46560  * 
46561  * Usage:
46562  *
46563  new Roo.form.HtmlEditor({
46564     ....
46565     toolbars : [
46566         { xtype: 'ToolbarStandard', styles : {} }
46567         { xtype: 'ToolbarContext', disable : {} }
46568     ]
46569 })
46570
46571      
46572  * 
46573  * @config : {Object} disable List of elements to disable.. (not done yet.)
46574  * @config : {Object} styles  Map of styles available.
46575  * 
46576  */
46577
46578 Roo.form.HtmlEditor.ToolbarContext = function(config)
46579 {
46580     
46581     Roo.apply(this, config);
46582     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46583     // dont call parent... till later.
46584     this.styles = this.styles || {};
46585 }
46586
46587  
46588
46589 Roo.form.HtmlEditor.ToolbarContext.types = {
46590     'IMG' : {
46591         width : {
46592             title: "Width",
46593             width: 40
46594         },
46595         height:  {
46596             title: "Height",
46597             width: 40
46598         },
46599         align: {
46600             title: "Align",
46601             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46602             width : 80
46603             
46604         },
46605         border: {
46606             title: "Border",
46607             width: 40
46608         },
46609         alt: {
46610             title: "Alt",
46611             width: 120
46612         },
46613         src : {
46614             title: "Src",
46615             width: 220
46616         }
46617         
46618     },
46619     'A' : {
46620         name : {
46621             title: "Name",
46622             width: 50
46623         },
46624         target:  {
46625             title: "Target",
46626             width: 120
46627         },
46628         href:  {
46629             title: "Href",
46630             width: 220
46631         } // border?
46632         
46633     },
46634     'TABLE' : {
46635         rows : {
46636             title: "Rows",
46637             width: 20
46638         },
46639         cols : {
46640             title: "Cols",
46641             width: 20
46642         },
46643         width : {
46644             title: "Width",
46645             width: 40
46646         },
46647         height : {
46648             title: "Height",
46649             width: 40
46650         },
46651         border : {
46652             title: "Border",
46653             width: 20
46654         }
46655     },
46656     'TD' : {
46657         width : {
46658             title: "Width",
46659             width: 40
46660         },
46661         height : {
46662             title: "Height",
46663             width: 40
46664         },   
46665         align: {
46666             title: "Align",
46667             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46668             width: 80
46669         },
46670         valign: {
46671             title: "Valign",
46672             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46673             width: 80
46674         },
46675         colspan: {
46676             title: "Colspan",
46677             width: 20
46678             
46679         },
46680          'font-family'  : {
46681             title : "Font",
46682             style : 'fontFamily',
46683             displayField: 'display',
46684             optname : 'font-family',
46685             width: 140
46686         }
46687     },
46688     'INPUT' : {
46689         name : {
46690             title: "name",
46691             width: 120
46692         },
46693         value : {
46694             title: "Value",
46695             width: 120
46696         },
46697         width : {
46698             title: "Width",
46699             width: 40
46700         }
46701     },
46702     'LABEL' : {
46703         'for' : {
46704             title: "For",
46705             width: 120
46706         }
46707     },
46708     'TEXTAREA' : {
46709           name : {
46710             title: "name",
46711             width: 120
46712         },
46713         rows : {
46714             title: "Rows",
46715             width: 20
46716         },
46717         cols : {
46718             title: "Cols",
46719             width: 20
46720         }
46721     },
46722     'SELECT' : {
46723         name : {
46724             title: "name",
46725             width: 120
46726         },
46727         selectoptions : {
46728             title: "Options",
46729             width: 200
46730         }
46731     },
46732     
46733     // should we really allow this??
46734     // should this just be 
46735     'BODY' : {
46736         title : {
46737             title: "Title",
46738             width: 200,
46739             disabled : true
46740         }
46741     },
46742     'SPAN' : {
46743         'font-family'  : {
46744             title : "Font",
46745             style : 'fontFamily',
46746             displayField: 'display',
46747             optname : 'font-family',
46748             width: 140
46749         }
46750     },
46751     'DIV' : {
46752         'font-family'  : {
46753             title : "Font",
46754             style : 'fontFamily',
46755             displayField: 'display',
46756             optname : 'font-family',
46757             width: 140
46758         }
46759     },
46760      'P' : {
46761         'font-family'  : {
46762             title : "Font",
46763             style : 'fontFamily',
46764             displayField: 'display',
46765             optname : 'font-family',
46766             width: 140
46767         }
46768     },
46769     
46770     '*' : {
46771         // empty..
46772     }
46773
46774 };
46775
46776 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46777 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46778
46779 Roo.form.HtmlEditor.ToolbarContext.options = {
46780         'font-family'  : [ 
46781                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46782                 [ 'Courier New', 'Courier New'],
46783                 [ 'Tahoma', 'Tahoma'],
46784                 [ 'Times New Roman,serif', 'Times'],
46785                 [ 'Verdana','Verdana' ]
46786         ]
46787 };
46788
46789 // fixme - these need to be configurable..
46790  
46791
46792 //Roo.form.HtmlEditor.ToolbarContext.types
46793
46794
46795 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46796     
46797     tb: false,
46798     
46799     rendered: false,
46800     
46801     editor : false,
46802     editorcore : false,
46803     /**
46804      * @cfg {Object} disable  List of toolbar elements to disable
46805          
46806      */
46807     disable : false,
46808     /**
46809      * @cfg {Object} styles List of styles 
46810      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46811      *
46812      * These must be defined in the page, so they get rendered correctly..
46813      * .headline { }
46814      * TD.underline { }
46815      * 
46816      */
46817     styles : false,
46818     
46819     options: false,
46820     
46821     toolbars : false,
46822     
46823     init : function(editor)
46824     {
46825         this.editor = editor;
46826         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46827         var editorcore = this.editorcore;
46828         
46829         var fid = editorcore.frameId;
46830         var etb = this;
46831         function btn(id, toggle, handler){
46832             var xid = fid + '-'+ id ;
46833             return {
46834                 id : xid,
46835                 cmd : id,
46836                 cls : 'x-btn-icon x-edit-'+id,
46837                 enableToggle:toggle !== false,
46838                 scope: editorcore, // was editor...
46839                 handler:handler||editorcore.relayBtnCmd,
46840                 clickEvent:'mousedown',
46841                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46842                 tabIndex:-1
46843             };
46844         }
46845         // create a new element.
46846         var wdiv = editor.wrap.createChild({
46847                 tag: 'div'
46848             }, editor.wrap.dom.firstChild.nextSibling, true);
46849         
46850         // can we do this more than once??
46851         
46852          // stop form submits
46853       
46854  
46855         // disable everything...
46856         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46857         this.toolbars = {};
46858            
46859         for (var i in  ty) {
46860           
46861             this.toolbars[i] = this.buildToolbar(ty[i],i);
46862         }
46863         this.tb = this.toolbars.BODY;
46864         this.tb.el.show();
46865         this.buildFooter();
46866         this.footer.show();
46867         editor.on('hide', function( ) { this.footer.hide() }, this);
46868         editor.on('show', function( ) { this.footer.show() }, this);
46869         
46870          
46871         this.rendered = true;
46872         
46873         // the all the btns;
46874         editor.on('editorevent', this.updateToolbar, this);
46875         // other toolbars need to implement this..
46876         //editor.on('editmodechange', this.updateToolbar, this);
46877     },
46878     
46879     
46880     
46881     /**
46882      * Protected method that will not generally be called directly. It triggers
46883      * a toolbar update by reading the markup state of the current selection in the editor.
46884      *
46885      * Note you can force an update by calling on('editorevent', scope, false)
46886      */
46887     updateToolbar: function(editor,ev,sel){
46888
46889         //Roo.log(ev);
46890         // capture mouse up - this is handy for selecting images..
46891         // perhaps should go somewhere else...
46892         if(!this.editorcore.activated){
46893              this.editor.onFirstFocus();
46894             return;
46895         }
46896         
46897         
46898         
46899         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46900         // selectNode - might want to handle IE?
46901         if (ev &&
46902             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46903             ev.target && ev.target.tagName == 'IMG') {
46904             // they have click on an image...
46905             // let's see if we can change the selection...
46906             sel = ev.target;
46907          
46908               var nodeRange = sel.ownerDocument.createRange();
46909             try {
46910                 nodeRange.selectNode(sel);
46911             } catch (e) {
46912                 nodeRange.selectNodeContents(sel);
46913             }
46914             //nodeRange.collapse(true);
46915             var s = this.editorcore.win.getSelection();
46916             s.removeAllRanges();
46917             s.addRange(nodeRange);
46918         }  
46919         
46920       
46921         var updateFooter = sel ? false : true;
46922         
46923         
46924         var ans = this.editorcore.getAllAncestors();
46925         
46926         // pick
46927         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46928         
46929         if (!sel) { 
46930             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46931             sel = sel ? sel : this.editorcore.doc.body;
46932             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46933             
46934         }
46935         // pick a menu that exists..
46936         var tn = sel.tagName.toUpperCase();
46937         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46938         
46939         tn = sel.tagName.toUpperCase();
46940         
46941         var lastSel = this.tb.selectedNode;
46942         
46943         this.tb.selectedNode = sel;
46944         
46945         // if current menu does not match..
46946         
46947         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46948                 
46949             this.tb.el.hide();
46950             ///console.log("show: " + tn);
46951             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46952             this.tb.el.show();
46953             // update name
46954             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46955             
46956             
46957             // update attributes
46958             if (this.tb.fields) {
46959                 this.tb.fields.each(function(e) {
46960                     if (e.stylename) {
46961                         e.setValue(sel.style[e.stylename]);
46962                         return;
46963                     } 
46964                    e.setValue(sel.getAttribute(e.attrname));
46965                 });
46966             }
46967             
46968             var hasStyles = false;
46969             for(var i in this.styles) {
46970                 hasStyles = true;
46971                 break;
46972             }
46973             
46974             // update styles
46975             if (hasStyles) { 
46976                 var st = this.tb.fields.item(0);
46977                 
46978                 st.store.removeAll();
46979                
46980                 
46981                 var cn = sel.className.split(/\s+/);
46982                 
46983                 var avs = [];
46984                 if (this.styles['*']) {
46985                     
46986                     Roo.each(this.styles['*'], function(v) {
46987                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46988                     });
46989                 }
46990                 if (this.styles[tn]) { 
46991                     Roo.each(this.styles[tn], function(v) {
46992                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46993                     });
46994                 }
46995                 
46996                 st.store.loadData(avs);
46997                 st.collapse();
46998                 st.setValue(cn);
46999             }
47000             // flag our selected Node.
47001             this.tb.selectedNode = sel;
47002            
47003            
47004             Roo.menu.MenuMgr.hideAll();
47005
47006         }
47007         
47008         if (!updateFooter) {
47009             //this.footDisp.dom.innerHTML = ''; 
47010             return;
47011         }
47012         // update the footer
47013         //
47014         var html = '';
47015         
47016         this.footerEls = ans.reverse();
47017         Roo.each(this.footerEls, function(a,i) {
47018             if (!a) { return; }
47019             html += html.length ? ' &gt; '  :  '';
47020             
47021             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47022             
47023         });
47024        
47025         // 
47026         var sz = this.footDisp.up('td').getSize();
47027         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47028         this.footDisp.dom.style.marginLeft = '5px';
47029         
47030         this.footDisp.dom.style.overflow = 'hidden';
47031         
47032         this.footDisp.dom.innerHTML = html;
47033             
47034         //this.editorsyncValue();
47035     },
47036      
47037     
47038    
47039        
47040     // private
47041     onDestroy : function(){
47042         if(this.rendered){
47043             
47044             this.tb.items.each(function(item){
47045                 if(item.menu){
47046                     item.menu.removeAll();
47047                     if(item.menu.el){
47048                         item.menu.el.destroy();
47049                     }
47050                 }
47051                 item.destroy();
47052             });
47053              
47054         }
47055     },
47056     onFirstFocus: function() {
47057         // need to do this for all the toolbars..
47058         this.tb.items.each(function(item){
47059            item.enable();
47060         });
47061     },
47062     buildToolbar: function(tlist, nm)
47063     {
47064         var editor = this.editor;
47065         var editorcore = this.editorcore;
47066          // create a new element.
47067         var wdiv = editor.wrap.createChild({
47068                 tag: 'div'
47069             }, editor.wrap.dom.firstChild.nextSibling, true);
47070         
47071        
47072         var tb = new Roo.Toolbar(wdiv);
47073         // add the name..
47074         
47075         tb.add(nm+ ":&nbsp;");
47076         
47077         var styles = [];
47078         for(var i in this.styles) {
47079             styles.push(i);
47080         }
47081         
47082         // styles...
47083         if (styles && styles.length) {
47084             
47085             // this needs a multi-select checkbox...
47086             tb.addField( new Roo.form.ComboBox({
47087                 store: new Roo.data.SimpleStore({
47088                     id : 'val',
47089                     fields: ['val', 'selected'],
47090                     data : [] 
47091                 }),
47092                 name : '-roo-edit-className',
47093                 attrname : 'className',
47094                 displayField: 'val',
47095                 typeAhead: false,
47096                 mode: 'local',
47097                 editable : false,
47098                 triggerAction: 'all',
47099                 emptyText:'Select Style',
47100                 selectOnFocus:true,
47101                 width: 130,
47102                 listeners : {
47103                     'select': function(c, r, i) {
47104                         // initial support only for on class per el..
47105                         tb.selectedNode.className =  r ? r.get('val') : '';
47106                         editorcore.syncValue();
47107                     }
47108                 }
47109     
47110             }));
47111         }
47112         
47113         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47114         var tbops = tbc.options;
47115         
47116         for (var i in tlist) {
47117             
47118             var item = tlist[i];
47119             tb.add(item.title + ":&nbsp;");
47120             
47121             
47122             //optname == used so you can configure the options available..
47123             var opts = item.opts ? item.opts : false;
47124             if (item.optname) {
47125                 opts = tbops[item.optname];
47126            
47127             }
47128             
47129             if (opts) {
47130                 // opts == pulldown..
47131                 tb.addField( new Roo.form.ComboBox({
47132                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47133                         id : 'val',
47134                         fields: ['val', 'display'],
47135                         data : opts  
47136                     }),
47137                     name : '-roo-edit-' + i,
47138                     attrname : i,
47139                     stylename : item.style ? item.style : false,
47140                     displayField: item.displayField ? item.displayField : 'val',
47141                     valueField :  'val',
47142                     typeAhead: false,
47143                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47144                     editable : false,
47145                     triggerAction: 'all',
47146                     emptyText:'Select',
47147                     selectOnFocus:true,
47148                     width: item.width ? item.width  : 130,
47149                     listeners : {
47150                         'select': function(c, r, i) {
47151                             if (c.stylename) {
47152                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47153                                 return;
47154                             }
47155                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47156                         }
47157                     }
47158
47159                 }));
47160                 continue;
47161                     
47162                  
47163                 
47164                 tb.addField( new Roo.form.TextField({
47165                     name: i,
47166                     width: 100,
47167                     //allowBlank:false,
47168                     value: ''
47169                 }));
47170                 continue;
47171             }
47172             tb.addField( new Roo.form.TextField({
47173                 name: '-roo-edit-' + i,
47174                 attrname : i,
47175                 
47176                 width: item.width,
47177                 //allowBlank:true,
47178                 value: '',
47179                 listeners: {
47180                     'change' : function(f, nv, ov) {
47181                         tb.selectedNode.setAttribute(f.attrname, nv);
47182                         editorcore.syncValue();
47183                     }
47184                 }
47185             }));
47186              
47187         }
47188         
47189         var _this = this;
47190         
47191         if(nm == 'BODY'){
47192             tb.addSeparator();
47193         
47194             tb.addButton( {
47195                 text: 'Stylesheets',
47196
47197                 listeners : {
47198                     click : function ()
47199                     {
47200                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47201                     }
47202                 }
47203             });
47204         }
47205         
47206         tb.addFill();
47207         tb.addButton( {
47208             text: 'Remove Tag',
47209     
47210             listeners : {
47211                 click : function ()
47212                 {
47213                     // remove
47214                     // undo does not work.
47215                      
47216                     var sn = tb.selectedNode;
47217                     
47218                     var pn = sn.parentNode;
47219                     
47220                     var stn =  sn.childNodes[0];
47221                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47222                     while (sn.childNodes.length) {
47223                         var node = sn.childNodes[0];
47224                         sn.removeChild(node);
47225                         //Roo.log(node);
47226                         pn.insertBefore(node, sn);
47227                         
47228                     }
47229                     pn.removeChild(sn);
47230                     var range = editorcore.createRange();
47231         
47232                     range.setStart(stn,0);
47233                     range.setEnd(en,0); //????
47234                     //range.selectNode(sel);
47235                     
47236                     
47237                     var selection = editorcore.getSelection();
47238                     selection.removeAllRanges();
47239                     selection.addRange(range);
47240                     
47241                     
47242                     
47243                     //_this.updateToolbar(null, null, pn);
47244                     _this.updateToolbar(null, null, null);
47245                     _this.footDisp.dom.innerHTML = ''; 
47246                 }
47247             }
47248             
47249                     
47250                 
47251             
47252         });
47253         
47254         
47255         tb.el.on('click', function(e){
47256             e.preventDefault(); // what does this do?
47257         });
47258         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47259         tb.el.hide();
47260         tb.name = nm;
47261         // dont need to disable them... as they will get hidden
47262         return tb;
47263          
47264         
47265     },
47266     buildFooter : function()
47267     {
47268         
47269         var fel = this.editor.wrap.createChild();
47270         this.footer = new Roo.Toolbar(fel);
47271         // toolbar has scrolly on left / right?
47272         var footDisp= new Roo.Toolbar.Fill();
47273         var _t = this;
47274         this.footer.add(
47275             {
47276                 text : '&lt;',
47277                 xtype: 'Button',
47278                 handler : function() {
47279                     _t.footDisp.scrollTo('left',0,true)
47280                 }
47281             }
47282         );
47283         this.footer.add( footDisp );
47284         this.footer.add( 
47285             {
47286                 text : '&gt;',
47287                 xtype: 'Button',
47288                 handler : function() {
47289                     // no animation..
47290                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47291                 }
47292             }
47293         );
47294         var fel = Roo.get(footDisp.el);
47295         fel.addClass('x-editor-context');
47296         this.footDispWrap = fel; 
47297         this.footDispWrap.overflow  = 'hidden';
47298         
47299         this.footDisp = fel.createChild();
47300         this.footDispWrap.on('click', this.onContextClick, this)
47301         
47302         
47303     },
47304     onContextClick : function (ev,dom)
47305     {
47306         ev.preventDefault();
47307         var  cn = dom.className;
47308         //Roo.log(cn);
47309         if (!cn.match(/x-ed-loc-/)) {
47310             return;
47311         }
47312         var n = cn.split('-').pop();
47313         var ans = this.footerEls;
47314         var sel = ans[n];
47315         
47316          // pick
47317         var range = this.editorcore.createRange();
47318         
47319         range.selectNodeContents(sel);
47320         //range.selectNode(sel);
47321         
47322         
47323         var selection = this.editorcore.getSelection();
47324         selection.removeAllRanges();
47325         selection.addRange(range);
47326         
47327         
47328         
47329         this.updateToolbar(null, null, sel);
47330         
47331         
47332     }
47333     
47334     
47335     
47336     
47337     
47338 });
47339
47340
47341
47342
47343
47344 /*
47345  * Based on:
47346  * Ext JS Library 1.1.1
47347  * Copyright(c) 2006-2007, Ext JS, LLC.
47348  *
47349  * Originally Released Under LGPL - original licence link has changed is not relivant.
47350  *
47351  * Fork - LGPL
47352  * <script type="text/javascript">
47353  */
47354  
47355 /**
47356  * @class Roo.form.BasicForm
47357  * @extends Roo.util.Observable
47358  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47359  * @constructor
47360  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47361  * @param {Object} config Configuration options
47362  */
47363 Roo.form.BasicForm = function(el, config){
47364     this.allItems = [];
47365     this.childForms = [];
47366     Roo.apply(this, config);
47367     /*
47368      * The Roo.form.Field items in this form.
47369      * @type MixedCollection
47370      */
47371      
47372      
47373     this.items = new Roo.util.MixedCollection(false, function(o){
47374         return o.id || (o.id = Roo.id());
47375     });
47376     this.addEvents({
47377         /**
47378          * @event beforeaction
47379          * Fires before any action is performed. Return false to cancel the action.
47380          * @param {Form} this
47381          * @param {Action} action The action to be performed
47382          */
47383         beforeaction: true,
47384         /**
47385          * @event actionfailed
47386          * Fires when an action fails.
47387          * @param {Form} this
47388          * @param {Action} action The action that failed
47389          */
47390         actionfailed : true,
47391         /**
47392          * @event actioncomplete
47393          * Fires when an action is completed.
47394          * @param {Form} this
47395          * @param {Action} action The action that completed
47396          */
47397         actioncomplete : true
47398     });
47399     if(el){
47400         this.initEl(el);
47401     }
47402     Roo.form.BasicForm.superclass.constructor.call(this);
47403     
47404     Roo.form.BasicForm.popover.apply();
47405 };
47406
47407 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47408     /**
47409      * @cfg {String} method
47410      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47411      */
47412     /**
47413      * @cfg {DataReader} reader
47414      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47415      * This is optional as there is built-in support for processing JSON.
47416      */
47417     /**
47418      * @cfg {DataReader} errorReader
47419      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47420      * This is completely optional as there is built-in support for processing JSON.
47421      */
47422     /**
47423      * @cfg {String} url
47424      * The URL to use for form actions if one isn't supplied in the action options.
47425      */
47426     /**
47427      * @cfg {Boolean} fileUpload
47428      * Set to true if this form is a file upload.
47429      */
47430      
47431     /**
47432      * @cfg {Object} baseParams
47433      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47434      */
47435      /**
47436      
47437     /**
47438      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47439      */
47440     timeout: 30,
47441
47442     // private
47443     activeAction : null,
47444
47445     /**
47446      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47447      * or setValues() data instead of when the form was first created.
47448      */
47449     trackResetOnLoad : false,
47450     
47451     
47452     /**
47453      * childForms - used for multi-tab forms
47454      * @type {Array}
47455      */
47456     childForms : false,
47457     
47458     /**
47459      * allItems - full list of fields.
47460      * @type {Array}
47461      */
47462     allItems : false,
47463     
47464     /**
47465      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47466      * element by passing it or its id or mask the form itself by passing in true.
47467      * @type Mixed
47468      */
47469     waitMsgTarget : false,
47470     
47471     /**
47472      * @type Boolean
47473      */
47474     disableMask : false,
47475     
47476     /**
47477      * @cfg {Boolean} errorMask (true|false) default false
47478      */
47479     errorMask : false,
47480     
47481     /**
47482      * @cfg {Number} maskOffset Default 100
47483      */
47484     maskOffset : 100,
47485
47486     // private
47487     initEl : function(el){
47488         this.el = Roo.get(el);
47489         this.id = this.el.id || Roo.id();
47490         this.el.on('submit', this.onSubmit, this);
47491         this.el.addClass('x-form');
47492     },
47493
47494     // private
47495     onSubmit : function(e){
47496         e.stopEvent();
47497     },
47498
47499     /**
47500      * Returns true if client-side validation on the form is successful.
47501      * @return Boolean
47502      */
47503     isValid : function(){
47504         var valid = true;
47505         var target = false;
47506         this.items.each(function(f){
47507             if(f.validate()){
47508                 return;
47509             }
47510             
47511             valid = false;
47512                 
47513             if(!target && f.el.isVisible(true)){
47514                 target = f;
47515             }
47516         });
47517         
47518         if(this.errorMask && !valid){
47519             Roo.form.BasicForm.popover.mask(this, target);
47520         }
47521         
47522         return valid;
47523     },
47524
47525     /**
47526      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47527      * @return Boolean
47528      */
47529     isDirty : function(){
47530         var dirty = false;
47531         this.items.each(function(f){
47532            if(f.isDirty()){
47533                dirty = true;
47534                return false;
47535            }
47536         });
47537         return dirty;
47538     },
47539     
47540     /**
47541      * Returns true if any fields in this form have changed since their original load. (New version)
47542      * @return Boolean
47543      */
47544     
47545     hasChanged : function()
47546     {
47547         var dirty = false;
47548         this.items.each(function(f){
47549            if(f.hasChanged()){
47550                dirty = true;
47551                return false;
47552            }
47553         });
47554         return dirty;
47555         
47556     },
47557     /**
47558      * Resets all hasChanged to 'false' -
47559      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47560      * So hasChanged storage is only to be used for this purpose
47561      * @return Boolean
47562      */
47563     resetHasChanged : function()
47564     {
47565         this.items.each(function(f){
47566            f.resetHasChanged();
47567         });
47568         
47569     },
47570     
47571     
47572     /**
47573      * Performs a predefined action (submit or load) or custom actions you define on this form.
47574      * @param {String} actionName The name of the action type
47575      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47576      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47577      * accept other config options):
47578      * <pre>
47579 Property          Type             Description
47580 ----------------  ---------------  ----------------------------------------------------------------------------------
47581 url               String           The url for the action (defaults to the form's url)
47582 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47583 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47584 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47585                                    validate the form on the client (defaults to false)
47586      * </pre>
47587      * @return {BasicForm} this
47588      */
47589     doAction : function(action, options){
47590         if(typeof action == 'string'){
47591             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47592         }
47593         if(this.fireEvent('beforeaction', this, action) !== false){
47594             this.beforeAction(action);
47595             action.run.defer(100, action);
47596         }
47597         return this;
47598     },
47599
47600     /**
47601      * Shortcut to do a submit action.
47602      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47603      * @return {BasicForm} this
47604      */
47605     submit : function(options){
47606         this.doAction('submit', options);
47607         return this;
47608     },
47609
47610     /**
47611      * Shortcut to do a load action.
47612      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47613      * @return {BasicForm} this
47614      */
47615     load : function(options){
47616         this.doAction('load', options);
47617         return this;
47618     },
47619
47620     /**
47621      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47622      * @param {Record} record The record to edit
47623      * @return {BasicForm} this
47624      */
47625     updateRecord : function(record){
47626         record.beginEdit();
47627         var fs = record.fields;
47628         fs.each(function(f){
47629             var field = this.findField(f.name);
47630             if(field){
47631                 record.set(f.name, field.getValue());
47632             }
47633         }, this);
47634         record.endEdit();
47635         return this;
47636     },
47637
47638     /**
47639      * Loads an Roo.data.Record into this form.
47640      * @param {Record} record The record to load
47641      * @return {BasicForm} this
47642      */
47643     loadRecord : function(record){
47644         this.setValues(record.data);
47645         return this;
47646     },
47647
47648     // private
47649     beforeAction : function(action){
47650         var o = action.options;
47651         
47652         if(!this.disableMask) {
47653             if(this.waitMsgTarget === true){
47654                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47655             }else if(this.waitMsgTarget){
47656                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47657                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47658             }else {
47659                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47660             }
47661         }
47662         
47663          
47664     },
47665
47666     // private
47667     afterAction : function(action, success){
47668         this.activeAction = null;
47669         var o = action.options;
47670         
47671         if(!this.disableMask) {
47672             if(this.waitMsgTarget === true){
47673                 this.el.unmask();
47674             }else if(this.waitMsgTarget){
47675                 this.waitMsgTarget.unmask();
47676             }else{
47677                 Roo.MessageBox.updateProgress(1);
47678                 Roo.MessageBox.hide();
47679             }
47680         }
47681         
47682         if(success){
47683             if(o.reset){
47684                 this.reset();
47685             }
47686             Roo.callback(o.success, o.scope, [this, action]);
47687             this.fireEvent('actioncomplete', this, action);
47688             
47689         }else{
47690             
47691             // failure condition..
47692             // we have a scenario where updates need confirming.
47693             // eg. if a locking scenario exists..
47694             // we look for { errors : { needs_confirm : true }} in the response.
47695             if (
47696                 (typeof(action.result) != 'undefined')  &&
47697                 (typeof(action.result.errors) != 'undefined')  &&
47698                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47699            ){
47700                 var _t = this;
47701                 Roo.MessageBox.confirm(
47702                     "Change requires confirmation",
47703                     action.result.errorMsg,
47704                     function(r) {
47705                         if (r != 'yes') {
47706                             return;
47707                         }
47708                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47709                     }
47710                     
47711                 );
47712                 
47713                 
47714                 
47715                 return;
47716             }
47717             
47718             Roo.callback(o.failure, o.scope, [this, action]);
47719             // show an error message if no failed handler is set..
47720             if (!this.hasListener('actionfailed')) {
47721                 Roo.MessageBox.alert("Error",
47722                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47723                         action.result.errorMsg :
47724                         "Saving Failed, please check your entries or try again"
47725                 );
47726             }
47727             
47728             this.fireEvent('actionfailed', this, action);
47729         }
47730         
47731     },
47732
47733     /**
47734      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47735      * @param {String} id The value to search for
47736      * @return Field
47737      */
47738     findField : function(id){
47739         var field = this.items.get(id);
47740         if(!field){
47741             this.items.each(function(f){
47742                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47743                     field = f;
47744                     return false;
47745                 }
47746             });
47747         }
47748         return field || null;
47749     },
47750
47751     /**
47752      * Add a secondary form to this one, 
47753      * Used to provide tabbed forms. One form is primary, with hidden values 
47754      * which mirror the elements from the other forms.
47755      * 
47756      * @param {Roo.form.Form} form to add.
47757      * 
47758      */
47759     addForm : function(form)
47760     {
47761        
47762         if (this.childForms.indexOf(form) > -1) {
47763             // already added..
47764             return;
47765         }
47766         this.childForms.push(form);
47767         var n = '';
47768         Roo.each(form.allItems, function (fe) {
47769             
47770             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47771             if (this.findField(n)) { // already added..
47772                 return;
47773             }
47774             var add = new Roo.form.Hidden({
47775                 name : n
47776             });
47777             add.render(this.el);
47778             
47779             this.add( add );
47780         }, this);
47781         
47782     },
47783     /**
47784      * Mark fields in this form invalid in bulk.
47785      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47786      * @return {BasicForm} this
47787      */
47788     markInvalid : function(errors){
47789         if(errors instanceof Array){
47790             for(var i = 0, len = errors.length; i < len; i++){
47791                 var fieldError = errors[i];
47792                 var f = this.findField(fieldError.id);
47793                 if(f){
47794                     f.markInvalid(fieldError.msg);
47795                 }
47796             }
47797         }else{
47798             var field, id;
47799             for(id in errors){
47800                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47801                     field.markInvalid(errors[id]);
47802                 }
47803             }
47804         }
47805         Roo.each(this.childForms || [], function (f) {
47806             f.markInvalid(errors);
47807         });
47808         
47809         return this;
47810     },
47811
47812     /**
47813      * Set values for fields in this form in bulk.
47814      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47815      * @return {BasicForm} this
47816      */
47817     setValues : function(values){
47818         if(values instanceof Array){ // array of objects
47819             for(var i = 0, len = values.length; i < len; i++){
47820                 var v = values[i];
47821                 var f = this.findField(v.id);
47822                 if(f){
47823                     f.setValue(v.value);
47824                     if(this.trackResetOnLoad){
47825                         f.originalValue = f.getValue();
47826                     }
47827                 }
47828             }
47829         }else{ // object hash
47830             var field, id;
47831             for(id in values){
47832                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47833                     
47834                     if (field.setFromData && 
47835                         field.valueField && 
47836                         field.displayField &&
47837                         // combos' with local stores can 
47838                         // be queried via setValue()
47839                         // to set their value..
47840                         (field.store && !field.store.isLocal)
47841                         ) {
47842                         // it's a combo
47843                         var sd = { };
47844                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47845                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47846                         field.setFromData(sd);
47847                         
47848                     } else {
47849                         field.setValue(values[id]);
47850                     }
47851                     
47852                     
47853                     if(this.trackResetOnLoad){
47854                         field.originalValue = field.getValue();
47855                     }
47856                 }
47857             }
47858         }
47859         this.resetHasChanged();
47860         
47861         
47862         Roo.each(this.childForms || [], function (f) {
47863             f.setValues(values);
47864             f.resetHasChanged();
47865         });
47866                 
47867         return this;
47868     },
47869  
47870     /**
47871      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47872      * they are returned as an array.
47873      * @param {Boolean} asString
47874      * @return {Object}
47875      */
47876     getValues : function(asString){
47877         if (this.childForms) {
47878             // copy values from the child forms
47879             Roo.each(this.childForms, function (f) {
47880                 this.setValues(f.getValues());
47881             }, this);
47882         }
47883         
47884         // use formdata
47885         if (typeof(FormData) != 'undefined' && asString !== true) {
47886             // this relies on a 'recent' version of chrome apparently...
47887             try {
47888                 var fd = (new FormData(this.el.dom)).entries();
47889                 var ret = {};
47890                 var ent = fd.next();
47891                 while (!ent.done) {
47892                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47893                     ent = fd.next();
47894                 };
47895                 return ret;
47896             } catch(e) {
47897                 
47898             }
47899             
47900         }
47901         
47902         
47903         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47904         if(asString === true){
47905             return fs;
47906         }
47907         return Roo.urlDecode(fs);
47908     },
47909     
47910     /**
47911      * Returns the fields in this form as an object with key/value pairs. 
47912      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47913      * @return {Object}
47914      */
47915     getFieldValues : function(with_hidden)
47916     {
47917         if (this.childForms) {
47918             // copy values from the child forms
47919             // should this call getFieldValues - probably not as we do not currently copy
47920             // hidden fields when we generate..
47921             Roo.each(this.childForms, function (f) {
47922                 this.setValues(f.getValues());
47923             }, this);
47924         }
47925         
47926         var ret = {};
47927         this.items.each(function(f){
47928             if (!f.getName()) {
47929                 return;
47930             }
47931             var v = f.getValue();
47932             if (f.inputType =='radio') {
47933                 if (typeof(ret[f.getName()]) == 'undefined') {
47934                     ret[f.getName()] = ''; // empty..
47935                 }
47936                 
47937                 if (!f.el.dom.checked) {
47938                     return;
47939                     
47940                 }
47941                 v = f.el.dom.value;
47942                 
47943             }
47944             
47945             // not sure if this supported any more..
47946             if ((typeof(v) == 'object') && f.getRawValue) {
47947                 v = f.getRawValue() ; // dates..
47948             }
47949             // combo boxes where name != hiddenName...
47950             if (f.name != f.getName()) {
47951                 ret[f.name] = f.getRawValue();
47952             }
47953             ret[f.getName()] = v;
47954         });
47955         
47956         return ret;
47957     },
47958
47959     /**
47960      * Clears all invalid messages in this form.
47961      * @return {BasicForm} this
47962      */
47963     clearInvalid : function(){
47964         this.items.each(function(f){
47965            f.clearInvalid();
47966         });
47967         
47968         Roo.each(this.childForms || [], function (f) {
47969             f.clearInvalid();
47970         });
47971         
47972         
47973         return this;
47974     },
47975
47976     /**
47977      * Resets this form.
47978      * @return {BasicForm} this
47979      */
47980     reset : function(){
47981         this.items.each(function(f){
47982             f.reset();
47983         });
47984         
47985         Roo.each(this.childForms || [], function (f) {
47986             f.reset();
47987         });
47988         this.resetHasChanged();
47989         
47990         return this;
47991     },
47992
47993     /**
47994      * Add Roo.form components to this form.
47995      * @param {Field} field1
47996      * @param {Field} field2 (optional)
47997      * @param {Field} etc (optional)
47998      * @return {BasicForm} this
47999      */
48000     add : function(){
48001         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48002         return this;
48003     },
48004
48005
48006     /**
48007      * Removes a field from the items collection (does NOT remove its markup).
48008      * @param {Field} field
48009      * @return {BasicForm} this
48010      */
48011     remove : function(field){
48012         this.items.remove(field);
48013         return this;
48014     },
48015
48016     /**
48017      * Looks at the fields in this form, checks them for an id attribute,
48018      * and calls applyTo on the existing dom element with that id.
48019      * @return {BasicForm} this
48020      */
48021     render : function(){
48022         this.items.each(function(f){
48023             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48024                 f.applyTo(f.id);
48025             }
48026         });
48027         return this;
48028     },
48029
48030     /**
48031      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48032      * @param {Object} values
48033      * @return {BasicForm} this
48034      */
48035     applyToFields : function(o){
48036         this.items.each(function(f){
48037            Roo.apply(f, o);
48038         });
48039         return this;
48040     },
48041
48042     /**
48043      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48044      * @param {Object} values
48045      * @return {BasicForm} this
48046      */
48047     applyIfToFields : function(o){
48048         this.items.each(function(f){
48049            Roo.applyIf(f, o);
48050         });
48051         return this;
48052     }
48053 });
48054
48055 // back compat
48056 Roo.BasicForm = Roo.form.BasicForm;
48057
48058 Roo.apply(Roo.form.BasicForm, {
48059     
48060     popover : {
48061         
48062         padding : 5,
48063         
48064         isApplied : false,
48065         
48066         isMasked : false,
48067         
48068         form : false,
48069         
48070         target : false,
48071         
48072         intervalID : false,
48073         
48074         maskEl : false,
48075         
48076         apply : function()
48077         {
48078             if(this.isApplied){
48079                 return;
48080             }
48081             
48082             this.maskEl = {
48083                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48084                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48085                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48086                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48087             };
48088             
48089             this.maskEl.top.enableDisplayMode("block");
48090             this.maskEl.left.enableDisplayMode("block");
48091             this.maskEl.bottom.enableDisplayMode("block");
48092             this.maskEl.right.enableDisplayMode("block");
48093             
48094             Roo.get(document.body).on('click', function(){
48095                 this.unmask();
48096             }, this);
48097             
48098             Roo.get(document.body).on('touchstart', function(){
48099                 this.unmask();
48100             }, this);
48101             
48102             this.isApplied = true
48103         },
48104         
48105         mask : function(form, target)
48106         {
48107             this.form = form;
48108             
48109             this.target = target;
48110             
48111             if(!this.form.errorMask || !target.el){
48112                 return;
48113             }
48114             
48115             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48116             
48117             var ot = this.target.el.calcOffsetsTo(scrollable);
48118             
48119             var scrollTo = ot[1] - this.form.maskOffset;
48120             
48121             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48122             
48123             scrollable.scrollTo('top', scrollTo);
48124             
48125             var el = this.target.wrap || this.target.el;
48126             
48127             var box = el.getBox();
48128             
48129             this.maskEl.top.setStyle('position', 'absolute');
48130             this.maskEl.top.setStyle('z-index', 10000);
48131             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48132             this.maskEl.top.setLeft(0);
48133             this.maskEl.top.setTop(0);
48134             this.maskEl.top.show();
48135             
48136             this.maskEl.left.setStyle('position', 'absolute');
48137             this.maskEl.left.setStyle('z-index', 10000);
48138             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48139             this.maskEl.left.setLeft(0);
48140             this.maskEl.left.setTop(box.y - this.padding);
48141             this.maskEl.left.show();
48142
48143             this.maskEl.bottom.setStyle('position', 'absolute');
48144             this.maskEl.bottom.setStyle('z-index', 10000);
48145             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48146             this.maskEl.bottom.setLeft(0);
48147             this.maskEl.bottom.setTop(box.bottom + this.padding);
48148             this.maskEl.bottom.show();
48149
48150             this.maskEl.right.setStyle('position', 'absolute');
48151             this.maskEl.right.setStyle('z-index', 10000);
48152             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48153             this.maskEl.right.setLeft(box.right + this.padding);
48154             this.maskEl.right.setTop(box.y - this.padding);
48155             this.maskEl.right.show();
48156
48157             this.intervalID = window.setInterval(function() {
48158                 Roo.form.BasicForm.popover.unmask();
48159             }, 10000);
48160
48161             window.onwheel = function(){ return false;};
48162             
48163             (function(){ this.isMasked = true; }).defer(500, this);
48164             
48165         },
48166         
48167         unmask : function()
48168         {
48169             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48170                 return;
48171             }
48172             
48173             this.maskEl.top.setStyle('position', 'absolute');
48174             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48175             this.maskEl.top.hide();
48176
48177             this.maskEl.left.setStyle('position', 'absolute');
48178             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48179             this.maskEl.left.hide();
48180
48181             this.maskEl.bottom.setStyle('position', 'absolute');
48182             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48183             this.maskEl.bottom.hide();
48184
48185             this.maskEl.right.setStyle('position', 'absolute');
48186             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48187             this.maskEl.right.hide();
48188             
48189             window.onwheel = function(){ return true;};
48190             
48191             if(this.intervalID){
48192                 window.clearInterval(this.intervalID);
48193                 this.intervalID = false;
48194             }
48195             
48196             this.isMasked = false;
48197             
48198         }
48199         
48200     }
48201     
48202 });/*
48203  * Based on:
48204  * Ext JS Library 1.1.1
48205  * Copyright(c) 2006-2007, Ext JS, LLC.
48206  *
48207  * Originally Released Under LGPL - original licence link has changed is not relivant.
48208  *
48209  * Fork - LGPL
48210  * <script type="text/javascript">
48211  */
48212
48213 /**
48214  * @class Roo.form.Form
48215  * @extends Roo.form.BasicForm
48216  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48217  * @constructor
48218  * @param {Object} config Configuration options
48219  */
48220 Roo.form.Form = function(config){
48221     var xitems =  [];
48222     if (config.items) {
48223         xitems = config.items;
48224         delete config.items;
48225     }
48226    
48227     
48228     Roo.form.Form.superclass.constructor.call(this, null, config);
48229     this.url = this.url || this.action;
48230     if(!this.root){
48231         this.root = new Roo.form.Layout(Roo.applyIf({
48232             id: Roo.id()
48233         }, config));
48234     }
48235     this.active = this.root;
48236     /**
48237      * Array of all the buttons that have been added to this form via {@link addButton}
48238      * @type Array
48239      */
48240     this.buttons = [];
48241     this.allItems = [];
48242     this.addEvents({
48243         /**
48244          * @event clientvalidation
48245          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48246          * @param {Form} this
48247          * @param {Boolean} valid true if the form has passed client-side validation
48248          */
48249         clientvalidation: true,
48250         /**
48251          * @event rendered
48252          * Fires when the form is rendered
48253          * @param {Roo.form.Form} form
48254          */
48255         rendered : true
48256     });
48257     
48258     if (this.progressUrl) {
48259             // push a hidden field onto the list of fields..
48260             this.addxtype( {
48261                     xns: Roo.form, 
48262                     xtype : 'Hidden', 
48263                     name : 'UPLOAD_IDENTIFIER' 
48264             });
48265         }
48266         
48267     
48268     Roo.each(xitems, this.addxtype, this);
48269     
48270 };
48271
48272 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48273     /**
48274      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48275      */
48276     /**
48277      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48278      */
48279     /**
48280      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48281      */
48282     buttonAlign:'center',
48283
48284     /**
48285      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48286      */
48287     minButtonWidth:75,
48288
48289     /**
48290      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48291      * This property cascades to child containers if not set.
48292      */
48293     labelAlign:'left',
48294
48295     /**
48296      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48297      * fires a looping event with that state. This is required to bind buttons to the valid
48298      * state using the config value formBind:true on the button.
48299      */
48300     monitorValid : false,
48301
48302     /**
48303      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48304      */
48305     monitorPoll : 200,
48306     
48307     /**
48308      * @cfg {String} progressUrl - Url to return progress data 
48309      */
48310     
48311     progressUrl : false,
48312     /**
48313      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48314      * sending a formdata with extra parameters - eg uploaded elements.
48315      */
48316     
48317     formData : false,
48318     
48319     /**
48320      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48321      * fields are added and the column is closed. If no fields are passed the column remains open
48322      * until end() is called.
48323      * @param {Object} config The config to pass to the column
48324      * @param {Field} field1 (optional)
48325      * @param {Field} field2 (optional)
48326      * @param {Field} etc (optional)
48327      * @return Column The column container object
48328      */
48329     column : function(c){
48330         var col = new Roo.form.Column(c);
48331         this.start(col);
48332         if(arguments.length > 1){ // duplicate code required because of Opera
48333             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48334             this.end();
48335         }
48336         return col;
48337     },
48338
48339     /**
48340      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48341      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48342      * until end() is called.
48343      * @param {Object} config The config to pass to the fieldset
48344      * @param {Field} field1 (optional)
48345      * @param {Field} field2 (optional)
48346      * @param {Field} etc (optional)
48347      * @return FieldSet The fieldset container object
48348      */
48349     fieldset : function(c){
48350         var fs = new Roo.form.FieldSet(c);
48351         this.start(fs);
48352         if(arguments.length > 1){ // duplicate code required because of Opera
48353             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48354             this.end();
48355         }
48356         return fs;
48357     },
48358
48359     /**
48360      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48361      * fields are added and the container is closed. If no fields are passed the container remains open
48362      * until end() is called.
48363      * @param {Object} config The config to pass to the Layout
48364      * @param {Field} field1 (optional)
48365      * @param {Field} field2 (optional)
48366      * @param {Field} etc (optional)
48367      * @return Layout The container object
48368      */
48369     container : function(c){
48370         var l = new Roo.form.Layout(c);
48371         this.start(l);
48372         if(arguments.length > 1){ // duplicate code required because of Opera
48373             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48374             this.end();
48375         }
48376         return l;
48377     },
48378
48379     /**
48380      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48381      * @param {Object} container A Roo.form.Layout or subclass of Layout
48382      * @return {Form} this
48383      */
48384     start : function(c){
48385         // cascade label info
48386         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48387         this.active.stack.push(c);
48388         c.ownerCt = this.active;
48389         this.active = c;
48390         return this;
48391     },
48392
48393     /**
48394      * Closes the current open container
48395      * @return {Form} this
48396      */
48397     end : function(){
48398         if(this.active == this.root){
48399             return this;
48400         }
48401         this.active = this.active.ownerCt;
48402         return this;
48403     },
48404
48405     /**
48406      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48407      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48408      * as the label of the field.
48409      * @param {Field} field1
48410      * @param {Field} field2 (optional)
48411      * @param {Field} etc. (optional)
48412      * @return {Form} this
48413      */
48414     add : function(){
48415         this.active.stack.push.apply(this.active.stack, arguments);
48416         this.allItems.push.apply(this.allItems,arguments);
48417         var r = [];
48418         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48419             if(a[i].isFormField){
48420                 r.push(a[i]);
48421             }
48422         }
48423         if(r.length > 0){
48424             Roo.form.Form.superclass.add.apply(this, r);
48425         }
48426         return this;
48427     },
48428     
48429
48430     
48431     
48432     
48433      /**
48434      * Find any element that has been added to a form, using it's ID or name
48435      * This can include framesets, columns etc. along with regular fields..
48436      * @param {String} id - id or name to find.
48437      
48438      * @return {Element} e - or false if nothing found.
48439      */
48440     findbyId : function(id)
48441     {
48442         var ret = false;
48443         if (!id) {
48444             return ret;
48445         }
48446         Roo.each(this.allItems, function(f){
48447             if (f.id == id || f.name == id ){
48448                 ret = f;
48449                 return false;
48450             }
48451         });
48452         return ret;
48453     },
48454
48455     
48456     
48457     /**
48458      * Render this form into the passed container. This should only be called once!
48459      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48460      * @return {Form} this
48461      */
48462     render : function(ct)
48463     {
48464         
48465         
48466         
48467         ct = Roo.get(ct);
48468         var o = this.autoCreate || {
48469             tag: 'form',
48470             method : this.method || 'POST',
48471             id : this.id || Roo.id()
48472         };
48473         this.initEl(ct.createChild(o));
48474
48475         this.root.render(this.el);
48476         
48477        
48478              
48479         this.items.each(function(f){
48480             f.render('x-form-el-'+f.id);
48481         });
48482
48483         if(this.buttons.length > 0){
48484             // tables are required to maintain order and for correct IE layout
48485             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48486                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48487                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48488             }}, null, true);
48489             var tr = tb.getElementsByTagName('tr')[0];
48490             for(var i = 0, len = this.buttons.length; i < len; i++) {
48491                 var b = this.buttons[i];
48492                 var td = document.createElement('td');
48493                 td.className = 'x-form-btn-td';
48494                 b.render(tr.appendChild(td));
48495             }
48496         }
48497         if(this.monitorValid){ // initialize after render
48498             this.startMonitoring();
48499         }
48500         this.fireEvent('rendered', this);
48501         return this;
48502     },
48503
48504     /**
48505      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48506      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48507      * object or a valid Roo.DomHelper element config
48508      * @param {Function} handler The function called when the button is clicked
48509      * @param {Object} scope (optional) The scope of the handler function
48510      * @return {Roo.Button}
48511      */
48512     addButton : function(config, handler, scope){
48513         var bc = {
48514             handler: handler,
48515             scope: scope,
48516             minWidth: this.minButtonWidth,
48517             hideParent:true
48518         };
48519         if(typeof config == "string"){
48520             bc.text = config;
48521         }else{
48522             Roo.apply(bc, config);
48523         }
48524         var btn = new Roo.Button(null, bc);
48525         this.buttons.push(btn);
48526         return btn;
48527     },
48528
48529      /**
48530      * Adds a series of form elements (using the xtype property as the factory method.
48531      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48532      * @param {Object} config 
48533      */
48534     
48535     addxtype : function()
48536     {
48537         var ar = Array.prototype.slice.call(arguments, 0);
48538         var ret = false;
48539         for(var i = 0; i < ar.length; i++) {
48540             if (!ar[i]) {
48541                 continue; // skip -- if this happends something invalid got sent, we 
48542                 // should ignore it, as basically that interface element will not show up
48543                 // and that should be pretty obvious!!
48544             }
48545             
48546             if (Roo.form[ar[i].xtype]) {
48547                 ar[i].form = this;
48548                 var fe = Roo.factory(ar[i], Roo.form);
48549                 if (!ret) {
48550                     ret = fe;
48551                 }
48552                 fe.form = this;
48553                 if (fe.store) {
48554                     fe.store.form = this;
48555                 }
48556                 if (fe.isLayout) {  
48557                          
48558                     this.start(fe);
48559                     this.allItems.push(fe);
48560                     if (fe.items && fe.addxtype) {
48561                         fe.addxtype.apply(fe, fe.items);
48562                         delete fe.items;
48563                     }
48564                      this.end();
48565                     continue;
48566                 }
48567                 
48568                 
48569                  
48570                 this.add(fe);
48571               //  console.log('adding ' + ar[i].xtype);
48572             }
48573             if (ar[i].xtype == 'Button') {  
48574                 //console.log('adding button');
48575                 //console.log(ar[i]);
48576                 this.addButton(ar[i]);
48577                 this.allItems.push(fe);
48578                 continue;
48579             }
48580             
48581             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48582                 alert('end is not supported on xtype any more, use items');
48583             //    this.end();
48584             //    //console.log('adding end');
48585             }
48586             
48587         }
48588         return ret;
48589     },
48590     
48591     /**
48592      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48593      * option "monitorValid"
48594      */
48595     startMonitoring : function(){
48596         if(!this.bound){
48597             this.bound = true;
48598             Roo.TaskMgr.start({
48599                 run : this.bindHandler,
48600                 interval : this.monitorPoll || 200,
48601                 scope: this
48602             });
48603         }
48604     },
48605
48606     /**
48607      * Stops monitoring of the valid state of this form
48608      */
48609     stopMonitoring : function(){
48610         this.bound = false;
48611     },
48612
48613     // private
48614     bindHandler : function(){
48615         if(!this.bound){
48616             return false; // stops binding
48617         }
48618         var valid = true;
48619         this.items.each(function(f){
48620             if(!f.isValid(true)){
48621                 valid = false;
48622                 return false;
48623             }
48624         });
48625         for(var i = 0, len = this.buttons.length; i < len; i++){
48626             var btn = this.buttons[i];
48627             if(btn.formBind === true && btn.disabled === valid){
48628                 btn.setDisabled(!valid);
48629             }
48630         }
48631         this.fireEvent('clientvalidation', this, valid);
48632     }
48633     
48634     
48635     
48636     
48637     
48638     
48639     
48640     
48641 });
48642
48643
48644 // back compat
48645 Roo.Form = Roo.form.Form;
48646 /*
48647  * Based on:
48648  * Ext JS Library 1.1.1
48649  * Copyright(c) 2006-2007, Ext JS, LLC.
48650  *
48651  * Originally Released Under LGPL - original licence link has changed is not relivant.
48652  *
48653  * Fork - LGPL
48654  * <script type="text/javascript">
48655  */
48656
48657 // as we use this in bootstrap.
48658 Roo.namespace('Roo.form');
48659  /**
48660  * @class Roo.form.Action
48661  * Internal Class used to handle form actions
48662  * @constructor
48663  * @param {Roo.form.BasicForm} el The form element or its id
48664  * @param {Object} config Configuration options
48665  */
48666
48667  
48668  
48669 // define the action interface
48670 Roo.form.Action = function(form, options){
48671     this.form = form;
48672     this.options = options || {};
48673 };
48674 /**
48675  * Client Validation Failed
48676  * @const 
48677  */
48678 Roo.form.Action.CLIENT_INVALID = 'client';
48679 /**
48680  * Server Validation Failed
48681  * @const 
48682  */
48683 Roo.form.Action.SERVER_INVALID = 'server';
48684  /**
48685  * Connect to Server Failed
48686  * @const 
48687  */
48688 Roo.form.Action.CONNECT_FAILURE = 'connect';
48689 /**
48690  * Reading Data from Server Failed
48691  * @const 
48692  */
48693 Roo.form.Action.LOAD_FAILURE = 'load';
48694
48695 Roo.form.Action.prototype = {
48696     type : 'default',
48697     failureType : undefined,
48698     response : undefined,
48699     result : undefined,
48700
48701     // interface method
48702     run : function(options){
48703
48704     },
48705
48706     // interface method
48707     success : function(response){
48708
48709     },
48710
48711     // interface method
48712     handleResponse : function(response){
48713
48714     },
48715
48716     // default connection failure
48717     failure : function(response){
48718         
48719         this.response = response;
48720         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48721         this.form.afterAction(this, false);
48722     },
48723
48724     processResponse : function(response){
48725         this.response = response;
48726         if(!response.responseText){
48727             return true;
48728         }
48729         this.result = this.handleResponse(response);
48730         return this.result;
48731     },
48732
48733     // utility functions used internally
48734     getUrl : function(appendParams){
48735         var url = this.options.url || this.form.url || this.form.el.dom.action;
48736         if(appendParams){
48737             var p = this.getParams();
48738             if(p){
48739                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48740             }
48741         }
48742         return url;
48743     },
48744
48745     getMethod : function(){
48746         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48747     },
48748
48749     getParams : function(){
48750         var bp = this.form.baseParams;
48751         var p = this.options.params;
48752         if(p){
48753             if(typeof p == "object"){
48754                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48755             }else if(typeof p == 'string' && bp){
48756                 p += '&' + Roo.urlEncode(bp);
48757             }
48758         }else if(bp){
48759             p = Roo.urlEncode(bp);
48760         }
48761         return p;
48762     },
48763
48764     createCallback : function(){
48765         return {
48766             success: this.success,
48767             failure: this.failure,
48768             scope: this,
48769             timeout: (this.form.timeout*1000),
48770             upload: this.form.fileUpload ? this.success : undefined
48771         };
48772     }
48773 };
48774
48775 Roo.form.Action.Submit = function(form, options){
48776     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48777 };
48778
48779 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48780     type : 'submit',
48781
48782     haveProgress : false,
48783     uploadComplete : false,
48784     
48785     // uploadProgress indicator.
48786     uploadProgress : function()
48787     {
48788         if (!this.form.progressUrl) {
48789             return;
48790         }
48791         
48792         if (!this.haveProgress) {
48793             Roo.MessageBox.progress("Uploading", "Uploading");
48794         }
48795         if (this.uploadComplete) {
48796            Roo.MessageBox.hide();
48797            return;
48798         }
48799         
48800         this.haveProgress = true;
48801    
48802         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48803         
48804         var c = new Roo.data.Connection();
48805         c.request({
48806             url : this.form.progressUrl,
48807             params: {
48808                 id : uid
48809             },
48810             method: 'GET',
48811             success : function(req){
48812                //console.log(data);
48813                 var rdata = false;
48814                 var edata;
48815                 try  {
48816                    rdata = Roo.decode(req.responseText)
48817                 } catch (e) {
48818                     Roo.log("Invalid data from server..");
48819                     Roo.log(edata);
48820                     return;
48821                 }
48822                 if (!rdata || !rdata.success) {
48823                     Roo.log(rdata);
48824                     Roo.MessageBox.alert(Roo.encode(rdata));
48825                     return;
48826                 }
48827                 var data = rdata.data;
48828                 
48829                 if (this.uploadComplete) {
48830                    Roo.MessageBox.hide();
48831                    return;
48832                 }
48833                    
48834                 if (data){
48835                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48836                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48837                     );
48838                 }
48839                 this.uploadProgress.defer(2000,this);
48840             },
48841        
48842             failure: function(data) {
48843                 Roo.log('progress url failed ');
48844                 Roo.log(data);
48845             },
48846             scope : this
48847         });
48848            
48849     },
48850     
48851     
48852     run : function()
48853     {
48854         // run get Values on the form, so it syncs any secondary forms.
48855         this.form.getValues();
48856         
48857         var o = this.options;
48858         var method = this.getMethod();
48859         var isPost = method == 'POST';
48860         if(o.clientValidation === false || this.form.isValid()){
48861             
48862             if (this.form.progressUrl) {
48863                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48864                     (new Date() * 1) + '' + Math.random());
48865                     
48866             } 
48867             
48868             
48869             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48870                 form:this.form.el.dom,
48871                 url:this.getUrl(!isPost),
48872                 method: method,
48873                 params:isPost ? this.getParams() : null,
48874                 isUpload: this.form.fileUpload,
48875                 formData : this.form.formData
48876             }));
48877             
48878             this.uploadProgress();
48879
48880         }else if (o.clientValidation !== false){ // client validation failed
48881             this.failureType = Roo.form.Action.CLIENT_INVALID;
48882             this.form.afterAction(this, false);
48883         }
48884     },
48885
48886     success : function(response)
48887     {
48888         this.uploadComplete= true;
48889         if (this.haveProgress) {
48890             Roo.MessageBox.hide();
48891         }
48892         
48893         
48894         var result = this.processResponse(response);
48895         if(result === true || result.success){
48896             this.form.afterAction(this, true);
48897             return;
48898         }
48899         if(result.errors){
48900             this.form.markInvalid(result.errors);
48901             this.failureType = Roo.form.Action.SERVER_INVALID;
48902         }
48903         this.form.afterAction(this, false);
48904     },
48905     failure : function(response)
48906     {
48907         this.uploadComplete= true;
48908         if (this.haveProgress) {
48909             Roo.MessageBox.hide();
48910         }
48911         
48912         this.response = response;
48913         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48914         this.form.afterAction(this, false);
48915     },
48916     
48917     handleResponse : function(response){
48918         if(this.form.errorReader){
48919             var rs = this.form.errorReader.read(response);
48920             var errors = [];
48921             if(rs.records){
48922                 for(var i = 0, len = rs.records.length; i < len; i++) {
48923                     var r = rs.records[i];
48924                     errors[i] = r.data;
48925                 }
48926             }
48927             if(errors.length < 1){
48928                 errors = null;
48929             }
48930             return {
48931                 success : rs.success,
48932                 errors : errors
48933             };
48934         }
48935         var ret = false;
48936         try {
48937             ret = Roo.decode(response.responseText);
48938         } catch (e) {
48939             ret = {
48940                 success: false,
48941                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48942                 errors : []
48943             };
48944         }
48945         return ret;
48946         
48947     }
48948 });
48949
48950
48951 Roo.form.Action.Load = function(form, options){
48952     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48953     this.reader = this.form.reader;
48954 };
48955
48956 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48957     type : 'load',
48958
48959     run : function(){
48960         
48961         Roo.Ajax.request(Roo.apply(
48962                 this.createCallback(), {
48963                     method:this.getMethod(),
48964                     url:this.getUrl(false),
48965                     params:this.getParams()
48966         }));
48967     },
48968
48969     success : function(response){
48970         
48971         var result = this.processResponse(response);
48972         if(result === true || !result.success || !result.data){
48973             this.failureType = Roo.form.Action.LOAD_FAILURE;
48974             this.form.afterAction(this, false);
48975             return;
48976         }
48977         this.form.clearInvalid();
48978         this.form.setValues(result.data);
48979         this.form.afterAction(this, true);
48980     },
48981
48982     handleResponse : function(response){
48983         if(this.form.reader){
48984             var rs = this.form.reader.read(response);
48985             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48986             return {
48987                 success : rs.success,
48988                 data : data
48989             };
48990         }
48991         return Roo.decode(response.responseText);
48992     }
48993 });
48994
48995 Roo.form.Action.ACTION_TYPES = {
48996     'load' : Roo.form.Action.Load,
48997     'submit' : Roo.form.Action.Submit
48998 };/*
48999  * Based on:
49000  * Ext JS Library 1.1.1
49001  * Copyright(c) 2006-2007, Ext JS, LLC.
49002  *
49003  * Originally Released Under LGPL - original licence link has changed is not relivant.
49004  *
49005  * Fork - LGPL
49006  * <script type="text/javascript">
49007  */
49008  
49009 /**
49010  * @class Roo.form.Layout
49011  * @extends Roo.Component
49012  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49013  * @constructor
49014  * @param {Object} config Configuration options
49015  */
49016 Roo.form.Layout = function(config){
49017     var xitems = [];
49018     if (config.items) {
49019         xitems = config.items;
49020         delete config.items;
49021     }
49022     Roo.form.Layout.superclass.constructor.call(this, config);
49023     this.stack = [];
49024     Roo.each(xitems, this.addxtype, this);
49025      
49026 };
49027
49028 Roo.extend(Roo.form.Layout, Roo.Component, {
49029     /**
49030      * @cfg {String/Object} autoCreate
49031      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49032      */
49033     /**
49034      * @cfg {String/Object/Function} style
49035      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49036      * a function which returns such a specification.
49037      */
49038     /**
49039      * @cfg {String} labelAlign
49040      * Valid values are "left," "top" and "right" (defaults to "left")
49041      */
49042     /**
49043      * @cfg {Number} labelWidth
49044      * Fixed width in pixels of all field labels (defaults to undefined)
49045      */
49046     /**
49047      * @cfg {Boolean} clear
49048      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49049      */
49050     clear : true,
49051     /**
49052      * @cfg {String} labelSeparator
49053      * The separator to use after field labels (defaults to ':')
49054      */
49055     labelSeparator : ':',
49056     /**
49057      * @cfg {Boolean} hideLabels
49058      * True to suppress the display of field labels in this layout (defaults to false)
49059      */
49060     hideLabels : false,
49061
49062     // private
49063     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49064     
49065     isLayout : true,
49066     
49067     // private
49068     onRender : function(ct, position){
49069         if(this.el){ // from markup
49070             this.el = Roo.get(this.el);
49071         }else {  // generate
49072             var cfg = this.getAutoCreate();
49073             this.el = ct.createChild(cfg, position);
49074         }
49075         if(this.style){
49076             this.el.applyStyles(this.style);
49077         }
49078         if(this.labelAlign){
49079             this.el.addClass('x-form-label-'+this.labelAlign);
49080         }
49081         if(this.hideLabels){
49082             this.labelStyle = "display:none";
49083             this.elementStyle = "padding-left:0;";
49084         }else{
49085             if(typeof this.labelWidth == 'number'){
49086                 this.labelStyle = "width:"+this.labelWidth+"px;";
49087                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49088             }
49089             if(this.labelAlign == 'top'){
49090                 this.labelStyle = "width:auto;";
49091                 this.elementStyle = "padding-left:0;";
49092             }
49093         }
49094         var stack = this.stack;
49095         var slen = stack.length;
49096         if(slen > 0){
49097             if(!this.fieldTpl){
49098                 var t = new Roo.Template(
49099                     '<div class="x-form-item {5}">',
49100                         '<label for="{0}" style="{2}">{1}{4}</label>',
49101                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49102                         '</div>',
49103                     '</div><div class="x-form-clear-left"></div>'
49104                 );
49105                 t.disableFormats = true;
49106                 t.compile();
49107                 Roo.form.Layout.prototype.fieldTpl = t;
49108             }
49109             for(var i = 0; i < slen; i++) {
49110                 if(stack[i].isFormField){
49111                     this.renderField(stack[i]);
49112                 }else{
49113                     this.renderComponent(stack[i]);
49114                 }
49115             }
49116         }
49117         if(this.clear){
49118             this.el.createChild({cls:'x-form-clear'});
49119         }
49120     },
49121
49122     // private
49123     renderField : function(f){
49124         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49125                f.id, //0
49126                f.fieldLabel, //1
49127                f.labelStyle||this.labelStyle||'', //2
49128                this.elementStyle||'', //3
49129                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49130                f.itemCls||this.itemCls||''  //5
49131        ], true).getPrevSibling());
49132     },
49133
49134     // private
49135     renderComponent : function(c){
49136         c.render(c.isLayout ? this.el : this.el.createChild());    
49137     },
49138     /**
49139      * Adds a object form elements (using the xtype property as the factory method.)
49140      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49141      * @param {Object} config 
49142      */
49143     addxtype : function(o)
49144     {
49145         // create the lement.
49146         o.form = this.form;
49147         var fe = Roo.factory(o, Roo.form);
49148         this.form.allItems.push(fe);
49149         this.stack.push(fe);
49150         
49151         if (fe.isFormField) {
49152             this.form.items.add(fe);
49153         }
49154          
49155         return fe;
49156     }
49157 });
49158
49159 /**
49160  * @class Roo.form.Column
49161  * @extends Roo.form.Layout
49162  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49163  * @constructor
49164  * @param {Object} config Configuration options
49165  */
49166 Roo.form.Column = function(config){
49167     Roo.form.Column.superclass.constructor.call(this, config);
49168 };
49169
49170 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49171     /**
49172      * @cfg {Number/String} width
49173      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49174      */
49175     /**
49176      * @cfg {String/Object} autoCreate
49177      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49178      */
49179
49180     // private
49181     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49182
49183     // private
49184     onRender : function(ct, position){
49185         Roo.form.Column.superclass.onRender.call(this, ct, position);
49186         if(this.width){
49187             this.el.setWidth(this.width);
49188         }
49189     }
49190 });
49191
49192
49193 /**
49194  * @class Roo.form.Row
49195  * @extends Roo.form.Layout
49196  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49197  * @constructor
49198  * @param {Object} config Configuration options
49199  */
49200
49201  
49202 Roo.form.Row = function(config){
49203     Roo.form.Row.superclass.constructor.call(this, config);
49204 };
49205  
49206 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49207       /**
49208      * @cfg {Number/String} width
49209      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49210      */
49211     /**
49212      * @cfg {Number/String} height
49213      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49214      */
49215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49216     
49217     padWidth : 20,
49218     // private
49219     onRender : function(ct, position){
49220         //console.log('row render');
49221         if(!this.rowTpl){
49222             var t = new Roo.Template(
49223                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49224                     '<label for="{0}" style="{2}">{1}{4}</label>',
49225                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49226                     '</div>',
49227                 '</div>'
49228             );
49229             t.disableFormats = true;
49230             t.compile();
49231             Roo.form.Layout.prototype.rowTpl = t;
49232         }
49233         this.fieldTpl = this.rowTpl;
49234         
49235         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49236         var labelWidth = 100;
49237         
49238         if ((this.labelAlign != 'top')) {
49239             if (typeof this.labelWidth == 'number') {
49240                 labelWidth = this.labelWidth
49241             }
49242             this.padWidth =  20 + labelWidth;
49243             
49244         }
49245         
49246         Roo.form.Column.superclass.onRender.call(this, ct, position);
49247         if(this.width){
49248             this.el.setWidth(this.width);
49249         }
49250         if(this.height){
49251             this.el.setHeight(this.height);
49252         }
49253     },
49254     
49255     // private
49256     renderField : function(f){
49257         f.fieldEl = this.fieldTpl.append(this.el, [
49258                f.id, f.fieldLabel,
49259                f.labelStyle||this.labelStyle||'',
49260                this.elementStyle||'',
49261                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49262                f.itemCls||this.itemCls||'',
49263                f.width ? f.width + this.padWidth : 160 + this.padWidth
49264        ],true);
49265     }
49266 });
49267  
49268
49269 /**
49270  * @class Roo.form.FieldSet
49271  * @extends Roo.form.Layout
49272  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49273  * @constructor
49274  * @param {Object} config Configuration options
49275  */
49276 Roo.form.FieldSet = function(config){
49277     Roo.form.FieldSet.superclass.constructor.call(this, config);
49278 };
49279
49280 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49281     /**
49282      * @cfg {String} legend
49283      * The text to display as the legend for the FieldSet (defaults to '')
49284      */
49285     /**
49286      * @cfg {String/Object} autoCreate
49287      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49288      */
49289
49290     // private
49291     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49292
49293     // private
49294     onRender : function(ct, position){
49295         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49296         if(this.legend){
49297             this.setLegend(this.legend);
49298         }
49299     },
49300
49301     // private
49302     setLegend : function(text){
49303         if(this.rendered){
49304             this.el.child('legend').update(text);
49305         }
49306     }
49307 });/*
49308  * Based on:
49309  * Ext JS Library 1.1.1
49310  * Copyright(c) 2006-2007, Ext JS, LLC.
49311  *
49312  * Originally Released Under LGPL - original licence link has changed is not relivant.
49313  *
49314  * Fork - LGPL
49315  * <script type="text/javascript">
49316  */
49317 /**
49318  * @class Roo.form.VTypes
49319  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49320  * @singleton
49321  */
49322 Roo.form.VTypes = function(){
49323     // closure these in so they are only created once.
49324     var alpha = /^[a-zA-Z_]+$/;
49325     var alphanum = /^[a-zA-Z0-9_]+$/;
49326     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49327     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49328
49329     // All these messages and functions are configurable
49330     return {
49331         /**
49332          * The function used to validate email addresses
49333          * @param {String} value The email address
49334          */
49335         'email' : function(v){
49336             return email.test(v);
49337         },
49338         /**
49339          * The error text to display when the email validation function returns false
49340          * @type String
49341          */
49342         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49343         /**
49344          * The keystroke filter mask to be applied on email input
49345          * @type RegExp
49346          */
49347         'emailMask' : /[a-z0-9_\.\-@]/i,
49348
49349         /**
49350          * The function used to validate URLs
49351          * @param {String} value The URL
49352          */
49353         'url' : function(v){
49354             return url.test(v);
49355         },
49356         /**
49357          * The error text to display when the url validation function returns false
49358          * @type String
49359          */
49360         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49361         
49362         /**
49363          * The function used to validate alpha values
49364          * @param {String} value The value
49365          */
49366         'alpha' : function(v){
49367             return alpha.test(v);
49368         },
49369         /**
49370          * The error text to display when the alpha validation function returns false
49371          * @type String
49372          */
49373         'alphaText' : 'This field should only contain letters and _',
49374         /**
49375          * The keystroke filter mask to be applied on alpha input
49376          * @type RegExp
49377          */
49378         'alphaMask' : /[a-z_]/i,
49379
49380         /**
49381          * The function used to validate alphanumeric values
49382          * @param {String} value The value
49383          */
49384         'alphanum' : function(v){
49385             return alphanum.test(v);
49386         },
49387         /**
49388          * The error text to display when the alphanumeric validation function returns false
49389          * @type String
49390          */
49391         'alphanumText' : 'This field should only contain letters, numbers and _',
49392         /**
49393          * The keystroke filter mask to be applied on alphanumeric input
49394          * @type RegExp
49395          */
49396         'alphanumMask' : /[a-z0-9_]/i
49397     };
49398 }();//<script type="text/javascript">
49399
49400 /**
49401  * @class Roo.form.FCKeditor
49402  * @extends Roo.form.TextArea
49403  * Wrapper around the FCKEditor http://www.fckeditor.net
49404  * @constructor
49405  * Creates a new FCKeditor
49406  * @param {Object} config Configuration options
49407  */
49408 Roo.form.FCKeditor = function(config){
49409     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49410     this.addEvents({
49411          /**
49412          * @event editorinit
49413          * Fired when the editor is initialized - you can add extra handlers here..
49414          * @param {FCKeditor} this
49415          * @param {Object} the FCK object.
49416          */
49417         editorinit : true
49418     });
49419     
49420     
49421 };
49422 Roo.form.FCKeditor.editors = { };
49423 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49424 {
49425     //defaultAutoCreate : {
49426     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49427     //},
49428     // private
49429     /**
49430      * @cfg {Object} fck options - see fck manual for details.
49431      */
49432     fckconfig : false,
49433     
49434     /**
49435      * @cfg {Object} fck toolbar set (Basic or Default)
49436      */
49437     toolbarSet : 'Basic',
49438     /**
49439      * @cfg {Object} fck BasePath
49440      */ 
49441     basePath : '/fckeditor/',
49442     
49443     
49444     frame : false,
49445     
49446     value : '',
49447     
49448    
49449     onRender : function(ct, position)
49450     {
49451         if(!this.el){
49452             this.defaultAutoCreate = {
49453                 tag: "textarea",
49454                 style:"width:300px;height:60px;",
49455                 autocomplete: "new-password"
49456             };
49457         }
49458         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49459         /*
49460         if(this.grow){
49461             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49462             if(this.preventScrollbars){
49463                 this.el.setStyle("overflow", "hidden");
49464             }
49465             this.el.setHeight(this.growMin);
49466         }
49467         */
49468         //console.log('onrender' + this.getId() );
49469         Roo.form.FCKeditor.editors[this.getId()] = this;
49470          
49471
49472         this.replaceTextarea() ;
49473         
49474     },
49475     
49476     getEditor : function() {
49477         return this.fckEditor;
49478     },
49479     /**
49480      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49481      * @param {Mixed} value The value to set
49482      */
49483     
49484     
49485     setValue : function(value)
49486     {
49487         //console.log('setValue: ' + value);
49488         
49489         if(typeof(value) == 'undefined') { // not sure why this is happending...
49490             return;
49491         }
49492         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49493         
49494         //if(!this.el || !this.getEditor()) {
49495         //    this.value = value;
49496             //this.setValue.defer(100,this,[value]);    
49497         //    return;
49498         //} 
49499         
49500         if(!this.getEditor()) {
49501             return;
49502         }
49503         
49504         this.getEditor().SetData(value);
49505         
49506         //
49507
49508     },
49509
49510     /**
49511      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49512      * @return {Mixed} value The field value
49513      */
49514     getValue : function()
49515     {
49516         
49517         if (this.frame && this.frame.dom.style.display == 'none') {
49518             return Roo.form.FCKeditor.superclass.getValue.call(this);
49519         }
49520         
49521         if(!this.el || !this.getEditor()) {
49522            
49523            // this.getValue.defer(100,this); 
49524             return this.value;
49525         }
49526        
49527         
49528         var value=this.getEditor().GetData();
49529         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49530         return Roo.form.FCKeditor.superclass.getValue.call(this);
49531         
49532
49533     },
49534
49535     /**
49536      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49537      * @return {Mixed} value The field value
49538      */
49539     getRawValue : function()
49540     {
49541         if (this.frame && this.frame.dom.style.display == 'none') {
49542             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49543         }
49544         
49545         if(!this.el || !this.getEditor()) {
49546             //this.getRawValue.defer(100,this); 
49547             return this.value;
49548             return;
49549         }
49550         
49551         
49552         
49553         var value=this.getEditor().GetData();
49554         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49555         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49556          
49557     },
49558     
49559     setSize : function(w,h) {
49560         
49561         
49562         
49563         //if (this.frame && this.frame.dom.style.display == 'none') {
49564         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49565         //    return;
49566         //}
49567         //if(!this.el || !this.getEditor()) {
49568         //    this.setSize.defer(100,this, [w,h]); 
49569         //    return;
49570         //}
49571         
49572         
49573         
49574         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49575         
49576         this.frame.dom.setAttribute('width', w);
49577         this.frame.dom.setAttribute('height', h);
49578         this.frame.setSize(w,h);
49579         
49580     },
49581     
49582     toggleSourceEdit : function(value) {
49583         
49584       
49585          
49586         this.el.dom.style.display = value ? '' : 'none';
49587         this.frame.dom.style.display = value ?  'none' : '';
49588         
49589     },
49590     
49591     
49592     focus: function(tag)
49593     {
49594         if (this.frame.dom.style.display == 'none') {
49595             return Roo.form.FCKeditor.superclass.focus.call(this);
49596         }
49597         if(!this.el || !this.getEditor()) {
49598             this.focus.defer(100,this, [tag]); 
49599             return;
49600         }
49601         
49602         
49603         
49604         
49605         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49606         this.getEditor().Focus();
49607         if (tgs.length) {
49608             if (!this.getEditor().Selection.GetSelection()) {
49609                 this.focus.defer(100,this, [tag]); 
49610                 return;
49611             }
49612             
49613             
49614             var r = this.getEditor().EditorDocument.createRange();
49615             r.setStart(tgs[0],0);
49616             r.setEnd(tgs[0],0);
49617             this.getEditor().Selection.GetSelection().removeAllRanges();
49618             this.getEditor().Selection.GetSelection().addRange(r);
49619             this.getEditor().Focus();
49620         }
49621         
49622     },
49623     
49624     
49625     
49626     replaceTextarea : function()
49627     {
49628         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49629             return ;
49630         }
49631         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49632         //{
49633             // We must check the elements firstly using the Id and then the name.
49634         var oTextarea = document.getElementById( this.getId() );
49635         
49636         var colElementsByName = document.getElementsByName( this.getId() ) ;
49637          
49638         oTextarea.style.display = 'none' ;
49639
49640         if ( oTextarea.tabIndex ) {            
49641             this.TabIndex = oTextarea.tabIndex ;
49642         }
49643         
49644         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49645         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49646         this.frame = Roo.get(this.getId() + '___Frame')
49647     },
49648     
49649     _getConfigHtml : function()
49650     {
49651         var sConfig = '' ;
49652
49653         for ( var o in this.fckconfig ) {
49654             sConfig += sConfig.length > 0  ? '&amp;' : '';
49655             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49656         }
49657
49658         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49659     },
49660     
49661     
49662     _getIFrameHtml : function()
49663     {
49664         var sFile = 'fckeditor.html' ;
49665         /* no idea what this is about..
49666         try
49667         {
49668             if ( (/fcksource=true/i).test( window.top.location.search ) )
49669                 sFile = 'fckeditor.original.html' ;
49670         }
49671         catch (e) { 
49672         */
49673
49674         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49675         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49676         
49677         
49678         var html = '<iframe id="' + this.getId() +
49679             '___Frame" src="' + sLink +
49680             '" width="' + this.width +
49681             '" height="' + this.height + '"' +
49682             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49683             ' frameborder="0" scrolling="no"></iframe>' ;
49684
49685         return html ;
49686     },
49687     
49688     _insertHtmlBefore : function( html, element )
49689     {
49690         if ( element.insertAdjacentHTML )       {
49691             // IE
49692             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49693         } else { // Gecko
49694             var oRange = document.createRange() ;
49695             oRange.setStartBefore( element ) ;
49696             var oFragment = oRange.createContextualFragment( html );
49697             element.parentNode.insertBefore( oFragment, element ) ;
49698         }
49699     }
49700     
49701     
49702   
49703     
49704     
49705     
49706     
49707
49708 });
49709
49710 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49711
49712 function FCKeditor_OnComplete(editorInstance){
49713     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49714     f.fckEditor = editorInstance;
49715     //console.log("loaded");
49716     f.fireEvent('editorinit', f, editorInstance);
49717
49718   
49719
49720  
49721
49722
49723
49724
49725
49726
49727
49728
49729
49730
49731
49732
49733
49734
49735
49736 //<script type="text/javascript">
49737 /**
49738  * @class Roo.form.GridField
49739  * @extends Roo.form.Field
49740  * Embed a grid (or editable grid into a form)
49741  * STATUS ALPHA
49742  * 
49743  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49744  * it needs 
49745  * xgrid.store = Roo.data.Store
49746  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49747  * xgrid.store.reader = Roo.data.JsonReader 
49748  * 
49749  * 
49750  * @constructor
49751  * Creates a new GridField
49752  * @param {Object} config Configuration options
49753  */
49754 Roo.form.GridField = function(config){
49755     Roo.form.GridField.superclass.constructor.call(this, config);
49756      
49757 };
49758
49759 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49760     /**
49761      * @cfg {Number} width  - used to restrict width of grid..
49762      */
49763     width : 100,
49764     /**
49765      * @cfg {Number} height - used to restrict height of grid..
49766      */
49767     height : 50,
49768      /**
49769      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49770          * 
49771          *}
49772      */
49773     xgrid : false, 
49774     /**
49775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49776      * {tag: "input", type: "checkbox", autocomplete: "off"})
49777      */
49778    // defaultAutoCreate : { tag: 'div' },
49779     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49780     /**
49781      * @cfg {String} addTitle Text to include for adding a title.
49782      */
49783     addTitle : false,
49784     //
49785     onResize : function(){
49786         Roo.form.Field.superclass.onResize.apply(this, arguments);
49787     },
49788
49789     initEvents : function(){
49790         // Roo.form.Checkbox.superclass.initEvents.call(this);
49791         // has no events...
49792        
49793     },
49794
49795
49796     getResizeEl : function(){
49797         return this.wrap;
49798     },
49799
49800     getPositionEl : function(){
49801         return this.wrap;
49802     },
49803
49804     // private
49805     onRender : function(ct, position){
49806         
49807         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49808         var style = this.style;
49809         delete this.style;
49810         
49811         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49812         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49813         this.viewEl = this.wrap.createChild({ tag: 'div' });
49814         if (style) {
49815             this.viewEl.applyStyles(style);
49816         }
49817         if (this.width) {
49818             this.viewEl.setWidth(this.width);
49819         }
49820         if (this.height) {
49821             this.viewEl.setHeight(this.height);
49822         }
49823         //if(this.inputValue !== undefined){
49824         //this.setValue(this.value);
49825         
49826         
49827         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49828         
49829         
49830         this.grid.render();
49831         this.grid.getDataSource().on('remove', this.refreshValue, this);
49832         this.grid.getDataSource().on('update', this.refreshValue, this);
49833         this.grid.on('afteredit', this.refreshValue, this);
49834  
49835     },
49836      
49837     
49838     /**
49839      * Sets the value of the item. 
49840      * @param {String} either an object  or a string..
49841      */
49842     setValue : function(v){
49843         //this.value = v;
49844         v = v || []; // empty set..
49845         // this does not seem smart - it really only affects memoryproxy grids..
49846         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49847             var ds = this.grid.getDataSource();
49848             // assumes a json reader..
49849             var data = {}
49850             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49851             ds.loadData( data);
49852         }
49853         // clear selection so it does not get stale.
49854         if (this.grid.sm) { 
49855             this.grid.sm.clearSelections();
49856         }
49857         
49858         Roo.form.GridField.superclass.setValue.call(this, v);
49859         this.refreshValue();
49860         // should load data in the grid really....
49861     },
49862     
49863     // private
49864     refreshValue: function() {
49865          var val = [];
49866         this.grid.getDataSource().each(function(r) {
49867             val.push(r.data);
49868         });
49869         this.el.dom.value = Roo.encode(val);
49870     }
49871     
49872      
49873     
49874     
49875 });/*
49876  * Based on:
49877  * Ext JS Library 1.1.1
49878  * Copyright(c) 2006-2007, Ext JS, LLC.
49879  *
49880  * Originally Released Under LGPL - original licence link has changed is not relivant.
49881  *
49882  * Fork - LGPL
49883  * <script type="text/javascript">
49884  */
49885 /**
49886  * @class Roo.form.DisplayField
49887  * @extends Roo.form.Field
49888  * A generic Field to display non-editable data.
49889  * @cfg {Boolean} closable (true|false) default false
49890  * @constructor
49891  * Creates a new Display Field item.
49892  * @param {Object} config Configuration options
49893  */
49894 Roo.form.DisplayField = function(config){
49895     Roo.form.DisplayField.superclass.constructor.call(this, config);
49896     
49897     this.addEvents({
49898         /**
49899          * @event close
49900          * Fires after the click the close btn
49901              * @param {Roo.form.DisplayField} this
49902              */
49903         close : true
49904     });
49905 };
49906
49907 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49908     inputType:      'hidden',
49909     allowBlank:     true,
49910     readOnly:         true,
49911     
49912  
49913     /**
49914      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49915      */
49916     focusClass : undefined,
49917     /**
49918      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49919      */
49920     fieldClass: 'x-form-field',
49921     
49922      /**
49923      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49924      */
49925     valueRenderer: undefined,
49926     
49927     width: 100,
49928     /**
49929      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49930      * {tag: "input", type: "checkbox", autocomplete: "off"})
49931      */
49932      
49933  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49934  
49935     closable : false,
49936     
49937     onResize : function(){
49938         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49939         
49940     },
49941
49942     initEvents : function(){
49943         // Roo.form.Checkbox.superclass.initEvents.call(this);
49944         // has no events...
49945         
49946         if(this.closable){
49947             this.closeEl.on('click', this.onClose, this);
49948         }
49949        
49950     },
49951
49952
49953     getResizeEl : function(){
49954         return this.wrap;
49955     },
49956
49957     getPositionEl : function(){
49958         return this.wrap;
49959     },
49960
49961     // private
49962     onRender : function(ct, position){
49963         
49964         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49965         //if(this.inputValue !== undefined){
49966         this.wrap = this.el.wrap();
49967         
49968         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49969         
49970         if(this.closable){
49971             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49972         }
49973         
49974         if (this.bodyStyle) {
49975             this.viewEl.applyStyles(this.bodyStyle);
49976         }
49977         //this.viewEl.setStyle('padding', '2px');
49978         
49979         this.setValue(this.value);
49980         
49981     },
49982 /*
49983     // private
49984     initValue : Roo.emptyFn,
49985
49986   */
49987
49988         // private
49989     onClick : function(){
49990         
49991     },
49992
49993     /**
49994      * Sets the checked state of the checkbox.
49995      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49996      */
49997     setValue : function(v){
49998         this.value = v;
49999         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50000         // this might be called before we have a dom element..
50001         if (!this.viewEl) {
50002             return;
50003         }
50004         this.viewEl.dom.innerHTML = html;
50005         Roo.form.DisplayField.superclass.setValue.call(this, v);
50006
50007     },
50008     
50009     onClose : function(e)
50010     {
50011         e.preventDefault();
50012         
50013         this.fireEvent('close', this);
50014     }
50015 });/*
50016  * 
50017  * Licence- LGPL
50018  * 
50019  */
50020
50021 /**
50022  * @class Roo.form.DayPicker
50023  * @extends Roo.form.Field
50024  * A Day picker show [M] [T] [W] ....
50025  * @constructor
50026  * Creates a new Day Picker
50027  * @param {Object} config Configuration options
50028  */
50029 Roo.form.DayPicker= function(config){
50030     Roo.form.DayPicker.superclass.constructor.call(this, config);
50031      
50032 };
50033
50034 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50035     /**
50036      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50037      */
50038     focusClass : undefined,
50039     /**
50040      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50041      */
50042     fieldClass: "x-form-field",
50043    
50044     /**
50045      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50046      * {tag: "input", type: "checkbox", autocomplete: "off"})
50047      */
50048     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50049     
50050    
50051     actionMode : 'viewEl', 
50052     //
50053     // private
50054  
50055     inputType : 'hidden',
50056     
50057      
50058     inputElement: false, // real input element?
50059     basedOn: false, // ????
50060     
50061     isFormField: true, // not sure where this is needed!!!!
50062
50063     onResize : function(){
50064         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50065         if(!this.boxLabel){
50066             this.el.alignTo(this.wrap, 'c-c');
50067         }
50068     },
50069
50070     initEvents : function(){
50071         Roo.form.Checkbox.superclass.initEvents.call(this);
50072         this.el.on("click", this.onClick,  this);
50073         this.el.on("change", this.onClick,  this);
50074     },
50075
50076
50077     getResizeEl : function(){
50078         return this.wrap;
50079     },
50080
50081     getPositionEl : function(){
50082         return this.wrap;
50083     },
50084
50085     
50086     // private
50087     onRender : function(ct, position){
50088         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50089        
50090         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50091         
50092         var r1 = '<table><tr>';
50093         var r2 = '<tr class="x-form-daypick-icons">';
50094         for (var i=0; i < 7; i++) {
50095             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50096             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50097         }
50098         
50099         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50100         viewEl.select('img').on('click', this.onClick, this);
50101         this.viewEl = viewEl;   
50102         
50103         
50104         // this will not work on Chrome!!!
50105         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50106         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50107         
50108         
50109           
50110
50111     },
50112
50113     // private
50114     initValue : Roo.emptyFn,
50115
50116     /**
50117      * Returns the checked state of the checkbox.
50118      * @return {Boolean} True if checked, else false
50119      */
50120     getValue : function(){
50121         return this.el.dom.value;
50122         
50123     },
50124
50125         // private
50126     onClick : function(e){ 
50127         //this.setChecked(!this.checked);
50128         Roo.get(e.target).toggleClass('x-menu-item-checked');
50129         this.refreshValue();
50130         //if(this.el.dom.checked != this.checked){
50131         //    this.setValue(this.el.dom.checked);
50132        // }
50133     },
50134     
50135     // private
50136     refreshValue : function()
50137     {
50138         var val = '';
50139         this.viewEl.select('img',true).each(function(e,i,n)  {
50140             val += e.is(".x-menu-item-checked") ? String(n) : '';
50141         });
50142         this.setValue(val, true);
50143     },
50144
50145     /**
50146      * Sets the checked state of the checkbox.
50147      * On is always based on a string comparison between inputValue and the param.
50148      * @param {Boolean/String} value - the value to set 
50149      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50150      */
50151     setValue : function(v,suppressEvent){
50152         if (!this.el.dom) {
50153             return;
50154         }
50155         var old = this.el.dom.value ;
50156         this.el.dom.value = v;
50157         if (suppressEvent) {
50158             return ;
50159         }
50160          
50161         // update display..
50162         this.viewEl.select('img',true).each(function(e,i,n)  {
50163             
50164             var on = e.is(".x-menu-item-checked");
50165             var newv = v.indexOf(String(n)) > -1;
50166             if (on != newv) {
50167                 e.toggleClass('x-menu-item-checked');
50168             }
50169             
50170         });
50171         
50172         
50173         this.fireEvent('change', this, v, old);
50174         
50175         
50176     },
50177    
50178     // handle setting of hidden value by some other method!!?!?
50179     setFromHidden: function()
50180     {
50181         if(!this.el){
50182             return;
50183         }
50184         //console.log("SET FROM HIDDEN");
50185         //alert('setFrom hidden');
50186         this.setValue(this.el.dom.value);
50187     },
50188     
50189     onDestroy : function()
50190     {
50191         if(this.viewEl){
50192             Roo.get(this.viewEl).remove();
50193         }
50194          
50195         Roo.form.DayPicker.superclass.onDestroy.call(this);
50196     }
50197
50198 });/*
50199  * RooJS Library 1.1.1
50200  * Copyright(c) 2008-2011  Alan Knowles
50201  *
50202  * License - LGPL
50203  */
50204  
50205
50206 /**
50207  * @class Roo.form.ComboCheck
50208  * @extends Roo.form.ComboBox
50209  * A combobox for multiple select items.
50210  *
50211  * FIXME - could do with a reset button..
50212  * 
50213  * @constructor
50214  * Create a new ComboCheck
50215  * @param {Object} config Configuration options
50216  */
50217 Roo.form.ComboCheck = function(config){
50218     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50219     // should verify some data...
50220     // like
50221     // hiddenName = required..
50222     // displayField = required
50223     // valudField == required
50224     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50225     var _t = this;
50226     Roo.each(req, function(e) {
50227         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50228             throw "Roo.form.ComboCheck : missing value for: " + e;
50229         }
50230     });
50231     
50232     
50233 };
50234
50235 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50236      
50237      
50238     editable : false,
50239      
50240     selectedClass: 'x-menu-item-checked', 
50241     
50242     // private
50243     onRender : function(ct, position){
50244         var _t = this;
50245         
50246         
50247         
50248         if(!this.tpl){
50249             var cls = 'x-combo-list';
50250
50251             
50252             this.tpl =  new Roo.Template({
50253                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50254                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50255                    '<span>{' + this.displayField + '}</span>' +
50256                     '</div>' 
50257                 
50258             });
50259         }
50260  
50261         
50262         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50263         this.view.singleSelect = false;
50264         this.view.multiSelect = true;
50265         this.view.toggleSelect = true;
50266         this.pageTb.add(new Roo.Toolbar.Fill(), {
50267             
50268             text: 'Done',
50269             handler: function()
50270             {
50271                 _t.collapse();
50272             }
50273         });
50274     },
50275     
50276     onViewOver : function(e, t){
50277         // do nothing...
50278         return;
50279         
50280     },
50281     
50282     onViewClick : function(doFocus,index){
50283         return;
50284         
50285     },
50286     select: function () {
50287         //Roo.log("SELECT CALLED");
50288     },
50289      
50290     selectByValue : function(xv, scrollIntoView){
50291         var ar = this.getValueArray();
50292         var sels = [];
50293         
50294         Roo.each(ar, function(v) {
50295             if(v === undefined || v === null){
50296                 return;
50297             }
50298             var r = this.findRecord(this.valueField, v);
50299             if(r){
50300                 sels.push(this.store.indexOf(r))
50301                 
50302             }
50303         },this);
50304         this.view.select(sels);
50305         return false;
50306     },
50307     
50308     
50309     
50310     onSelect : function(record, index){
50311        // Roo.log("onselect Called");
50312        // this is only called by the clear button now..
50313         this.view.clearSelections();
50314         this.setValue('[]');
50315         if (this.value != this.valueBefore) {
50316             this.fireEvent('change', this, this.value, this.valueBefore);
50317             this.valueBefore = this.value;
50318         }
50319     },
50320     getValueArray : function()
50321     {
50322         var ar = [] ;
50323         
50324         try {
50325             //Roo.log(this.value);
50326             if (typeof(this.value) == 'undefined') {
50327                 return [];
50328             }
50329             var ar = Roo.decode(this.value);
50330             return  ar instanceof Array ? ar : []; //?? valid?
50331             
50332         } catch(e) {
50333             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50334             return [];
50335         }
50336          
50337     },
50338     expand : function ()
50339     {
50340         
50341         Roo.form.ComboCheck.superclass.expand.call(this);
50342         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50343         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50344         
50345
50346     },
50347     
50348     collapse : function(){
50349         Roo.form.ComboCheck.superclass.collapse.call(this);
50350         var sl = this.view.getSelectedIndexes();
50351         var st = this.store;
50352         var nv = [];
50353         var tv = [];
50354         var r;
50355         Roo.each(sl, function(i) {
50356             r = st.getAt(i);
50357             nv.push(r.get(this.valueField));
50358         },this);
50359         this.setValue(Roo.encode(nv));
50360         if (this.value != this.valueBefore) {
50361
50362             this.fireEvent('change', this, this.value, this.valueBefore);
50363             this.valueBefore = this.value;
50364         }
50365         
50366     },
50367     
50368     setValue : function(v){
50369         // Roo.log(v);
50370         this.value = v;
50371         
50372         var vals = this.getValueArray();
50373         var tv = [];
50374         Roo.each(vals, function(k) {
50375             var r = this.findRecord(this.valueField, k);
50376             if(r){
50377                 tv.push(r.data[this.displayField]);
50378             }else if(this.valueNotFoundText !== undefined){
50379                 tv.push( this.valueNotFoundText );
50380             }
50381         },this);
50382        // Roo.log(tv);
50383         
50384         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50385         this.hiddenField.value = v;
50386         this.value = v;
50387     }
50388     
50389 });/*
50390  * Based on:
50391  * Ext JS Library 1.1.1
50392  * Copyright(c) 2006-2007, Ext JS, LLC.
50393  *
50394  * Originally Released Under LGPL - original licence link has changed is not relivant.
50395  *
50396  * Fork - LGPL
50397  * <script type="text/javascript">
50398  */
50399  
50400 /**
50401  * @class Roo.form.Signature
50402  * @extends Roo.form.Field
50403  * Signature field.  
50404  * @constructor
50405  * 
50406  * @param {Object} config Configuration options
50407  */
50408
50409 Roo.form.Signature = function(config){
50410     Roo.form.Signature.superclass.constructor.call(this, config);
50411     
50412     this.addEvents({// not in used??
50413          /**
50414          * @event confirm
50415          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50416              * @param {Roo.form.Signature} combo This combo box
50417              */
50418         'confirm' : true,
50419         /**
50420          * @event reset
50421          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50422              * @param {Roo.form.ComboBox} combo This combo box
50423              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50424              */
50425         'reset' : true
50426     });
50427 };
50428
50429 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50430     /**
50431      * @cfg {Object} labels Label to use when rendering a form.
50432      * defaults to 
50433      * labels : { 
50434      *      clear : "Clear",
50435      *      confirm : "Confirm"
50436      *  }
50437      */
50438     labels : { 
50439         clear : "Clear",
50440         confirm : "Confirm"
50441     },
50442     /**
50443      * @cfg {Number} width The signature panel width (defaults to 300)
50444      */
50445     width: 300,
50446     /**
50447      * @cfg {Number} height The signature panel height (defaults to 100)
50448      */
50449     height : 100,
50450     /**
50451      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50452      */
50453     allowBlank : false,
50454     
50455     //private
50456     // {Object} signPanel The signature SVG panel element (defaults to {})
50457     signPanel : {},
50458     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50459     isMouseDown : false,
50460     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50461     isConfirmed : false,
50462     // {String} signatureTmp SVG mapping string (defaults to empty string)
50463     signatureTmp : '',
50464     
50465     
50466     defaultAutoCreate : { // modified by initCompnoent..
50467         tag: "input",
50468         type:"hidden"
50469     },
50470
50471     // private
50472     onRender : function(ct, position){
50473         
50474         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50475         
50476         this.wrap = this.el.wrap({
50477             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50478         });
50479         
50480         this.createToolbar(this);
50481         this.signPanel = this.wrap.createChild({
50482                 tag: 'div',
50483                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50484             }, this.el
50485         );
50486             
50487         this.svgID = Roo.id();
50488         this.svgEl = this.signPanel.createChild({
50489               xmlns : 'http://www.w3.org/2000/svg',
50490               tag : 'svg',
50491               id : this.svgID + "-svg",
50492               width: this.width,
50493               height: this.height,
50494               viewBox: '0 0 '+this.width+' '+this.height,
50495               cn : [
50496                 {
50497                     tag: "rect",
50498                     id: this.svgID + "-svg-r",
50499                     width: this.width,
50500                     height: this.height,
50501                     fill: "#ffa"
50502                 },
50503                 {
50504                     tag: "line",
50505                     id: this.svgID + "-svg-l",
50506                     x1: "0", // start
50507                     y1: (this.height*0.8), // start set the line in 80% of height
50508                     x2: this.width, // end
50509                     y2: (this.height*0.8), // end set the line in 80% of height
50510                     'stroke': "#666",
50511                     'stroke-width': "1",
50512                     'stroke-dasharray': "3",
50513                     'shape-rendering': "crispEdges",
50514                     'pointer-events': "none"
50515                 },
50516                 {
50517                     tag: "path",
50518                     id: this.svgID + "-svg-p",
50519                     'stroke': "navy",
50520                     'stroke-width': "3",
50521                     'fill': "none",
50522                     'pointer-events': 'none'
50523                 }
50524               ]
50525         });
50526         this.createSVG();
50527         this.svgBox = this.svgEl.dom.getScreenCTM();
50528     },
50529     createSVG : function(){ 
50530         var svg = this.signPanel;
50531         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50532         var t = this;
50533
50534         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50535         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50536         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50537         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50538         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50539         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50540         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50541         
50542     },
50543     isTouchEvent : function(e){
50544         return e.type.match(/^touch/);
50545     },
50546     getCoords : function (e) {
50547         var pt    = this.svgEl.dom.createSVGPoint();
50548         pt.x = e.clientX; 
50549         pt.y = e.clientY;
50550         if (this.isTouchEvent(e)) {
50551             pt.x =  e.targetTouches[0].clientX;
50552             pt.y = e.targetTouches[0].clientY;
50553         }
50554         var a = this.svgEl.dom.getScreenCTM();
50555         var b = a.inverse();
50556         var mx = pt.matrixTransform(b);
50557         return mx.x + ',' + mx.y;
50558     },
50559     //mouse event headler 
50560     down : function (e) {
50561         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50562         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50563         
50564         this.isMouseDown = true;
50565         
50566         e.preventDefault();
50567     },
50568     move : function (e) {
50569         if (this.isMouseDown) {
50570             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50571             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50572         }
50573         
50574         e.preventDefault();
50575     },
50576     up : function (e) {
50577         this.isMouseDown = false;
50578         var sp = this.signatureTmp.split(' ');
50579         
50580         if(sp.length > 1){
50581             if(!sp[sp.length-2].match(/^L/)){
50582                 sp.pop();
50583                 sp.pop();
50584                 sp.push("");
50585                 this.signatureTmp = sp.join(" ");
50586             }
50587         }
50588         if(this.getValue() != this.signatureTmp){
50589             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50590             this.isConfirmed = false;
50591         }
50592         e.preventDefault();
50593     },
50594     
50595     /**
50596      * Protected method that will not generally be called directly. It
50597      * is called when the editor creates its toolbar. Override this method if you need to
50598      * add custom toolbar buttons.
50599      * @param {HtmlEditor} editor
50600      */
50601     createToolbar : function(editor){
50602          function btn(id, toggle, handler){
50603             var xid = fid + '-'+ id ;
50604             return {
50605                 id : xid,
50606                 cmd : id,
50607                 cls : 'x-btn-icon x-edit-'+id,
50608                 enableToggle:toggle !== false,
50609                 scope: editor, // was editor...
50610                 handler:handler||editor.relayBtnCmd,
50611                 clickEvent:'mousedown',
50612                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50613                 tabIndex:-1
50614             };
50615         }
50616         
50617         
50618         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50619         this.tb = tb;
50620         this.tb.add(
50621            {
50622                 cls : ' x-signature-btn x-signature-'+id,
50623                 scope: editor, // was editor...
50624                 handler: this.reset,
50625                 clickEvent:'mousedown',
50626                 text: this.labels.clear
50627             },
50628             {
50629                  xtype : 'Fill',
50630                  xns: Roo.Toolbar
50631             }, 
50632             {
50633                 cls : '  x-signature-btn x-signature-'+id,
50634                 scope: editor, // was editor...
50635                 handler: this.confirmHandler,
50636                 clickEvent:'mousedown',
50637                 text: this.labels.confirm
50638             }
50639         );
50640     
50641     },
50642     //public
50643     /**
50644      * when user is clicked confirm then show this image.....
50645      * 
50646      * @return {String} Image Data URI
50647      */
50648     getImageDataURI : function(){
50649         var svg = this.svgEl.dom.parentNode.innerHTML;
50650         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50651         return src; 
50652     },
50653     /**
50654      * 
50655      * @return {Boolean} this.isConfirmed
50656      */
50657     getConfirmed : function(){
50658         return this.isConfirmed;
50659     },
50660     /**
50661      * 
50662      * @return {Number} this.width
50663      */
50664     getWidth : function(){
50665         return this.width;
50666     },
50667     /**
50668      * 
50669      * @return {Number} this.height
50670      */
50671     getHeight : function(){
50672         return this.height;
50673     },
50674     // private
50675     getSignature : function(){
50676         return this.signatureTmp;
50677     },
50678     // private
50679     reset : function(){
50680         this.signatureTmp = '';
50681         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50682         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50683         this.isConfirmed = false;
50684         Roo.form.Signature.superclass.reset.call(this);
50685     },
50686     setSignature : function(s){
50687         this.signatureTmp = s;
50688         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50689         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50690         this.setValue(s);
50691         this.isConfirmed = false;
50692         Roo.form.Signature.superclass.reset.call(this);
50693     }, 
50694     test : function(){
50695 //        Roo.log(this.signPanel.dom.contentWindow.up())
50696     },
50697     //private
50698     setConfirmed : function(){
50699         
50700         
50701         
50702 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50703     },
50704     // private
50705     confirmHandler : function(){
50706         if(!this.getSignature()){
50707             return;
50708         }
50709         
50710         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50711         this.setValue(this.getSignature());
50712         this.isConfirmed = true;
50713         
50714         this.fireEvent('confirm', this);
50715     },
50716     // private
50717     // Subclasses should provide the validation implementation by overriding this
50718     validateValue : function(value){
50719         if(this.allowBlank){
50720             return true;
50721         }
50722         
50723         if(this.isConfirmed){
50724             return true;
50725         }
50726         return false;
50727     }
50728 });/*
50729  * Based on:
50730  * Ext JS Library 1.1.1
50731  * Copyright(c) 2006-2007, Ext JS, LLC.
50732  *
50733  * Originally Released Under LGPL - original licence link has changed is not relivant.
50734  *
50735  * Fork - LGPL
50736  * <script type="text/javascript">
50737  */
50738  
50739
50740 /**
50741  * @class Roo.form.ComboBox
50742  * @extends Roo.form.TriggerField
50743  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50744  * @constructor
50745  * Create a new ComboBox.
50746  * @param {Object} config Configuration options
50747  */
50748 Roo.form.Select = function(config){
50749     Roo.form.Select.superclass.constructor.call(this, config);
50750      
50751 };
50752
50753 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50754     /**
50755      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50756      */
50757     /**
50758      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50759      * rendering into an Roo.Editor, defaults to false)
50760      */
50761     /**
50762      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50763      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50764      */
50765     /**
50766      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50767      */
50768     /**
50769      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50770      * the dropdown list (defaults to undefined, with no header element)
50771      */
50772
50773      /**
50774      * @cfg {String/Roo.Template} tpl The template to use to render the output
50775      */
50776      
50777     // private
50778     defaultAutoCreate : {tag: "select"  },
50779     /**
50780      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50781      */
50782     listWidth: undefined,
50783     /**
50784      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50785      * mode = 'remote' or 'text' if mode = 'local')
50786      */
50787     displayField: undefined,
50788     /**
50789      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50790      * mode = 'remote' or 'value' if mode = 'local'). 
50791      * Note: use of a valueField requires the user make a selection
50792      * in order for a value to be mapped.
50793      */
50794     valueField: undefined,
50795     
50796     
50797     /**
50798      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50799      * field's data value (defaults to the underlying DOM element's name)
50800      */
50801     hiddenName: undefined,
50802     /**
50803      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50804      */
50805     listClass: '',
50806     /**
50807      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50808      */
50809     selectedClass: 'x-combo-selected',
50810     /**
50811      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50812      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50813      * which displays a downward arrow icon).
50814      */
50815     triggerClass : 'x-form-arrow-trigger',
50816     /**
50817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50818      */
50819     shadow:'sides',
50820     /**
50821      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50822      * anchor positions (defaults to 'tl-bl')
50823      */
50824     listAlign: 'tl-bl?',
50825     /**
50826      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50827      */
50828     maxHeight: 300,
50829     /**
50830      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50831      * query specified by the allQuery config option (defaults to 'query')
50832      */
50833     triggerAction: 'query',
50834     /**
50835      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50836      * (defaults to 4, does not apply if editable = false)
50837      */
50838     minChars : 4,
50839     /**
50840      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50841      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50842      */
50843     typeAhead: false,
50844     /**
50845      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50846      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50847      */
50848     queryDelay: 500,
50849     /**
50850      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50851      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50852      */
50853     pageSize: 0,
50854     /**
50855      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50856      * when editable = true (defaults to false)
50857      */
50858     selectOnFocus:false,
50859     /**
50860      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50861      */
50862     queryParam: 'query',
50863     /**
50864      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50865      * when mode = 'remote' (defaults to 'Loading...')
50866      */
50867     loadingText: 'Loading...',
50868     /**
50869      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50870      */
50871     resizable: false,
50872     /**
50873      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50874      */
50875     handleHeight : 8,
50876     /**
50877      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50878      * traditional select (defaults to true)
50879      */
50880     editable: true,
50881     /**
50882      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50883      */
50884     allQuery: '',
50885     /**
50886      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50887      */
50888     mode: 'remote',
50889     /**
50890      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50891      * listWidth has a higher value)
50892      */
50893     minListWidth : 70,
50894     /**
50895      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50896      * allow the user to set arbitrary text into the field (defaults to false)
50897      */
50898     forceSelection:false,
50899     /**
50900      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50901      * if typeAhead = true (defaults to 250)
50902      */
50903     typeAheadDelay : 250,
50904     /**
50905      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50906      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50907      */
50908     valueNotFoundText : undefined,
50909     
50910     /**
50911      * @cfg {String} defaultValue The value displayed after loading the store.
50912      */
50913     defaultValue: '',
50914     
50915     /**
50916      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50917      */
50918     blockFocus : false,
50919     
50920     /**
50921      * @cfg {Boolean} disableClear Disable showing of clear button.
50922      */
50923     disableClear : false,
50924     /**
50925      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50926      */
50927     alwaysQuery : false,
50928     
50929     //private
50930     addicon : false,
50931     editicon: false,
50932     
50933     // element that contains real text value.. (when hidden is used..)
50934      
50935     // private
50936     onRender : function(ct, position){
50937         Roo.form.Field.prototype.onRender.call(this, ct, position);
50938         
50939         if(this.store){
50940             this.store.on('beforeload', this.onBeforeLoad, this);
50941             this.store.on('load', this.onLoad, this);
50942             this.store.on('loadexception', this.onLoadException, this);
50943             this.store.load({});
50944         }
50945         
50946         
50947         
50948     },
50949
50950     // private
50951     initEvents : function(){
50952         //Roo.form.ComboBox.superclass.initEvents.call(this);
50953  
50954     },
50955
50956     onDestroy : function(){
50957        
50958         if(this.store){
50959             this.store.un('beforeload', this.onBeforeLoad, this);
50960             this.store.un('load', this.onLoad, this);
50961             this.store.un('loadexception', this.onLoadException, this);
50962         }
50963         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50964     },
50965
50966     // private
50967     fireKey : function(e){
50968         if(e.isNavKeyPress() && !this.list.isVisible()){
50969             this.fireEvent("specialkey", this, e);
50970         }
50971     },
50972
50973     // private
50974     onResize: function(w, h){
50975         
50976         return; 
50977     
50978         
50979     },
50980
50981     /**
50982      * Allow or prevent the user from directly editing the field text.  If false is passed,
50983      * the user will only be able to select from the items defined in the dropdown list.  This method
50984      * is the runtime equivalent of setting the 'editable' config option at config time.
50985      * @param {Boolean} value True to allow the user to directly edit the field text
50986      */
50987     setEditable : function(value){
50988          
50989     },
50990
50991     // private
50992     onBeforeLoad : function(){
50993         
50994         Roo.log("Select before load");
50995         return;
50996     
50997         this.innerList.update(this.loadingText ?
50998                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50999         //this.restrictHeight();
51000         this.selectedIndex = -1;
51001     },
51002
51003     // private
51004     onLoad : function(){
51005
51006     
51007         var dom = this.el.dom;
51008         dom.innerHTML = '';
51009          var od = dom.ownerDocument;
51010          
51011         if (this.emptyText) {
51012             var op = od.createElement('option');
51013             op.setAttribute('value', '');
51014             op.innerHTML = String.format('{0}', this.emptyText);
51015             dom.appendChild(op);
51016         }
51017         if(this.store.getCount() > 0){
51018            
51019             var vf = this.valueField;
51020             var df = this.displayField;
51021             this.store.data.each(function(r) {
51022                 // which colmsn to use... testing - cdoe / title..
51023                 var op = od.createElement('option');
51024                 op.setAttribute('value', r.data[vf]);
51025                 op.innerHTML = String.format('{0}', r.data[df]);
51026                 dom.appendChild(op);
51027             });
51028             if (typeof(this.defaultValue != 'undefined')) {
51029                 this.setValue(this.defaultValue);
51030             }
51031             
51032              
51033         }else{
51034             //this.onEmptyResults();
51035         }
51036         //this.el.focus();
51037     },
51038     // private
51039     onLoadException : function()
51040     {
51041         dom.innerHTML = '';
51042             
51043         Roo.log("Select on load exception");
51044         return;
51045     
51046         this.collapse();
51047         Roo.log(this.store.reader.jsonData);
51048         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51049             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51050         }
51051         
51052         
51053     },
51054     // private
51055     onTypeAhead : function(){
51056          
51057     },
51058
51059     // private
51060     onSelect : function(record, index){
51061         Roo.log('on select?');
51062         return;
51063         if(this.fireEvent('beforeselect', this, record, index) !== false){
51064             this.setFromData(index > -1 ? record.data : false);
51065             this.collapse();
51066             this.fireEvent('select', this, record, index);
51067         }
51068     },
51069
51070     /**
51071      * Returns the currently selected field value or empty string if no value is set.
51072      * @return {String} value The selected value
51073      */
51074     getValue : function(){
51075         var dom = this.el.dom;
51076         this.value = dom.options[dom.selectedIndex].value;
51077         return this.value;
51078         
51079     },
51080
51081     /**
51082      * Clears any text/value currently set in the field
51083      */
51084     clearValue : function(){
51085         this.value = '';
51086         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51087         
51088     },
51089
51090     /**
51091      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51092      * will be displayed in the field.  If the value does not match the data value of an existing item,
51093      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51094      * Otherwise the field will be blank (although the value will still be set).
51095      * @param {String} value The value to match
51096      */
51097     setValue : function(v){
51098         var d = this.el.dom;
51099         for (var i =0; i < d.options.length;i++) {
51100             if (v == d.options[i].value) {
51101                 d.selectedIndex = i;
51102                 this.value = v;
51103                 return;
51104             }
51105         }
51106         this.clearValue();
51107     },
51108     /**
51109      * @property {Object} the last set data for the element
51110      */
51111     
51112     lastData : false,
51113     /**
51114      * Sets the value of the field based on a object which is related to the record format for the store.
51115      * @param {Object} value the value to set as. or false on reset?
51116      */
51117     setFromData : function(o){
51118         Roo.log('setfrom data?');
51119          
51120         
51121         
51122     },
51123     // private
51124     reset : function(){
51125         this.clearValue();
51126     },
51127     // private
51128     findRecord : function(prop, value){
51129         
51130         return false;
51131     
51132         var record;
51133         if(this.store.getCount() > 0){
51134             this.store.each(function(r){
51135                 if(r.data[prop] == value){
51136                     record = r;
51137                     return false;
51138                 }
51139                 return true;
51140             });
51141         }
51142         return record;
51143     },
51144     
51145     getName: function()
51146     {
51147         // returns hidden if it's set..
51148         if (!this.rendered) {return ''};
51149         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51150         
51151     },
51152      
51153
51154     
51155
51156     // private
51157     onEmptyResults : function(){
51158         Roo.log('empty results');
51159         //this.collapse();
51160     },
51161
51162     /**
51163      * Returns true if the dropdown list is expanded, else false.
51164      */
51165     isExpanded : function(){
51166         return false;
51167     },
51168
51169     /**
51170      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51171      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51172      * @param {String} value The data value of the item to select
51173      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51174      * selected item if it is not currently in view (defaults to true)
51175      * @return {Boolean} True if the value matched an item in the list, else false
51176      */
51177     selectByValue : function(v, scrollIntoView){
51178         Roo.log('select By Value');
51179         return false;
51180     
51181         if(v !== undefined && v !== null){
51182             var r = this.findRecord(this.valueField || this.displayField, v);
51183             if(r){
51184                 this.select(this.store.indexOf(r), scrollIntoView);
51185                 return true;
51186             }
51187         }
51188         return false;
51189     },
51190
51191     /**
51192      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51193      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51194      * @param {Number} index The zero-based index of the list item to select
51195      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51196      * selected item if it is not currently in view (defaults to true)
51197      */
51198     select : function(index, scrollIntoView){
51199         Roo.log('select ');
51200         return  ;
51201         
51202         this.selectedIndex = index;
51203         this.view.select(index);
51204         if(scrollIntoView !== false){
51205             var el = this.view.getNode(index);
51206             if(el){
51207                 this.innerList.scrollChildIntoView(el, false);
51208             }
51209         }
51210     },
51211
51212       
51213
51214     // private
51215     validateBlur : function(){
51216         
51217         return;
51218         
51219     },
51220
51221     // private
51222     initQuery : function(){
51223         this.doQuery(this.getRawValue());
51224     },
51225
51226     // private
51227     doForce : function(){
51228         if(this.el.dom.value.length > 0){
51229             this.el.dom.value =
51230                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51231              
51232         }
51233     },
51234
51235     /**
51236      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51237      * query allowing the query action to be canceled if needed.
51238      * @param {String} query The SQL query to execute
51239      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51240      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51241      * saved in the current store (defaults to false)
51242      */
51243     doQuery : function(q, forceAll){
51244         
51245         Roo.log('doQuery?');
51246         if(q === undefined || q === null){
51247             q = '';
51248         }
51249         var qe = {
51250             query: q,
51251             forceAll: forceAll,
51252             combo: this,
51253             cancel:false
51254         };
51255         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51256             return false;
51257         }
51258         q = qe.query;
51259         forceAll = qe.forceAll;
51260         if(forceAll === true || (q.length >= this.minChars)){
51261             if(this.lastQuery != q || this.alwaysQuery){
51262                 this.lastQuery = q;
51263                 if(this.mode == 'local'){
51264                     this.selectedIndex = -1;
51265                     if(forceAll){
51266                         this.store.clearFilter();
51267                     }else{
51268                         this.store.filter(this.displayField, q);
51269                     }
51270                     this.onLoad();
51271                 }else{
51272                     this.store.baseParams[this.queryParam] = q;
51273                     this.store.load({
51274                         params: this.getParams(q)
51275                     });
51276                     this.expand();
51277                 }
51278             }else{
51279                 this.selectedIndex = -1;
51280                 this.onLoad();   
51281             }
51282         }
51283     },
51284
51285     // private
51286     getParams : function(q){
51287         var p = {};
51288         //p[this.queryParam] = q;
51289         if(this.pageSize){
51290             p.start = 0;
51291             p.limit = this.pageSize;
51292         }
51293         return p;
51294     },
51295
51296     /**
51297      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51298      */
51299     collapse : function(){
51300         
51301     },
51302
51303     // private
51304     collapseIf : function(e){
51305         
51306     },
51307
51308     /**
51309      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51310      */
51311     expand : function(){
51312         
51313     } ,
51314
51315     // private
51316      
51317
51318     /** 
51319     * @cfg {Boolean} grow 
51320     * @hide 
51321     */
51322     /** 
51323     * @cfg {Number} growMin 
51324     * @hide 
51325     */
51326     /** 
51327     * @cfg {Number} growMax 
51328     * @hide 
51329     */
51330     /**
51331      * @hide
51332      * @method autoSize
51333      */
51334     
51335     setWidth : function()
51336     {
51337         
51338     },
51339     getResizeEl : function(){
51340         return this.el;
51341     }
51342 });//<script type="text/javasscript">
51343  
51344
51345 /**
51346  * @class Roo.DDView
51347  * A DnD enabled version of Roo.View.
51348  * @param {Element/String} container The Element in which to create the View.
51349  * @param {String} tpl The template string used to create the markup for each element of the View
51350  * @param {Object} config The configuration properties. These include all the config options of
51351  * {@link Roo.View} plus some specific to this class.<br>
51352  * <p>
51353  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51354  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51355  * <p>
51356  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51357 .x-view-drag-insert-above {
51358         border-top:1px dotted #3366cc;
51359 }
51360 .x-view-drag-insert-below {
51361         border-bottom:1px dotted #3366cc;
51362 }
51363 </code></pre>
51364  * 
51365  */
51366  
51367 Roo.DDView = function(container, tpl, config) {
51368     Roo.DDView.superclass.constructor.apply(this, arguments);
51369     this.getEl().setStyle("outline", "0px none");
51370     this.getEl().unselectable();
51371     if (this.dragGroup) {
51372                 this.setDraggable(this.dragGroup.split(","));
51373     }
51374     if (this.dropGroup) {
51375                 this.setDroppable(this.dropGroup.split(","));
51376     }
51377     if (this.deletable) {
51378         this.setDeletable();
51379     }
51380     this.isDirtyFlag = false;
51381         this.addEvents({
51382                 "drop" : true
51383         });
51384 };
51385
51386 Roo.extend(Roo.DDView, Roo.View, {
51387 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51388 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51389 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51390 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51391
51392         isFormField: true,
51393
51394         reset: Roo.emptyFn,
51395         
51396         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51397
51398         validate: function() {
51399                 return true;
51400         },
51401         
51402         destroy: function() {
51403                 this.purgeListeners();
51404                 this.getEl.removeAllListeners();
51405                 this.getEl().remove();
51406                 if (this.dragZone) {
51407                         if (this.dragZone.destroy) {
51408                                 this.dragZone.destroy();
51409                         }
51410                 }
51411                 if (this.dropZone) {
51412                         if (this.dropZone.destroy) {
51413                                 this.dropZone.destroy();
51414                         }
51415                 }
51416         },
51417
51418 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51419         getName: function() {
51420                 return this.name;
51421         },
51422
51423 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51424         setValue: function(v) {
51425                 if (!this.store) {
51426                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51427                 }
51428                 var data = {};
51429                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51430                 this.store.proxy = new Roo.data.MemoryProxy(data);
51431                 this.store.load();
51432         },
51433
51434 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51435         getValue: function() {
51436                 var result = '(';
51437                 this.store.each(function(rec) {
51438                         result += rec.id + ',';
51439                 });
51440                 return result.substr(0, result.length - 1) + ')';
51441         },
51442         
51443         getIds: function() {
51444                 var i = 0, result = new Array(this.store.getCount());
51445                 this.store.each(function(rec) {
51446                         result[i++] = rec.id;
51447                 });
51448                 return result;
51449         },
51450         
51451         isDirty: function() {
51452                 return this.isDirtyFlag;
51453         },
51454
51455 /**
51456  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51457  *      whole Element becomes the target, and this causes the drop gesture to append.
51458  */
51459     getTargetFromEvent : function(e) {
51460                 var target = e.getTarget();
51461                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51462                 target = target.parentNode;
51463                 }
51464                 if (!target) {
51465                         target = this.el.dom.lastChild || this.el.dom;
51466                 }
51467                 return target;
51468     },
51469
51470 /**
51471  *      Create the drag data which consists of an object which has the property "ddel" as
51472  *      the drag proxy element. 
51473  */
51474     getDragData : function(e) {
51475         var target = this.findItemFromChild(e.getTarget());
51476                 if(target) {
51477                         this.handleSelection(e);
51478                         var selNodes = this.getSelectedNodes();
51479             var dragData = {
51480                 source: this,
51481                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51482                 nodes: selNodes,
51483                 records: []
51484                         };
51485                         var selectedIndices = this.getSelectedIndexes();
51486                         for (var i = 0; i < selectedIndices.length; i++) {
51487                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51488                         }
51489                         if (selNodes.length == 1) {
51490                                 dragData.ddel = target.cloneNode(true); // the div element
51491                         } else {
51492                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51493                                 div.className = 'multi-proxy';
51494                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51495                                         div.appendChild(selNodes[i].cloneNode(true));
51496                                 }
51497                                 dragData.ddel = div;
51498                         }
51499             //console.log(dragData)
51500             //console.log(dragData.ddel.innerHTML)
51501                         return dragData;
51502                 }
51503         //console.log('nodragData')
51504                 return false;
51505     },
51506     
51507 /**     Specify to which ddGroup items in this DDView may be dragged. */
51508     setDraggable: function(ddGroup) {
51509         if (ddGroup instanceof Array) {
51510                 Roo.each(ddGroup, this.setDraggable, this);
51511                 return;
51512         }
51513         if (this.dragZone) {
51514                 this.dragZone.addToGroup(ddGroup);
51515         } else {
51516                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51517                                 containerScroll: true,
51518                                 ddGroup: ddGroup 
51519
51520                         });
51521 //                      Draggability implies selection. DragZone's mousedown selects the element.
51522                         if (!this.multiSelect) { this.singleSelect = true; }
51523
51524 //                      Wire the DragZone's handlers up to methods in *this*
51525                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51526                 }
51527     },
51528
51529 /**     Specify from which ddGroup this DDView accepts drops. */
51530     setDroppable: function(ddGroup) {
51531         if (ddGroup instanceof Array) {
51532                 Roo.each(ddGroup, this.setDroppable, this);
51533                 return;
51534         }
51535         if (this.dropZone) {
51536                 this.dropZone.addToGroup(ddGroup);
51537         } else {
51538                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51539                                 containerScroll: true,
51540                                 ddGroup: ddGroup
51541                         });
51542
51543 //                      Wire the DropZone's handlers up to methods in *this*
51544                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51545                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51546                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51547                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51548                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51549                 }
51550     },
51551
51552 /**     Decide whether to drop above or below a View node. */
51553     getDropPoint : function(e, n, dd){
51554         if (n == this.el.dom) { return "above"; }
51555                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51556                 var c = t + (b - t) / 2;
51557                 var y = Roo.lib.Event.getPageY(e);
51558                 if(y <= c) {
51559                         return "above";
51560                 }else{
51561                         return "below";
51562                 }
51563     },
51564
51565     onNodeEnter : function(n, dd, e, data){
51566                 return false;
51567     },
51568     
51569     onNodeOver : function(n, dd, e, data){
51570                 var pt = this.getDropPoint(e, n, dd);
51571                 // set the insert point style on the target node
51572                 var dragElClass = this.dropNotAllowed;
51573                 if (pt) {
51574                         var targetElClass;
51575                         if (pt == "above"){
51576                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51577                                 targetElClass = "x-view-drag-insert-above";
51578                         } else {
51579                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51580                                 targetElClass = "x-view-drag-insert-below";
51581                         }
51582                         if (this.lastInsertClass != targetElClass){
51583                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51584                                 this.lastInsertClass = targetElClass;
51585                         }
51586                 }
51587                 return dragElClass;
51588         },
51589
51590     onNodeOut : function(n, dd, e, data){
51591                 this.removeDropIndicators(n);
51592     },
51593
51594     onNodeDrop : function(n, dd, e, data){
51595         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51596                 return false;
51597         }
51598         var pt = this.getDropPoint(e, n, dd);
51599                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51600                 if (pt == "below") { insertAt++; }
51601                 for (var i = 0; i < data.records.length; i++) {
51602                         var r = data.records[i];
51603                         var dup = this.store.getById(r.id);
51604                         if (dup && (dd != this.dragZone)) {
51605                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51606                         } else {
51607                                 if (data.copy) {
51608                                         this.store.insert(insertAt++, r.copy());
51609                                 } else {
51610                                         data.source.isDirtyFlag = true;
51611                                         r.store.remove(r);
51612                                         this.store.insert(insertAt++, r);
51613                                 }
51614                                 this.isDirtyFlag = true;
51615                         }
51616                 }
51617                 this.dragZone.cachedTarget = null;
51618                 return true;
51619     },
51620
51621     removeDropIndicators : function(n){
51622                 if(n){
51623                         Roo.fly(n).removeClass([
51624                                 "x-view-drag-insert-above",
51625                                 "x-view-drag-insert-below"]);
51626                         this.lastInsertClass = "_noclass";
51627                 }
51628     },
51629
51630 /**
51631  *      Utility method. Add a delete option to the DDView's context menu.
51632  *      @param {String} imageUrl The URL of the "delete" icon image.
51633  */
51634         setDeletable: function(imageUrl) {
51635                 if (!this.singleSelect && !this.multiSelect) {
51636                         this.singleSelect = true;
51637                 }
51638                 var c = this.getContextMenu();
51639                 this.contextMenu.on("itemclick", function(item) {
51640                         switch (item.id) {
51641                                 case "delete":
51642                                         this.remove(this.getSelectedIndexes());
51643                                         break;
51644                         }
51645                 }, this);
51646                 this.contextMenu.add({
51647                         icon: imageUrl,
51648                         id: "delete",
51649                         text: 'Delete'
51650                 });
51651         },
51652         
51653 /**     Return the context menu for this DDView. */
51654         getContextMenu: function() {
51655                 if (!this.contextMenu) {
51656 //                      Create the View's context menu
51657                         this.contextMenu = new Roo.menu.Menu({
51658                                 id: this.id + "-contextmenu"
51659                         });
51660                         this.el.on("contextmenu", this.showContextMenu, this);
51661                 }
51662                 return this.contextMenu;
51663         },
51664         
51665         disableContextMenu: function() {
51666                 if (this.contextMenu) {
51667                         this.el.un("contextmenu", this.showContextMenu, this);
51668                 }
51669         },
51670
51671         showContextMenu: function(e, item) {
51672         item = this.findItemFromChild(e.getTarget());
51673                 if (item) {
51674                         e.stopEvent();
51675                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51676                         this.contextMenu.showAt(e.getXY());
51677             }
51678     },
51679
51680 /**
51681  *      Remove {@link Roo.data.Record}s at the specified indices.
51682  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51683  */
51684     remove: function(selectedIndices) {
51685                 selectedIndices = [].concat(selectedIndices);
51686                 for (var i = 0; i < selectedIndices.length; i++) {
51687                         var rec = this.store.getAt(selectedIndices[i]);
51688                         this.store.remove(rec);
51689                 }
51690     },
51691
51692 /**
51693  *      Double click fires the event, but also, if this is draggable, and there is only one other
51694  *      related DropZone, it transfers the selected node.
51695  */
51696     onDblClick : function(e){
51697         var item = this.findItemFromChild(e.getTarget());
51698         if(item){
51699             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51700                 return false;
51701             }
51702             if (this.dragGroup) {
51703                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51704                     while (targets.indexOf(this.dropZone) > -1) {
51705                             targets.remove(this.dropZone);
51706                                 }
51707                     if (targets.length == 1) {
51708                                         this.dragZone.cachedTarget = null;
51709                         var el = Roo.get(targets[0].getEl());
51710                         var box = el.getBox(true);
51711                         targets[0].onNodeDrop(el.dom, {
51712                                 target: el.dom,
51713                                 xy: [box.x, box.y + box.height - 1]
51714                         }, null, this.getDragData(e));
51715                     }
51716                 }
51717         }
51718     },
51719     
51720     handleSelection: function(e) {
51721                 this.dragZone.cachedTarget = null;
51722         var item = this.findItemFromChild(e.getTarget());
51723         if (!item) {
51724                 this.clearSelections(true);
51725                 return;
51726         }
51727                 if (item && (this.multiSelect || this.singleSelect)){
51728                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51729                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51730                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51731                                 this.unselect(item);
51732                         } else {
51733                                 this.select(item, this.multiSelect && e.ctrlKey);
51734                                 this.lastSelection = item;
51735                         }
51736                 }
51737     },
51738
51739     onItemClick : function(item, index, e){
51740                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51741                         return false;
51742                 }
51743                 return true;
51744     },
51745
51746     unselect : function(nodeInfo, suppressEvent){
51747                 var node = this.getNode(nodeInfo);
51748                 if(node && this.isSelected(node)){
51749                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51750                                 Roo.fly(node).removeClass(this.selectedClass);
51751                                 this.selections.remove(node);
51752                                 if(!suppressEvent){
51753                                         this.fireEvent("selectionchange", this, this.selections);
51754                                 }
51755                         }
51756                 }
51757     }
51758 });
51759 /*
51760  * Based on:
51761  * Ext JS Library 1.1.1
51762  * Copyright(c) 2006-2007, Ext JS, LLC.
51763  *
51764  * Originally Released Under LGPL - original licence link has changed is not relivant.
51765  *
51766  * Fork - LGPL
51767  * <script type="text/javascript">
51768  */
51769  
51770 /**
51771  * @class Roo.LayoutManager
51772  * @extends Roo.util.Observable
51773  * Base class for layout managers.
51774  */
51775 Roo.LayoutManager = function(container, config){
51776     Roo.LayoutManager.superclass.constructor.call(this);
51777     this.el = Roo.get(container);
51778     // ie scrollbar fix
51779     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51780         document.body.scroll = "no";
51781     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51782         this.el.position('relative');
51783     }
51784     this.id = this.el.id;
51785     this.el.addClass("x-layout-container");
51786     /** false to disable window resize monitoring @type Boolean */
51787     this.monitorWindowResize = true;
51788     this.regions = {};
51789     this.addEvents({
51790         /**
51791          * @event layout
51792          * Fires when a layout is performed. 
51793          * @param {Roo.LayoutManager} this
51794          */
51795         "layout" : true,
51796         /**
51797          * @event regionresized
51798          * Fires when the user resizes a region. 
51799          * @param {Roo.LayoutRegion} region The resized region
51800          * @param {Number} newSize The new size (width for east/west, height for north/south)
51801          */
51802         "regionresized" : true,
51803         /**
51804          * @event regioncollapsed
51805          * Fires when a region is collapsed. 
51806          * @param {Roo.LayoutRegion} region The collapsed region
51807          */
51808         "regioncollapsed" : true,
51809         /**
51810          * @event regionexpanded
51811          * Fires when a region is expanded.  
51812          * @param {Roo.LayoutRegion} region The expanded region
51813          */
51814         "regionexpanded" : true
51815     });
51816     this.updating = false;
51817     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51818 };
51819
51820 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51821     /**
51822      * Returns true if this layout is currently being updated
51823      * @return {Boolean}
51824      */
51825     isUpdating : function(){
51826         return this.updating; 
51827     },
51828     
51829     /**
51830      * Suspend the LayoutManager from doing auto-layouts while
51831      * making multiple add or remove calls
51832      */
51833     beginUpdate : function(){
51834         this.updating = true;    
51835     },
51836     
51837     /**
51838      * Restore auto-layouts and optionally disable the manager from performing a layout
51839      * @param {Boolean} noLayout true to disable a layout update 
51840      */
51841     endUpdate : function(noLayout){
51842         this.updating = false;
51843         if(!noLayout){
51844             this.layout();
51845         }    
51846     },
51847     
51848     layout: function(){
51849         
51850     },
51851     
51852     onRegionResized : function(region, newSize){
51853         this.fireEvent("regionresized", region, newSize);
51854         this.layout();
51855     },
51856     
51857     onRegionCollapsed : function(region){
51858         this.fireEvent("regioncollapsed", region);
51859     },
51860     
51861     onRegionExpanded : function(region){
51862         this.fireEvent("regionexpanded", region);
51863     },
51864         
51865     /**
51866      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51867      * performs box-model adjustments.
51868      * @return {Object} The size as an object {width: (the width), height: (the height)}
51869      */
51870     getViewSize : function(){
51871         var size;
51872         if(this.el.dom != document.body){
51873             size = this.el.getSize();
51874         }else{
51875             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51876         }
51877         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51878         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51879         return size;
51880     },
51881     
51882     /**
51883      * Returns the Element this layout is bound to.
51884      * @return {Roo.Element}
51885      */
51886     getEl : function(){
51887         return this.el;
51888     },
51889     
51890     /**
51891      * Returns the specified region.
51892      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51893      * @return {Roo.LayoutRegion}
51894      */
51895     getRegion : function(target){
51896         return this.regions[target.toLowerCase()];
51897     },
51898     
51899     onWindowResize : function(){
51900         if(this.monitorWindowResize){
51901             this.layout();
51902         }
51903     }
51904 });/*
51905  * Based on:
51906  * Ext JS Library 1.1.1
51907  * Copyright(c) 2006-2007, Ext JS, LLC.
51908  *
51909  * Originally Released Under LGPL - original licence link has changed is not relivant.
51910  *
51911  * Fork - LGPL
51912  * <script type="text/javascript">
51913  */
51914 /**
51915  * @class Roo.BorderLayout
51916  * @extends Roo.LayoutManager
51917  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51918  * please see: <br><br>
51919  * <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>
51920  * <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>
51921  * Example:
51922  <pre><code>
51923  var layout = new Roo.BorderLayout(document.body, {
51924     north: {
51925         initialSize: 25,
51926         titlebar: false
51927     },
51928     west: {
51929         split:true,
51930         initialSize: 200,
51931         minSize: 175,
51932         maxSize: 400,
51933         titlebar: true,
51934         collapsible: true
51935     },
51936     east: {
51937         split:true,
51938         initialSize: 202,
51939         minSize: 175,
51940         maxSize: 400,
51941         titlebar: true,
51942         collapsible: true
51943     },
51944     south: {
51945         split:true,
51946         initialSize: 100,
51947         minSize: 100,
51948         maxSize: 200,
51949         titlebar: true,
51950         collapsible: true
51951     },
51952     center: {
51953         titlebar: true,
51954         autoScroll:true,
51955         resizeTabs: true,
51956         minTabWidth: 50,
51957         preferredTabWidth: 150
51958     }
51959 });
51960
51961 // shorthand
51962 var CP = Roo.ContentPanel;
51963
51964 layout.beginUpdate();
51965 layout.add("north", new CP("north", "North"));
51966 layout.add("south", new CP("south", {title: "South", closable: true}));
51967 layout.add("west", new CP("west", {title: "West"}));
51968 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51969 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51970 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51971 layout.getRegion("center").showPanel("center1");
51972 layout.endUpdate();
51973 </code></pre>
51974
51975 <b>The container the layout is rendered into can be either the body element or any other element.
51976 If it is not the body element, the container needs to either be an absolute positioned element,
51977 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51978 the container size if it is not the body element.</b>
51979
51980 * @constructor
51981 * Create a new BorderLayout
51982 * @param {String/HTMLElement/Element} container The container this layout is bound to
51983 * @param {Object} config Configuration options
51984  */
51985 Roo.BorderLayout = function(container, config){
51986     config = config || {};
51987     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51988     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51989     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51990         var target = this.factory.validRegions[i];
51991         if(config[target]){
51992             this.addRegion(target, config[target]);
51993         }
51994     }
51995 };
51996
51997 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51998     /**
51999      * Creates and adds a new region if it doesn't already exist.
52000      * @param {String} target The target region key (north, south, east, west or center).
52001      * @param {Object} config The regions config object
52002      * @return {BorderLayoutRegion} The new region
52003      */
52004     addRegion : function(target, config){
52005         if(!this.regions[target]){
52006             var r = this.factory.create(target, this, config);
52007             this.bindRegion(target, r);
52008         }
52009         return this.regions[target];
52010     },
52011
52012     // private (kinda)
52013     bindRegion : function(name, r){
52014         this.regions[name] = r;
52015         r.on("visibilitychange", this.layout, this);
52016         r.on("paneladded", this.layout, this);
52017         r.on("panelremoved", this.layout, this);
52018         r.on("invalidated", this.layout, this);
52019         r.on("resized", this.onRegionResized, this);
52020         r.on("collapsed", this.onRegionCollapsed, this);
52021         r.on("expanded", this.onRegionExpanded, this);
52022     },
52023
52024     /**
52025      * Performs a layout update.
52026      */
52027     layout : function(){
52028         if(this.updating) {
52029             return;
52030         }
52031         var size = this.getViewSize();
52032         var w = size.width;
52033         var h = size.height;
52034         var centerW = w;
52035         var centerH = h;
52036         var centerY = 0;
52037         var centerX = 0;
52038         //var x = 0, y = 0;
52039
52040         var rs = this.regions;
52041         var north = rs["north"];
52042         var south = rs["south"]; 
52043         var west = rs["west"];
52044         var east = rs["east"];
52045         var center = rs["center"];
52046         //if(this.hideOnLayout){ // not supported anymore
52047             //c.el.setStyle("display", "none");
52048         //}
52049         if(north && north.isVisible()){
52050             var b = north.getBox();
52051             var m = north.getMargins();
52052             b.width = w - (m.left+m.right);
52053             b.x = m.left;
52054             b.y = m.top;
52055             centerY = b.height + b.y + m.bottom;
52056             centerH -= centerY;
52057             north.updateBox(this.safeBox(b));
52058         }
52059         if(south && south.isVisible()){
52060             var b = south.getBox();
52061             var m = south.getMargins();
52062             b.width = w - (m.left+m.right);
52063             b.x = m.left;
52064             var totalHeight = (b.height + m.top + m.bottom);
52065             b.y = h - totalHeight + m.top;
52066             centerH -= totalHeight;
52067             south.updateBox(this.safeBox(b));
52068         }
52069         if(west && west.isVisible()){
52070             var b = west.getBox();
52071             var m = west.getMargins();
52072             b.height = centerH - (m.top+m.bottom);
52073             b.x = m.left;
52074             b.y = centerY + m.top;
52075             var totalWidth = (b.width + m.left + m.right);
52076             centerX += totalWidth;
52077             centerW -= totalWidth;
52078             west.updateBox(this.safeBox(b));
52079         }
52080         if(east && east.isVisible()){
52081             var b = east.getBox();
52082             var m = east.getMargins();
52083             b.height = centerH - (m.top+m.bottom);
52084             var totalWidth = (b.width + m.left + m.right);
52085             b.x = w - totalWidth + m.left;
52086             b.y = centerY + m.top;
52087             centerW -= totalWidth;
52088             east.updateBox(this.safeBox(b));
52089         }
52090         if(center){
52091             var m = center.getMargins();
52092             var centerBox = {
52093                 x: centerX + m.left,
52094                 y: centerY + m.top,
52095                 width: centerW - (m.left+m.right),
52096                 height: centerH - (m.top+m.bottom)
52097             };
52098             //if(this.hideOnLayout){
52099                 //center.el.setStyle("display", "block");
52100             //}
52101             center.updateBox(this.safeBox(centerBox));
52102         }
52103         this.el.repaint();
52104         this.fireEvent("layout", this);
52105     },
52106
52107     // private
52108     safeBox : function(box){
52109         box.width = Math.max(0, box.width);
52110         box.height = Math.max(0, box.height);
52111         return box;
52112     },
52113
52114     /**
52115      * Adds a ContentPanel (or subclass) to this layout.
52116      * @param {String} target The target region key (north, south, east, west or center).
52117      * @param {Roo.ContentPanel} panel The panel to add
52118      * @return {Roo.ContentPanel} The added panel
52119      */
52120     add : function(target, panel){
52121          
52122         target = target.toLowerCase();
52123         return this.regions[target].add(panel);
52124     },
52125
52126     /**
52127      * Remove a ContentPanel (or subclass) to this layout.
52128      * @param {String} target The target region key (north, south, east, west or center).
52129      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52130      * @return {Roo.ContentPanel} The removed panel
52131      */
52132     remove : function(target, panel){
52133         target = target.toLowerCase();
52134         return this.regions[target].remove(panel);
52135     },
52136
52137     /**
52138      * Searches all regions for a panel with the specified id
52139      * @param {String} panelId
52140      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52141      */
52142     findPanel : function(panelId){
52143         var rs = this.regions;
52144         for(var target in rs){
52145             if(typeof rs[target] != "function"){
52146                 var p = rs[target].getPanel(panelId);
52147                 if(p){
52148                     return p;
52149                 }
52150             }
52151         }
52152         return null;
52153     },
52154
52155     /**
52156      * Searches all regions for a panel with the specified id and activates (shows) it.
52157      * @param {String/ContentPanel} panelId The panels id or the panel itself
52158      * @return {Roo.ContentPanel} The shown panel or null
52159      */
52160     showPanel : function(panelId) {
52161       var rs = this.regions;
52162       for(var target in rs){
52163          var r = rs[target];
52164          if(typeof r != "function"){
52165             if(r.hasPanel(panelId)){
52166                return r.showPanel(panelId);
52167             }
52168          }
52169       }
52170       return null;
52171    },
52172
52173    /**
52174      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52175      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52176      */
52177     restoreState : function(provider){
52178         if(!provider){
52179             provider = Roo.state.Manager;
52180         }
52181         var sm = new Roo.LayoutStateManager();
52182         sm.init(this, provider);
52183     },
52184
52185     /**
52186      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52187      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52188      * a valid ContentPanel config object.  Example:
52189      * <pre><code>
52190 // Create the main layout
52191 var layout = new Roo.BorderLayout('main-ct', {
52192     west: {
52193         split:true,
52194         minSize: 175,
52195         titlebar: true
52196     },
52197     center: {
52198         title:'Components'
52199     }
52200 }, 'main-ct');
52201
52202 // Create and add multiple ContentPanels at once via configs
52203 layout.batchAdd({
52204    west: {
52205        id: 'source-files',
52206        autoCreate:true,
52207        title:'Ext Source Files',
52208        autoScroll:true,
52209        fitToFrame:true
52210    },
52211    center : {
52212        el: cview,
52213        autoScroll:true,
52214        fitToFrame:true,
52215        toolbar: tb,
52216        resizeEl:'cbody'
52217    }
52218 });
52219 </code></pre>
52220      * @param {Object} regions An object containing ContentPanel configs by region name
52221      */
52222     batchAdd : function(regions){
52223         this.beginUpdate();
52224         for(var rname in regions){
52225             var lr = this.regions[rname];
52226             if(lr){
52227                 this.addTypedPanels(lr, regions[rname]);
52228             }
52229         }
52230         this.endUpdate();
52231     },
52232
52233     // private
52234     addTypedPanels : function(lr, ps){
52235         if(typeof ps == 'string'){
52236             lr.add(new Roo.ContentPanel(ps));
52237         }
52238         else if(ps instanceof Array){
52239             for(var i =0, len = ps.length; i < len; i++){
52240                 this.addTypedPanels(lr, ps[i]);
52241             }
52242         }
52243         else if(!ps.events){ // raw config?
52244             var el = ps.el;
52245             delete ps.el; // prevent conflict
52246             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52247         }
52248         else {  // panel object assumed!
52249             lr.add(ps);
52250         }
52251     },
52252     /**
52253      * Adds a xtype elements to the layout.
52254      * <pre><code>
52255
52256 layout.addxtype({
52257        xtype : 'ContentPanel',
52258        region: 'west',
52259        items: [ .... ]
52260    }
52261 );
52262
52263 layout.addxtype({
52264         xtype : 'NestedLayoutPanel',
52265         region: 'west',
52266         layout: {
52267            center: { },
52268            west: { }   
52269         },
52270         items : [ ... list of content panels or nested layout panels.. ]
52271    }
52272 );
52273 </code></pre>
52274      * @param {Object} cfg Xtype definition of item to add.
52275      */
52276     addxtype : function(cfg)
52277     {
52278         // basically accepts a pannel...
52279         // can accept a layout region..!?!?
52280         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52281         
52282         if (!cfg.xtype.match(/Panel$/)) {
52283             return false;
52284         }
52285         var ret = false;
52286         
52287         if (typeof(cfg.region) == 'undefined') {
52288             Roo.log("Failed to add Panel, region was not set");
52289             Roo.log(cfg);
52290             return false;
52291         }
52292         var region = cfg.region;
52293         delete cfg.region;
52294         
52295           
52296         var xitems = [];
52297         if (cfg.items) {
52298             xitems = cfg.items;
52299             delete cfg.items;
52300         }
52301         var nb = false;
52302         
52303         switch(cfg.xtype) 
52304         {
52305             case 'ContentPanel':  // ContentPanel (el, cfg)
52306             case 'ScrollPanel':  // ContentPanel (el, cfg)
52307             case 'ViewPanel': 
52308                 if(cfg.autoCreate) {
52309                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52310                 } else {
52311                     var el = this.el.createChild();
52312                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52313                 }
52314                 
52315                 this.add(region, ret);
52316                 break;
52317             
52318             
52319             case 'TreePanel': // our new panel!
52320                 cfg.el = this.el.createChild();
52321                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52322                 this.add(region, ret);
52323                 break;
52324             
52325             case 'NestedLayoutPanel': 
52326                 // create a new Layout (which is  a Border Layout...
52327                 var el = this.el.createChild();
52328                 var clayout = cfg.layout;
52329                 delete cfg.layout;
52330                 clayout.items   = clayout.items  || [];
52331                 // replace this exitems with the clayout ones..
52332                 xitems = clayout.items;
52333                  
52334                 
52335                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52336                     cfg.background = false;
52337                 }
52338                 var layout = new Roo.BorderLayout(el, clayout);
52339                 
52340                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52341                 //console.log('adding nested layout panel '  + cfg.toSource());
52342                 this.add(region, ret);
52343                 nb = {}; /// find first...
52344                 break;
52345                 
52346             case 'GridPanel': 
52347             
52348                 // needs grid and region
52349                 
52350                 //var el = this.getRegion(region).el.createChild();
52351                 var el = this.el.createChild();
52352                 // create the grid first...
52353                 
52354                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52355                 delete cfg.grid;
52356                 if (region == 'center' && this.active ) {
52357                     cfg.background = false;
52358                 }
52359                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52360                 
52361                 this.add(region, ret);
52362                 if (cfg.background) {
52363                     ret.on('activate', function(gp) {
52364                         if (!gp.grid.rendered) {
52365                             gp.grid.render();
52366                         }
52367                     });
52368                 } else {
52369                     grid.render();
52370                 }
52371                 break;
52372            
52373            
52374            
52375                 
52376                 
52377                 
52378             default:
52379                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52380                     
52381                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52382                     this.add(region, ret);
52383                 } else {
52384                 
52385                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52386                     return null;
52387                 }
52388                 
52389              // GridPanel (grid, cfg)
52390             
52391         }
52392         this.beginUpdate();
52393         // add children..
52394         var region = '';
52395         var abn = {};
52396         Roo.each(xitems, function(i)  {
52397             region = nb && i.region ? i.region : false;
52398             
52399             var add = ret.addxtype(i);
52400            
52401             if (region) {
52402                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52403                 if (!i.background) {
52404                     abn[region] = nb[region] ;
52405                 }
52406             }
52407             
52408         });
52409         this.endUpdate();
52410
52411         // make the last non-background panel active..
52412         //if (nb) { Roo.log(abn); }
52413         if (nb) {
52414             
52415             for(var r in abn) {
52416                 region = this.getRegion(r);
52417                 if (region) {
52418                     // tried using nb[r], but it does not work..
52419                      
52420                     region.showPanel(abn[r]);
52421                    
52422                 }
52423             }
52424         }
52425         return ret;
52426         
52427     }
52428 });
52429
52430 /**
52431  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52432  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52433  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52434  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52435  * <pre><code>
52436 // shorthand
52437 var CP = Roo.ContentPanel;
52438
52439 var layout = Roo.BorderLayout.create({
52440     north: {
52441         initialSize: 25,
52442         titlebar: false,
52443         panels: [new CP("north", "North")]
52444     },
52445     west: {
52446         split:true,
52447         initialSize: 200,
52448         minSize: 175,
52449         maxSize: 400,
52450         titlebar: true,
52451         collapsible: true,
52452         panels: [new CP("west", {title: "West"})]
52453     },
52454     east: {
52455         split:true,
52456         initialSize: 202,
52457         minSize: 175,
52458         maxSize: 400,
52459         titlebar: true,
52460         collapsible: true,
52461         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52462     },
52463     south: {
52464         split:true,
52465         initialSize: 100,
52466         minSize: 100,
52467         maxSize: 200,
52468         titlebar: true,
52469         collapsible: true,
52470         panels: [new CP("south", {title: "South", closable: true})]
52471     },
52472     center: {
52473         titlebar: true,
52474         autoScroll:true,
52475         resizeTabs: true,
52476         minTabWidth: 50,
52477         preferredTabWidth: 150,
52478         panels: [
52479             new CP("center1", {title: "Close Me", closable: true}),
52480             new CP("center2", {title: "Center Panel", closable: false})
52481         ]
52482     }
52483 }, document.body);
52484
52485 layout.getRegion("center").showPanel("center1");
52486 </code></pre>
52487  * @param config
52488  * @param targetEl
52489  */
52490 Roo.BorderLayout.create = function(config, targetEl){
52491     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52492     layout.beginUpdate();
52493     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52494     for(var j = 0, jlen = regions.length; j < jlen; j++){
52495         var lr = regions[j];
52496         if(layout.regions[lr] && config[lr].panels){
52497             var r = layout.regions[lr];
52498             var ps = config[lr].panels;
52499             layout.addTypedPanels(r, ps);
52500         }
52501     }
52502     layout.endUpdate();
52503     return layout;
52504 };
52505
52506 // private
52507 Roo.BorderLayout.RegionFactory = {
52508     // private
52509     validRegions : ["north","south","east","west","center"],
52510
52511     // private
52512     create : function(target, mgr, config){
52513         target = target.toLowerCase();
52514         if(config.lightweight || config.basic){
52515             return new Roo.BasicLayoutRegion(mgr, config, target);
52516         }
52517         switch(target){
52518             case "north":
52519                 return new Roo.NorthLayoutRegion(mgr, config);
52520             case "south":
52521                 return new Roo.SouthLayoutRegion(mgr, config);
52522             case "east":
52523                 return new Roo.EastLayoutRegion(mgr, config);
52524             case "west":
52525                 return new Roo.WestLayoutRegion(mgr, config);
52526             case "center":
52527                 return new Roo.CenterLayoutRegion(mgr, config);
52528         }
52529         throw 'Layout region "'+target+'" not supported.';
52530     }
52531 };/*
52532  * Based on:
52533  * Ext JS Library 1.1.1
52534  * Copyright(c) 2006-2007, Ext JS, LLC.
52535  *
52536  * Originally Released Under LGPL - original licence link has changed is not relivant.
52537  *
52538  * Fork - LGPL
52539  * <script type="text/javascript">
52540  */
52541  
52542 /**
52543  * @class Roo.BasicLayoutRegion
52544  * @extends Roo.util.Observable
52545  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52546  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52547  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52548  */
52549 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52550     this.mgr = mgr;
52551     this.position  = pos;
52552     this.events = {
52553         /**
52554          * @scope Roo.BasicLayoutRegion
52555          */
52556         
52557         /**
52558          * @event beforeremove
52559          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52560          * @param {Roo.LayoutRegion} this
52561          * @param {Roo.ContentPanel} panel The panel
52562          * @param {Object} e The cancel event object
52563          */
52564         "beforeremove" : true,
52565         /**
52566          * @event invalidated
52567          * Fires when the layout for this region is changed.
52568          * @param {Roo.LayoutRegion} this
52569          */
52570         "invalidated" : true,
52571         /**
52572          * @event visibilitychange
52573          * Fires when this region is shown or hidden 
52574          * @param {Roo.LayoutRegion} this
52575          * @param {Boolean} visibility true or false
52576          */
52577         "visibilitychange" : true,
52578         /**
52579          * @event paneladded
52580          * Fires when a panel is added. 
52581          * @param {Roo.LayoutRegion} this
52582          * @param {Roo.ContentPanel} panel The panel
52583          */
52584         "paneladded" : true,
52585         /**
52586          * @event panelremoved
52587          * Fires when a panel is removed. 
52588          * @param {Roo.LayoutRegion} this
52589          * @param {Roo.ContentPanel} panel The panel
52590          */
52591         "panelremoved" : true,
52592         /**
52593          * @event beforecollapse
52594          * Fires when this region before collapse.
52595          * @param {Roo.LayoutRegion} this
52596          */
52597         "beforecollapse" : true,
52598         /**
52599          * @event collapsed
52600          * Fires when this region is collapsed.
52601          * @param {Roo.LayoutRegion} this
52602          */
52603         "collapsed" : true,
52604         /**
52605          * @event expanded
52606          * Fires when this region is expanded.
52607          * @param {Roo.LayoutRegion} this
52608          */
52609         "expanded" : true,
52610         /**
52611          * @event slideshow
52612          * Fires when this region is slid into view.
52613          * @param {Roo.LayoutRegion} this
52614          */
52615         "slideshow" : true,
52616         /**
52617          * @event slidehide
52618          * Fires when this region slides out of view. 
52619          * @param {Roo.LayoutRegion} this
52620          */
52621         "slidehide" : true,
52622         /**
52623          * @event panelactivated
52624          * Fires when a panel is activated. 
52625          * @param {Roo.LayoutRegion} this
52626          * @param {Roo.ContentPanel} panel The activated panel
52627          */
52628         "panelactivated" : true,
52629         /**
52630          * @event resized
52631          * Fires when the user resizes this region. 
52632          * @param {Roo.LayoutRegion} this
52633          * @param {Number} newSize The new size (width for east/west, height for north/south)
52634          */
52635         "resized" : true
52636     };
52637     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52638     this.panels = new Roo.util.MixedCollection();
52639     this.panels.getKey = this.getPanelId.createDelegate(this);
52640     this.box = null;
52641     this.activePanel = null;
52642     // ensure listeners are added...
52643     
52644     if (config.listeners || config.events) {
52645         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52646             listeners : config.listeners || {},
52647             events : config.events || {}
52648         });
52649     }
52650     
52651     if(skipConfig !== true){
52652         this.applyConfig(config);
52653     }
52654 };
52655
52656 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52657     getPanelId : function(p){
52658         return p.getId();
52659     },
52660     
52661     applyConfig : function(config){
52662         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52663         this.config = config;
52664         
52665     },
52666     
52667     /**
52668      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52669      * the width, for horizontal (north, south) the height.
52670      * @param {Number} newSize The new width or height
52671      */
52672     resizeTo : function(newSize){
52673         var el = this.el ? this.el :
52674                  (this.activePanel ? this.activePanel.getEl() : null);
52675         if(el){
52676             switch(this.position){
52677                 case "east":
52678                 case "west":
52679                     el.setWidth(newSize);
52680                     this.fireEvent("resized", this, newSize);
52681                 break;
52682                 case "north":
52683                 case "south":
52684                     el.setHeight(newSize);
52685                     this.fireEvent("resized", this, newSize);
52686                 break;                
52687             }
52688         }
52689     },
52690     
52691     getBox : function(){
52692         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52693     },
52694     
52695     getMargins : function(){
52696         return this.margins;
52697     },
52698     
52699     updateBox : function(box){
52700         this.box = box;
52701         var el = this.activePanel.getEl();
52702         el.dom.style.left = box.x + "px";
52703         el.dom.style.top = box.y + "px";
52704         this.activePanel.setSize(box.width, box.height);
52705     },
52706     
52707     /**
52708      * Returns the container element for this region.
52709      * @return {Roo.Element}
52710      */
52711     getEl : function(){
52712         return this.activePanel;
52713     },
52714     
52715     /**
52716      * Returns true if this region is currently visible.
52717      * @return {Boolean}
52718      */
52719     isVisible : function(){
52720         return this.activePanel ? true : false;
52721     },
52722     
52723     setActivePanel : function(panel){
52724         panel = this.getPanel(panel);
52725         if(this.activePanel && this.activePanel != panel){
52726             this.activePanel.setActiveState(false);
52727             this.activePanel.getEl().setLeftTop(-10000,-10000);
52728         }
52729         this.activePanel = panel;
52730         panel.setActiveState(true);
52731         if(this.box){
52732             panel.setSize(this.box.width, this.box.height);
52733         }
52734         this.fireEvent("panelactivated", this, panel);
52735         this.fireEvent("invalidated");
52736     },
52737     
52738     /**
52739      * Show the specified panel.
52740      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52741      * @return {Roo.ContentPanel} The shown panel or null
52742      */
52743     showPanel : function(panel){
52744         if(panel = this.getPanel(panel)){
52745             this.setActivePanel(panel);
52746         }
52747         return panel;
52748     },
52749     
52750     /**
52751      * Get the active panel for this region.
52752      * @return {Roo.ContentPanel} The active panel or null
52753      */
52754     getActivePanel : function(){
52755         return this.activePanel;
52756     },
52757     
52758     /**
52759      * Add the passed ContentPanel(s)
52760      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52761      * @return {Roo.ContentPanel} The panel added (if only one was added)
52762      */
52763     add : function(panel){
52764         if(arguments.length > 1){
52765             for(var i = 0, len = arguments.length; i < len; i++) {
52766                 this.add(arguments[i]);
52767             }
52768             return null;
52769         }
52770         if(this.hasPanel(panel)){
52771             this.showPanel(panel);
52772             return panel;
52773         }
52774         var el = panel.getEl();
52775         if(el.dom.parentNode != this.mgr.el.dom){
52776             this.mgr.el.dom.appendChild(el.dom);
52777         }
52778         if(panel.setRegion){
52779             panel.setRegion(this);
52780         }
52781         this.panels.add(panel);
52782         el.setStyle("position", "absolute");
52783         if(!panel.background){
52784             this.setActivePanel(panel);
52785             if(this.config.initialSize && this.panels.getCount()==1){
52786                 this.resizeTo(this.config.initialSize);
52787             }
52788         }
52789         this.fireEvent("paneladded", this, panel);
52790         return panel;
52791     },
52792     
52793     /**
52794      * Returns true if the panel is in this region.
52795      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52796      * @return {Boolean}
52797      */
52798     hasPanel : function(panel){
52799         if(typeof panel == "object"){ // must be panel obj
52800             panel = panel.getId();
52801         }
52802         return this.getPanel(panel) ? true : false;
52803     },
52804     
52805     /**
52806      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52808      * @param {Boolean} preservePanel Overrides the config preservePanel option
52809      * @return {Roo.ContentPanel} The panel that was removed
52810      */
52811     remove : function(panel, preservePanel){
52812         panel = this.getPanel(panel);
52813         if(!panel){
52814             return null;
52815         }
52816         var e = {};
52817         this.fireEvent("beforeremove", this, panel, e);
52818         if(e.cancel === true){
52819             return null;
52820         }
52821         var panelId = panel.getId();
52822         this.panels.removeKey(panelId);
52823         return panel;
52824     },
52825     
52826     /**
52827      * Returns the panel specified or null if it's not in this region.
52828      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52829      * @return {Roo.ContentPanel}
52830      */
52831     getPanel : function(id){
52832         if(typeof id == "object"){ // must be panel obj
52833             return id;
52834         }
52835         return this.panels.get(id);
52836     },
52837     
52838     /**
52839      * Returns this regions position (north/south/east/west/center).
52840      * @return {String} 
52841      */
52842     getPosition: function(){
52843         return this.position;    
52844     }
52845 });/*
52846  * Based on:
52847  * Ext JS Library 1.1.1
52848  * Copyright(c) 2006-2007, Ext JS, LLC.
52849  *
52850  * Originally Released Under LGPL - original licence link has changed is not relivant.
52851  *
52852  * Fork - LGPL
52853  * <script type="text/javascript">
52854  */
52855  
52856 /**
52857  * @class Roo.LayoutRegion
52858  * @extends Roo.BasicLayoutRegion
52859  * This class represents a region in a layout manager.
52860  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52861  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52862  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52863  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52864  * @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})
52865  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52866  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52867  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52868  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52869  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52870  * @cfg {String}    title           The title for the region (overrides panel titles)
52871  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52872  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52873  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52874  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52875  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52876  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52877  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52878  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52879  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52880  * @cfg {Boolean}   showPin         True to show a pin button
52881  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52882  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52883  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52884  * @cfg {Number}    width           For East/West panels
52885  * @cfg {Number}    height          For North/South panels
52886  * @cfg {Boolean}   split           To show the splitter
52887  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52888  */
52889 Roo.LayoutRegion = function(mgr, config, pos){
52890     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52891     var dh = Roo.DomHelper;
52892     /** This region's container element 
52893     * @type Roo.Element */
52894     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52895     /** This region's title element 
52896     * @type Roo.Element */
52897
52898     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52899         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52900         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52901     ]}, true);
52902     this.titleEl.enableDisplayMode();
52903     /** This region's title text element 
52904     * @type HTMLElement */
52905     this.titleTextEl = this.titleEl.dom.firstChild;
52906     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52907     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52908     this.closeBtn.enableDisplayMode();
52909     this.closeBtn.on("click", this.closeClicked, this);
52910     this.closeBtn.hide();
52911
52912     this.createBody(config);
52913     this.visible = true;
52914     this.collapsed = false;
52915
52916     if(config.hideWhenEmpty){
52917         this.hide();
52918         this.on("paneladded", this.validateVisibility, this);
52919         this.on("panelremoved", this.validateVisibility, this);
52920     }
52921     this.applyConfig(config);
52922 };
52923
52924 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52925
52926     createBody : function(){
52927         /** This region's body element 
52928         * @type Roo.Element */
52929         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52930     },
52931
52932     applyConfig : function(c){
52933         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52934             var dh = Roo.DomHelper;
52935             if(c.titlebar !== false){
52936                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52937                 this.collapseBtn.on("click", this.collapse, this);
52938                 this.collapseBtn.enableDisplayMode();
52939
52940                 if(c.showPin === true || this.showPin){
52941                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52942                     this.stickBtn.enableDisplayMode();
52943                     this.stickBtn.on("click", this.expand, this);
52944                     this.stickBtn.hide();
52945                 }
52946             }
52947             /** This region's collapsed element
52948             * @type Roo.Element */
52949             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52950                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52951             ]}, true);
52952             if(c.floatable !== false){
52953                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52954                this.collapsedEl.on("click", this.collapseClick, this);
52955             }
52956
52957             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52958                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52959                    id: "message", unselectable: "on", style:{"float":"left"}});
52960                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52961              }
52962             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52963             this.expandBtn.on("click", this.expand, this);
52964         }
52965         if(this.collapseBtn){
52966             this.collapseBtn.setVisible(c.collapsible == true);
52967         }
52968         this.cmargins = c.cmargins || this.cmargins ||
52969                          (this.position == "west" || this.position == "east" ?
52970                              {top: 0, left: 2, right:2, bottom: 0} :
52971                              {top: 2, left: 0, right:0, bottom: 2});
52972         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52973         this.bottomTabs = c.tabPosition != "top";
52974         this.autoScroll = c.autoScroll || false;
52975         if(this.autoScroll){
52976             this.bodyEl.setStyle("overflow", "auto");
52977         }else{
52978             this.bodyEl.setStyle("overflow", "hidden");
52979         }
52980         //if(c.titlebar !== false){
52981             if((!c.titlebar && !c.title) || c.titlebar === false){
52982                 this.titleEl.hide();
52983             }else{
52984                 this.titleEl.show();
52985                 if(c.title){
52986                     this.titleTextEl.innerHTML = c.title;
52987                 }
52988             }
52989         //}
52990         this.duration = c.duration || .30;
52991         this.slideDuration = c.slideDuration || .45;
52992         this.config = c;
52993         if(c.collapsed){
52994             this.collapse(true);
52995         }
52996         if(c.hidden){
52997             this.hide();
52998         }
52999     },
53000     /**
53001      * Returns true if this region is currently visible.
53002      * @return {Boolean}
53003      */
53004     isVisible : function(){
53005         return this.visible;
53006     },
53007
53008     /**
53009      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53010      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53011      */
53012     setCollapsedTitle : function(title){
53013         title = title || "&#160;";
53014         if(this.collapsedTitleTextEl){
53015             this.collapsedTitleTextEl.innerHTML = title;
53016         }
53017     },
53018
53019     getBox : function(){
53020         var b;
53021         if(!this.collapsed){
53022             b = this.el.getBox(false, true);
53023         }else{
53024             b = this.collapsedEl.getBox(false, true);
53025         }
53026         return b;
53027     },
53028
53029     getMargins : function(){
53030         return this.collapsed ? this.cmargins : this.margins;
53031     },
53032
53033     highlight : function(){
53034         this.el.addClass("x-layout-panel-dragover");
53035     },
53036
53037     unhighlight : function(){
53038         this.el.removeClass("x-layout-panel-dragover");
53039     },
53040
53041     updateBox : function(box){
53042         this.box = box;
53043         if(!this.collapsed){
53044             this.el.dom.style.left = box.x + "px";
53045             this.el.dom.style.top = box.y + "px";
53046             this.updateBody(box.width, box.height);
53047         }else{
53048             this.collapsedEl.dom.style.left = box.x + "px";
53049             this.collapsedEl.dom.style.top = box.y + "px";
53050             this.collapsedEl.setSize(box.width, box.height);
53051         }
53052         if(this.tabs){
53053             this.tabs.autoSizeTabs();
53054         }
53055     },
53056
53057     updateBody : function(w, h){
53058         if(w !== null){
53059             this.el.setWidth(w);
53060             w -= this.el.getBorderWidth("rl");
53061             if(this.config.adjustments){
53062                 w += this.config.adjustments[0];
53063             }
53064         }
53065         if(h !== null){
53066             this.el.setHeight(h);
53067             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53068             h -= this.el.getBorderWidth("tb");
53069             if(this.config.adjustments){
53070                 h += this.config.adjustments[1];
53071             }
53072             this.bodyEl.setHeight(h);
53073             if(this.tabs){
53074                 h = this.tabs.syncHeight(h);
53075             }
53076         }
53077         if(this.panelSize){
53078             w = w !== null ? w : this.panelSize.width;
53079             h = h !== null ? h : this.panelSize.height;
53080         }
53081         if(this.activePanel){
53082             var el = this.activePanel.getEl();
53083             w = w !== null ? w : el.getWidth();
53084             h = h !== null ? h : el.getHeight();
53085             this.panelSize = {width: w, height: h};
53086             this.activePanel.setSize(w, h);
53087         }
53088         if(Roo.isIE && this.tabs){
53089             this.tabs.el.repaint();
53090         }
53091     },
53092
53093     /**
53094      * Returns the container element for this region.
53095      * @return {Roo.Element}
53096      */
53097     getEl : function(){
53098         return this.el;
53099     },
53100
53101     /**
53102      * Hides this region.
53103      */
53104     hide : function(){
53105         if(!this.collapsed){
53106             this.el.dom.style.left = "-2000px";
53107             this.el.hide();
53108         }else{
53109             this.collapsedEl.dom.style.left = "-2000px";
53110             this.collapsedEl.hide();
53111         }
53112         this.visible = false;
53113         this.fireEvent("visibilitychange", this, false);
53114     },
53115
53116     /**
53117      * Shows this region if it was previously hidden.
53118      */
53119     show : function(){
53120         if(!this.collapsed){
53121             this.el.show();
53122         }else{
53123             this.collapsedEl.show();
53124         }
53125         this.visible = true;
53126         this.fireEvent("visibilitychange", this, true);
53127     },
53128
53129     closeClicked : function(){
53130         if(this.activePanel){
53131             this.remove(this.activePanel);
53132         }
53133     },
53134
53135     collapseClick : function(e){
53136         if(this.isSlid){
53137            e.stopPropagation();
53138            this.slideIn();
53139         }else{
53140            e.stopPropagation();
53141            this.slideOut();
53142         }
53143     },
53144
53145     /**
53146      * Collapses this region.
53147      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53148      */
53149     collapse : function(skipAnim, skipCheck){
53150         if(this.collapsed) {
53151             return;
53152         }
53153         
53154         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53155             
53156             this.collapsed = true;
53157             if(this.split){
53158                 this.split.el.hide();
53159             }
53160             if(this.config.animate && skipAnim !== true){
53161                 this.fireEvent("invalidated", this);
53162                 this.animateCollapse();
53163             }else{
53164                 this.el.setLocation(-20000,-20000);
53165                 this.el.hide();
53166                 this.collapsedEl.show();
53167                 this.fireEvent("collapsed", this);
53168                 this.fireEvent("invalidated", this);
53169             }
53170         }
53171         
53172     },
53173
53174     animateCollapse : function(){
53175         // overridden
53176     },
53177
53178     /**
53179      * Expands this region if it was previously collapsed.
53180      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53181      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53182      */
53183     expand : function(e, skipAnim){
53184         if(e) {
53185             e.stopPropagation();
53186         }
53187         if(!this.collapsed || this.el.hasActiveFx()) {
53188             return;
53189         }
53190         if(this.isSlid){
53191             this.afterSlideIn();
53192             skipAnim = true;
53193         }
53194         this.collapsed = false;
53195         if(this.config.animate && skipAnim !== true){
53196             this.animateExpand();
53197         }else{
53198             this.el.show();
53199             if(this.split){
53200                 this.split.el.show();
53201             }
53202             this.collapsedEl.setLocation(-2000,-2000);
53203             this.collapsedEl.hide();
53204             this.fireEvent("invalidated", this);
53205             this.fireEvent("expanded", this);
53206         }
53207     },
53208
53209     animateExpand : function(){
53210         // overridden
53211     },
53212
53213     initTabs : function()
53214     {
53215         this.bodyEl.setStyle("overflow", "hidden");
53216         var ts = new Roo.TabPanel(
53217                 this.bodyEl.dom,
53218                 {
53219                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53220                     disableTooltips: this.config.disableTabTips,
53221                     toolbar : this.config.toolbar
53222                 }
53223         );
53224         if(this.config.hideTabs){
53225             ts.stripWrap.setDisplayed(false);
53226         }
53227         this.tabs = ts;
53228         ts.resizeTabs = this.config.resizeTabs === true;
53229         ts.minTabWidth = this.config.minTabWidth || 40;
53230         ts.maxTabWidth = this.config.maxTabWidth || 250;
53231         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53232         ts.monitorResize = false;
53233         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53234         ts.bodyEl.addClass('x-layout-tabs-body');
53235         this.panels.each(this.initPanelAsTab, this);
53236     },
53237
53238     initPanelAsTab : function(panel){
53239         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53240                     this.config.closeOnTab && panel.isClosable());
53241         if(panel.tabTip !== undefined){
53242             ti.setTooltip(panel.tabTip);
53243         }
53244         ti.on("activate", function(){
53245               this.setActivePanel(panel);
53246         }, this);
53247         if(this.config.closeOnTab){
53248             ti.on("beforeclose", function(t, e){
53249                 e.cancel = true;
53250                 this.remove(panel);
53251             }, this);
53252         }
53253         return ti;
53254     },
53255
53256     updatePanelTitle : function(panel, title){
53257         if(this.activePanel == panel){
53258             this.updateTitle(title);
53259         }
53260         if(this.tabs){
53261             var ti = this.tabs.getTab(panel.getEl().id);
53262             ti.setText(title);
53263             if(panel.tabTip !== undefined){
53264                 ti.setTooltip(panel.tabTip);
53265             }
53266         }
53267     },
53268
53269     updateTitle : function(title){
53270         if(this.titleTextEl && !this.config.title){
53271             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53272         }
53273     },
53274
53275     setActivePanel : function(panel){
53276         panel = this.getPanel(panel);
53277         if(this.activePanel && this.activePanel != panel){
53278             this.activePanel.setActiveState(false);
53279         }
53280         this.activePanel = panel;
53281         panel.setActiveState(true);
53282         if(this.panelSize){
53283             panel.setSize(this.panelSize.width, this.panelSize.height);
53284         }
53285         if(this.closeBtn){
53286             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53287         }
53288         this.updateTitle(panel.getTitle());
53289         if(this.tabs){
53290             this.fireEvent("invalidated", this);
53291         }
53292         this.fireEvent("panelactivated", this, panel);
53293     },
53294
53295     /**
53296      * Shows the specified panel.
53297      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53298      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53299      */
53300     showPanel : function(panel)
53301     {
53302         panel = this.getPanel(panel);
53303         if(panel){
53304             if(this.tabs){
53305                 var tab = this.tabs.getTab(panel.getEl().id);
53306                 if(tab.isHidden()){
53307                     this.tabs.unhideTab(tab.id);
53308                 }
53309                 tab.activate();
53310             }else{
53311                 this.setActivePanel(panel);
53312             }
53313         }
53314         return panel;
53315     },
53316
53317     /**
53318      * Get the active panel for this region.
53319      * @return {Roo.ContentPanel} The active panel or null
53320      */
53321     getActivePanel : function(){
53322         return this.activePanel;
53323     },
53324
53325     validateVisibility : function(){
53326         if(this.panels.getCount() < 1){
53327             this.updateTitle("&#160;");
53328             this.closeBtn.hide();
53329             this.hide();
53330         }else{
53331             if(!this.isVisible()){
53332                 this.show();
53333             }
53334         }
53335     },
53336
53337     /**
53338      * Adds the passed ContentPanel(s) to this region.
53339      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53340      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53341      */
53342     add : function(panel){
53343         if(arguments.length > 1){
53344             for(var i = 0, len = arguments.length; i < len; i++) {
53345                 this.add(arguments[i]);
53346             }
53347             return null;
53348         }
53349         if(this.hasPanel(panel)){
53350             this.showPanel(panel);
53351             return panel;
53352         }
53353         panel.setRegion(this);
53354         this.panels.add(panel);
53355         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53356             this.bodyEl.dom.appendChild(panel.getEl().dom);
53357             if(panel.background !== true){
53358                 this.setActivePanel(panel);
53359             }
53360             this.fireEvent("paneladded", this, panel);
53361             return panel;
53362         }
53363         if(!this.tabs){
53364             this.initTabs();
53365         }else{
53366             this.initPanelAsTab(panel);
53367         }
53368         if(panel.background !== true){
53369             this.tabs.activate(panel.getEl().id);
53370         }
53371         this.fireEvent("paneladded", this, panel);
53372         return panel;
53373     },
53374
53375     /**
53376      * Hides the tab for the specified panel.
53377      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53378      */
53379     hidePanel : function(panel){
53380         if(this.tabs && (panel = this.getPanel(panel))){
53381             this.tabs.hideTab(panel.getEl().id);
53382         }
53383     },
53384
53385     /**
53386      * Unhides the tab for a previously hidden panel.
53387      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53388      */
53389     unhidePanel : function(panel){
53390         if(this.tabs && (panel = this.getPanel(panel))){
53391             this.tabs.unhideTab(panel.getEl().id);
53392         }
53393     },
53394
53395     clearPanels : function(){
53396         while(this.panels.getCount() > 0){
53397              this.remove(this.panels.first());
53398         }
53399     },
53400
53401     /**
53402      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53403      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53404      * @param {Boolean} preservePanel Overrides the config preservePanel option
53405      * @return {Roo.ContentPanel} The panel that was removed
53406      */
53407     remove : function(panel, preservePanel){
53408         panel = this.getPanel(panel);
53409         if(!panel){
53410             return null;
53411         }
53412         var e = {};
53413         this.fireEvent("beforeremove", this, panel, e);
53414         if(e.cancel === true){
53415             return null;
53416         }
53417         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53418         var panelId = panel.getId();
53419         this.panels.removeKey(panelId);
53420         if(preservePanel){
53421             document.body.appendChild(panel.getEl().dom);
53422         }
53423         if(this.tabs){
53424             this.tabs.removeTab(panel.getEl().id);
53425         }else if (!preservePanel){
53426             this.bodyEl.dom.removeChild(panel.getEl().dom);
53427         }
53428         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53429             var p = this.panels.first();
53430             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53431             tempEl.appendChild(p.getEl().dom);
53432             this.bodyEl.update("");
53433             this.bodyEl.dom.appendChild(p.getEl().dom);
53434             tempEl = null;
53435             this.updateTitle(p.getTitle());
53436             this.tabs = null;
53437             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53438             this.setActivePanel(p);
53439         }
53440         panel.setRegion(null);
53441         if(this.activePanel == panel){
53442             this.activePanel = null;
53443         }
53444         if(this.config.autoDestroy !== false && preservePanel !== true){
53445             try{panel.destroy();}catch(e){}
53446         }
53447         this.fireEvent("panelremoved", this, panel);
53448         return panel;
53449     },
53450
53451     /**
53452      * Returns the TabPanel component used by this region
53453      * @return {Roo.TabPanel}
53454      */
53455     getTabs : function(){
53456         return this.tabs;
53457     },
53458
53459     createTool : function(parentEl, className){
53460         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53461             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53462         btn.addClassOnOver("x-layout-tools-button-over");
53463         return btn;
53464     }
53465 });/*
53466  * Based on:
53467  * Ext JS Library 1.1.1
53468  * Copyright(c) 2006-2007, Ext JS, LLC.
53469  *
53470  * Originally Released Under LGPL - original licence link has changed is not relivant.
53471  *
53472  * Fork - LGPL
53473  * <script type="text/javascript">
53474  */
53475  
53476
53477
53478 /**
53479  * @class Roo.SplitLayoutRegion
53480  * @extends Roo.LayoutRegion
53481  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53482  */
53483 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53484     this.cursor = cursor;
53485     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53486 };
53487
53488 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53489     splitTip : "Drag to resize.",
53490     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53491     useSplitTips : false,
53492
53493     applyConfig : function(config){
53494         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53495         if(config.split){
53496             if(!this.split){
53497                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53498                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53499                 /** The SplitBar for this region 
53500                 * @type Roo.SplitBar */
53501                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53502                 this.split.on("moved", this.onSplitMove, this);
53503                 this.split.useShim = config.useShim === true;
53504                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53505                 if(this.useSplitTips){
53506                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53507                 }
53508                 if(config.collapsible){
53509                     this.split.el.on("dblclick", this.collapse,  this);
53510                 }
53511             }
53512             if(typeof config.minSize != "undefined"){
53513                 this.split.minSize = config.minSize;
53514             }
53515             if(typeof config.maxSize != "undefined"){
53516                 this.split.maxSize = config.maxSize;
53517             }
53518             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53519                 this.hideSplitter();
53520             }
53521         }
53522     },
53523
53524     getHMaxSize : function(){
53525          var cmax = this.config.maxSize || 10000;
53526          var center = this.mgr.getRegion("center");
53527          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53528     },
53529
53530     getVMaxSize : function(){
53531          var cmax = this.config.maxSize || 10000;
53532          var center = this.mgr.getRegion("center");
53533          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53534     },
53535
53536     onSplitMove : function(split, newSize){
53537         this.fireEvent("resized", this, newSize);
53538     },
53539     
53540     /** 
53541      * Returns the {@link Roo.SplitBar} for this region.
53542      * @return {Roo.SplitBar}
53543      */
53544     getSplitBar : function(){
53545         return this.split;
53546     },
53547     
53548     hide : function(){
53549         this.hideSplitter();
53550         Roo.SplitLayoutRegion.superclass.hide.call(this);
53551     },
53552
53553     hideSplitter : function(){
53554         if(this.split){
53555             this.split.el.setLocation(-2000,-2000);
53556             this.split.el.hide();
53557         }
53558     },
53559
53560     show : function(){
53561         if(this.split){
53562             this.split.el.show();
53563         }
53564         Roo.SplitLayoutRegion.superclass.show.call(this);
53565     },
53566     
53567     beforeSlide: function(){
53568         if(Roo.isGecko){// firefox overflow auto bug workaround
53569             this.bodyEl.clip();
53570             if(this.tabs) {
53571                 this.tabs.bodyEl.clip();
53572             }
53573             if(this.activePanel){
53574                 this.activePanel.getEl().clip();
53575                 
53576                 if(this.activePanel.beforeSlide){
53577                     this.activePanel.beforeSlide();
53578                 }
53579             }
53580         }
53581     },
53582     
53583     afterSlide : function(){
53584         if(Roo.isGecko){// firefox overflow auto bug workaround
53585             this.bodyEl.unclip();
53586             if(this.tabs) {
53587                 this.tabs.bodyEl.unclip();
53588             }
53589             if(this.activePanel){
53590                 this.activePanel.getEl().unclip();
53591                 if(this.activePanel.afterSlide){
53592                     this.activePanel.afterSlide();
53593                 }
53594             }
53595         }
53596     },
53597
53598     initAutoHide : function(){
53599         if(this.autoHide !== false){
53600             if(!this.autoHideHd){
53601                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53602                 this.autoHideHd = {
53603                     "mouseout": function(e){
53604                         if(!e.within(this.el, true)){
53605                             st.delay(500);
53606                         }
53607                     },
53608                     "mouseover" : function(e){
53609                         st.cancel();
53610                     },
53611                     scope : this
53612                 };
53613             }
53614             this.el.on(this.autoHideHd);
53615         }
53616     },
53617
53618     clearAutoHide : function(){
53619         if(this.autoHide !== false){
53620             this.el.un("mouseout", this.autoHideHd.mouseout);
53621             this.el.un("mouseover", this.autoHideHd.mouseover);
53622         }
53623     },
53624
53625     clearMonitor : function(){
53626         Roo.get(document).un("click", this.slideInIf, this);
53627     },
53628
53629     // these names are backwards but not changed for compat
53630     slideOut : function(){
53631         if(this.isSlid || this.el.hasActiveFx()){
53632             return;
53633         }
53634         this.isSlid = true;
53635         if(this.collapseBtn){
53636             this.collapseBtn.hide();
53637         }
53638         this.closeBtnState = this.closeBtn.getStyle('display');
53639         this.closeBtn.hide();
53640         if(this.stickBtn){
53641             this.stickBtn.show();
53642         }
53643         this.el.show();
53644         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53645         this.beforeSlide();
53646         this.el.setStyle("z-index", 10001);
53647         this.el.slideIn(this.getSlideAnchor(), {
53648             callback: function(){
53649                 this.afterSlide();
53650                 this.initAutoHide();
53651                 Roo.get(document).on("click", this.slideInIf, this);
53652                 this.fireEvent("slideshow", this);
53653             },
53654             scope: this,
53655             block: true
53656         });
53657     },
53658
53659     afterSlideIn : function(){
53660         this.clearAutoHide();
53661         this.isSlid = false;
53662         this.clearMonitor();
53663         this.el.setStyle("z-index", "");
53664         if(this.collapseBtn){
53665             this.collapseBtn.show();
53666         }
53667         this.closeBtn.setStyle('display', this.closeBtnState);
53668         if(this.stickBtn){
53669             this.stickBtn.hide();
53670         }
53671         this.fireEvent("slidehide", this);
53672     },
53673
53674     slideIn : function(cb){
53675         if(!this.isSlid || this.el.hasActiveFx()){
53676             Roo.callback(cb);
53677             return;
53678         }
53679         this.isSlid = false;
53680         this.beforeSlide();
53681         this.el.slideOut(this.getSlideAnchor(), {
53682             callback: function(){
53683                 this.el.setLeftTop(-10000, -10000);
53684                 this.afterSlide();
53685                 this.afterSlideIn();
53686                 Roo.callback(cb);
53687             },
53688             scope: this,
53689             block: true
53690         });
53691     },
53692     
53693     slideInIf : function(e){
53694         if(!e.within(this.el)){
53695             this.slideIn();
53696         }
53697     },
53698
53699     animateCollapse : function(){
53700         this.beforeSlide();
53701         this.el.setStyle("z-index", 20000);
53702         var anchor = this.getSlideAnchor();
53703         this.el.slideOut(anchor, {
53704             callback : function(){
53705                 this.el.setStyle("z-index", "");
53706                 this.collapsedEl.slideIn(anchor, {duration:.3});
53707                 this.afterSlide();
53708                 this.el.setLocation(-10000,-10000);
53709                 this.el.hide();
53710                 this.fireEvent("collapsed", this);
53711             },
53712             scope: this,
53713             block: true
53714         });
53715     },
53716
53717     animateExpand : function(){
53718         this.beforeSlide();
53719         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53720         this.el.setStyle("z-index", 20000);
53721         this.collapsedEl.hide({
53722             duration:.1
53723         });
53724         this.el.slideIn(this.getSlideAnchor(), {
53725             callback : function(){
53726                 this.el.setStyle("z-index", "");
53727                 this.afterSlide();
53728                 if(this.split){
53729                     this.split.el.show();
53730                 }
53731                 this.fireEvent("invalidated", this);
53732                 this.fireEvent("expanded", this);
53733             },
53734             scope: this,
53735             block: true
53736         });
53737     },
53738
53739     anchors : {
53740         "west" : "left",
53741         "east" : "right",
53742         "north" : "top",
53743         "south" : "bottom"
53744     },
53745
53746     sanchors : {
53747         "west" : "l",
53748         "east" : "r",
53749         "north" : "t",
53750         "south" : "b"
53751     },
53752
53753     canchors : {
53754         "west" : "tl-tr",
53755         "east" : "tr-tl",
53756         "north" : "tl-bl",
53757         "south" : "bl-tl"
53758     },
53759
53760     getAnchor : function(){
53761         return this.anchors[this.position];
53762     },
53763
53764     getCollapseAnchor : function(){
53765         return this.canchors[this.position];
53766     },
53767
53768     getSlideAnchor : function(){
53769         return this.sanchors[this.position];
53770     },
53771
53772     getAlignAdj : function(){
53773         var cm = this.cmargins;
53774         switch(this.position){
53775             case "west":
53776                 return [0, 0];
53777             break;
53778             case "east":
53779                 return [0, 0];
53780             break;
53781             case "north":
53782                 return [0, 0];
53783             break;
53784             case "south":
53785                 return [0, 0];
53786             break;
53787         }
53788     },
53789
53790     getExpandAdj : function(){
53791         var c = this.collapsedEl, cm = this.cmargins;
53792         switch(this.position){
53793             case "west":
53794                 return [-(cm.right+c.getWidth()+cm.left), 0];
53795             break;
53796             case "east":
53797                 return [cm.right+c.getWidth()+cm.left, 0];
53798             break;
53799             case "north":
53800                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53801             break;
53802             case "south":
53803                 return [0, cm.top+cm.bottom+c.getHeight()];
53804             break;
53805         }
53806     }
53807 });/*
53808  * Based on:
53809  * Ext JS Library 1.1.1
53810  * Copyright(c) 2006-2007, Ext JS, LLC.
53811  *
53812  * Originally Released Under LGPL - original licence link has changed is not relivant.
53813  *
53814  * Fork - LGPL
53815  * <script type="text/javascript">
53816  */
53817 /*
53818  * These classes are private internal classes
53819  */
53820 Roo.CenterLayoutRegion = function(mgr, config){
53821     Roo.LayoutRegion.call(this, mgr, config, "center");
53822     this.visible = true;
53823     this.minWidth = config.minWidth || 20;
53824     this.minHeight = config.minHeight || 20;
53825 };
53826
53827 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53828     hide : function(){
53829         // center panel can't be hidden
53830     },
53831     
53832     show : function(){
53833         // center panel can't be hidden
53834     },
53835     
53836     getMinWidth: function(){
53837         return this.minWidth;
53838     },
53839     
53840     getMinHeight: function(){
53841         return this.minHeight;
53842     }
53843 });
53844
53845
53846 Roo.NorthLayoutRegion = function(mgr, config){
53847     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53848     if(this.split){
53849         this.split.placement = Roo.SplitBar.TOP;
53850         this.split.orientation = Roo.SplitBar.VERTICAL;
53851         this.split.el.addClass("x-layout-split-v");
53852     }
53853     var size = config.initialSize || config.height;
53854     if(typeof size != "undefined"){
53855         this.el.setHeight(size);
53856     }
53857 };
53858 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53859     orientation: Roo.SplitBar.VERTICAL,
53860     getBox : function(){
53861         if(this.collapsed){
53862             return this.collapsedEl.getBox();
53863         }
53864         var box = this.el.getBox();
53865         if(this.split){
53866             box.height += this.split.el.getHeight();
53867         }
53868         return box;
53869     },
53870     
53871     updateBox : function(box){
53872         if(this.split && !this.collapsed){
53873             box.height -= this.split.el.getHeight();
53874             this.split.el.setLeft(box.x);
53875             this.split.el.setTop(box.y+box.height);
53876             this.split.el.setWidth(box.width);
53877         }
53878         if(this.collapsed){
53879             this.updateBody(box.width, null);
53880         }
53881         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53882     }
53883 });
53884
53885 Roo.SouthLayoutRegion = function(mgr, config){
53886     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53887     if(this.split){
53888         this.split.placement = Roo.SplitBar.BOTTOM;
53889         this.split.orientation = Roo.SplitBar.VERTICAL;
53890         this.split.el.addClass("x-layout-split-v");
53891     }
53892     var size = config.initialSize || config.height;
53893     if(typeof size != "undefined"){
53894         this.el.setHeight(size);
53895     }
53896 };
53897 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53898     orientation: Roo.SplitBar.VERTICAL,
53899     getBox : function(){
53900         if(this.collapsed){
53901             return this.collapsedEl.getBox();
53902         }
53903         var box = this.el.getBox();
53904         if(this.split){
53905             var sh = this.split.el.getHeight();
53906             box.height += sh;
53907             box.y -= sh;
53908         }
53909         return box;
53910     },
53911     
53912     updateBox : function(box){
53913         if(this.split && !this.collapsed){
53914             var sh = this.split.el.getHeight();
53915             box.height -= sh;
53916             box.y += sh;
53917             this.split.el.setLeft(box.x);
53918             this.split.el.setTop(box.y-sh);
53919             this.split.el.setWidth(box.width);
53920         }
53921         if(this.collapsed){
53922             this.updateBody(box.width, null);
53923         }
53924         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53925     }
53926 });
53927
53928 Roo.EastLayoutRegion = function(mgr, config){
53929     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53930     if(this.split){
53931         this.split.placement = Roo.SplitBar.RIGHT;
53932         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53933         this.split.el.addClass("x-layout-split-h");
53934     }
53935     var size = config.initialSize || config.width;
53936     if(typeof size != "undefined"){
53937         this.el.setWidth(size);
53938     }
53939 };
53940 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53941     orientation: Roo.SplitBar.HORIZONTAL,
53942     getBox : function(){
53943         if(this.collapsed){
53944             return this.collapsedEl.getBox();
53945         }
53946         var box = this.el.getBox();
53947         if(this.split){
53948             var sw = this.split.el.getWidth();
53949             box.width += sw;
53950             box.x -= sw;
53951         }
53952         return box;
53953     },
53954
53955     updateBox : function(box){
53956         if(this.split && !this.collapsed){
53957             var sw = this.split.el.getWidth();
53958             box.width -= sw;
53959             this.split.el.setLeft(box.x);
53960             this.split.el.setTop(box.y);
53961             this.split.el.setHeight(box.height);
53962             box.x += sw;
53963         }
53964         if(this.collapsed){
53965             this.updateBody(null, box.height);
53966         }
53967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53968     }
53969 });
53970
53971 Roo.WestLayoutRegion = function(mgr, config){
53972     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53973     if(this.split){
53974         this.split.placement = Roo.SplitBar.LEFT;
53975         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53976         this.split.el.addClass("x-layout-split-h");
53977     }
53978     var size = config.initialSize || config.width;
53979     if(typeof size != "undefined"){
53980         this.el.setWidth(size);
53981     }
53982 };
53983 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53984     orientation: Roo.SplitBar.HORIZONTAL,
53985     getBox : function(){
53986         if(this.collapsed){
53987             return this.collapsedEl.getBox();
53988         }
53989         var box = this.el.getBox();
53990         if(this.split){
53991             box.width += this.split.el.getWidth();
53992         }
53993         return box;
53994     },
53995     
53996     updateBox : function(box){
53997         if(this.split && !this.collapsed){
53998             var sw = this.split.el.getWidth();
53999             box.width -= sw;
54000             this.split.el.setLeft(box.x+box.width);
54001             this.split.el.setTop(box.y);
54002             this.split.el.setHeight(box.height);
54003         }
54004         if(this.collapsed){
54005             this.updateBody(null, box.height);
54006         }
54007         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54008     }
54009 });
54010 /*
54011  * Based on:
54012  * Ext JS Library 1.1.1
54013  * Copyright(c) 2006-2007, Ext JS, LLC.
54014  *
54015  * Originally Released Under LGPL - original licence link has changed is not relivant.
54016  *
54017  * Fork - LGPL
54018  * <script type="text/javascript">
54019  */
54020  
54021  
54022 /*
54023  * Private internal class for reading and applying state
54024  */
54025 Roo.LayoutStateManager = function(layout){
54026      // default empty state
54027      this.state = {
54028         north: {},
54029         south: {},
54030         east: {},
54031         west: {}       
54032     };
54033 };
54034
54035 Roo.LayoutStateManager.prototype = {
54036     init : function(layout, provider){
54037         this.provider = provider;
54038         var state = provider.get(layout.id+"-layout-state");
54039         if(state){
54040             var wasUpdating = layout.isUpdating();
54041             if(!wasUpdating){
54042                 layout.beginUpdate();
54043             }
54044             for(var key in state){
54045                 if(typeof state[key] != "function"){
54046                     var rstate = state[key];
54047                     var r = layout.getRegion(key);
54048                     if(r && rstate){
54049                         if(rstate.size){
54050                             r.resizeTo(rstate.size);
54051                         }
54052                         if(rstate.collapsed == true){
54053                             r.collapse(true);
54054                         }else{
54055                             r.expand(null, true);
54056                         }
54057                     }
54058                 }
54059             }
54060             if(!wasUpdating){
54061                 layout.endUpdate();
54062             }
54063             this.state = state; 
54064         }
54065         this.layout = layout;
54066         layout.on("regionresized", this.onRegionResized, this);
54067         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54068         layout.on("regionexpanded", this.onRegionExpanded, this);
54069     },
54070     
54071     storeState : function(){
54072         this.provider.set(this.layout.id+"-layout-state", this.state);
54073     },
54074     
54075     onRegionResized : function(region, newSize){
54076         this.state[region.getPosition()].size = newSize;
54077         this.storeState();
54078     },
54079     
54080     onRegionCollapsed : function(region){
54081         this.state[region.getPosition()].collapsed = true;
54082         this.storeState();
54083     },
54084     
54085     onRegionExpanded : function(region){
54086         this.state[region.getPosition()].collapsed = false;
54087         this.storeState();
54088     }
54089 };/*
54090  * Based on:
54091  * Ext JS Library 1.1.1
54092  * Copyright(c) 2006-2007, Ext JS, LLC.
54093  *
54094  * Originally Released Under LGPL - original licence link has changed is not relivant.
54095  *
54096  * Fork - LGPL
54097  * <script type="text/javascript">
54098  */
54099 /**
54100  * @class Roo.ContentPanel
54101  * @extends Roo.util.Observable
54102  * A basic ContentPanel element.
54103  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54104  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54105  * @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
54106  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54107  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54108  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54109  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54110  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54111  * @cfg {String} title          The title for this panel
54112  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54113  * @cfg {String} url            Calls {@link #setUrl} with this value
54114  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54115  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54116  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54117  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54118
54119  * @constructor
54120  * Create a new ContentPanel.
54121  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54122  * @param {String/Object} config A string to set only the title or a config object
54123  * @param {String} content (optional) Set the HTML content for this panel
54124  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54125  */
54126 Roo.ContentPanel = function(el, config, content){
54127     
54128      
54129     /*
54130     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54131         config = el;
54132         el = Roo.id();
54133     }
54134     if (config && config.parentLayout) { 
54135         el = config.parentLayout.el.createChild(); 
54136     }
54137     */
54138     if(el.autoCreate){ // xtype is available if this is called from factory
54139         config = el;
54140         el = Roo.id();
54141     }
54142     this.el = Roo.get(el);
54143     if(!this.el && config && config.autoCreate){
54144         if(typeof config.autoCreate == "object"){
54145             if(!config.autoCreate.id){
54146                 config.autoCreate.id = config.id||el;
54147             }
54148             this.el = Roo.DomHelper.append(document.body,
54149                         config.autoCreate, true);
54150         }else{
54151             this.el = Roo.DomHelper.append(document.body,
54152                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54153         }
54154     }
54155     this.closable = false;
54156     this.loaded = false;
54157     this.active = false;
54158     if(typeof config == "string"){
54159         this.title = config;
54160     }else{
54161         Roo.apply(this, config);
54162     }
54163     
54164     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54165         this.wrapEl = this.el.wrap();
54166         this.toolbar.container = this.el.insertSibling(false, 'before');
54167         this.toolbar = new Roo.Toolbar(this.toolbar);
54168     }
54169     
54170     // xtype created footer. - not sure if will work as we normally have to render first..
54171     if (this.footer && !this.footer.el && this.footer.xtype) {
54172         if (!this.wrapEl) {
54173             this.wrapEl = this.el.wrap();
54174         }
54175     
54176         this.footer.container = this.wrapEl.createChild();
54177          
54178         this.footer = Roo.factory(this.footer, Roo);
54179         
54180     }
54181     
54182     if(this.resizeEl){
54183         this.resizeEl = Roo.get(this.resizeEl, true);
54184     }else{
54185         this.resizeEl = this.el;
54186     }
54187     // handle view.xtype
54188     
54189  
54190     
54191     
54192     this.addEvents({
54193         /**
54194          * @event activate
54195          * Fires when this panel is activated. 
54196          * @param {Roo.ContentPanel} this
54197          */
54198         "activate" : true,
54199         /**
54200          * @event deactivate
54201          * Fires when this panel is activated. 
54202          * @param {Roo.ContentPanel} this
54203          */
54204         "deactivate" : true,
54205
54206         /**
54207          * @event resize
54208          * Fires when this panel is resized if fitToFrame is true.
54209          * @param {Roo.ContentPanel} this
54210          * @param {Number} width The width after any component adjustments
54211          * @param {Number} height The height after any component adjustments
54212          */
54213         "resize" : true,
54214         
54215          /**
54216          * @event render
54217          * Fires when this tab is created
54218          * @param {Roo.ContentPanel} this
54219          */
54220         "render" : true
54221          
54222         
54223     });
54224     
54225
54226     
54227     
54228     if(this.autoScroll){
54229         this.resizeEl.setStyle("overflow", "auto");
54230     } else {
54231         // fix randome scrolling
54232         this.el.on('scroll', function() {
54233             Roo.log('fix random scolling');
54234             this.scrollTo('top',0); 
54235         });
54236     }
54237     content = content || this.content;
54238     if(content){
54239         this.setContent(content);
54240     }
54241     if(config && config.url){
54242         this.setUrl(this.url, this.params, this.loadOnce);
54243     }
54244     
54245     
54246     
54247     Roo.ContentPanel.superclass.constructor.call(this);
54248     
54249     if (this.view && typeof(this.view.xtype) != 'undefined') {
54250         this.view.el = this.el.appendChild(document.createElement("div"));
54251         this.view = Roo.factory(this.view); 
54252         this.view.render  &&  this.view.render(false, '');  
54253     }
54254     
54255     
54256     this.fireEvent('render', this);
54257 };
54258
54259 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54260     tabTip:'',
54261     setRegion : function(region){
54262         this.region = region;
54263         if(region){
54264            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54265         }else{
54266            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54267         } 
54268     },
54269     
54270     /**
54271      * Returns the toolbar for this Panel if one was configured. 
54272      * @return {Roo.Toolbar} 
54273      */
54274     getToolbar : function(){
54275         return this.toolbar;
54276     },
54277     
54278     setActiveState : function(active){
54279         this.active = active;
54280         if(!active){
54281             this.fireEvent("deactivate", this);
54282         }else{
54283             this.fireEvent("activate", this);
54284         }
54285     },
54286     /**
54287      * Updates this panel's element
54288      * @param {String} content The new content
54289      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54290     */
54291     setContent : function(content, loadScripts){
54292         this.el.update(content, loadScripts);
54293     },
54294
54295     ignoreResize : function(w, h){
54296         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54297             return true;
54298         }else{
54299             this.lastSize = {width: w, height: h};
54300             return false;
54301         }
54302     },
54303     /**
54304      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54305      * @return {Roo.UpdateManager} The UpdateManager
54306      */
54307     getUpdateManager : function(){
54308         return this.el.getUpdateManager();
54309     },
54310      /**
54311      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54312      * @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:
54313 <pre><code>
54314 panel.load({
54315     url: "your-url.php",
54316     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54317     callback: yourFunction,
54318     scope: yourObject, //(optional scope)
54319     discardUrl: false,
54320     nocache: false,
54321     text: "Loading...",
54322     timeout: 30,
54323     scripts: false
54324 });
54325 </code></pre>
54326      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54327      * 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.
54328      * @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}
54329      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54330      * @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.
54331      * @return {Roo.ContentPanel} this
54332      */
54333     load : function(){
54334         var um = this.el.getUpdateManager();
54335         um.update.apply(um, arguments);
54336         return this;
54337     },
54338
54339
54340     /**
54341      * 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.
54342      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54343      * @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)
54344      * @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)
54345      * @return {Roo.UpdateManager} The UpdateManager
54346      */
54347     setUrl : function(url, params, loadOnce){
54348         if(this.refreshDelegate){
54349             this.removeListener("activate", this.refreshDelegate);
54350         }
54351         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54352         this.on("activate", this.refreshDelegate);
54353         return this.el.getUpdateManager();
54354     },
54355     
54356     _handleRefresh : function(url, params, loadOnce){
54357         if(!loadOnce || !this.loaded){
54358             var updater = this.el.getUpdateManager();
54359             updater.update(url, params, this._setLoaded.createDelegate(this));
54360         }
54361     },
54362     
54363     _setLoaded : function(){
54364         this.loaded = true;
54365     }, 
54366     
54367     /**
54368      * Returns this panel's id
54369      * @return {String} 
54370      */
54371     getId : function(){
54372         return this.el.id;
54373     },
54374     
54375     /** 
54376      * Returns this panel's element - used by regiosn to add.
54377      * @return {Roo.Element} 
54378      */
54379     getEl : function(){
54380         return this.wrapEl || this.el;
54381     },
54382     
54383     adjustForComponents : function(width, height)
54384     {
54385         //Roo.log('adjustForComponents ');
54386         if(this.resizeEl != this.el){
54387             width -= this.el.getFrameWidth('lr');
54388             height -= this.el.getFrameWidth('tb');
54389         }
54390         if(this.toolbar){
54391             var te = this.toolbar.getEl();
54392             height -= te.getHeight();
54393             te.setWidth(width);
54394         }
54395         if(this.footer){
54396             var te = this.footer.getEl();
54397             //Roo.log("footer:" + te.getHeight());
54398             
54399             height -= te.getHeight();
54400             te.setWidth(width);
54401         }
54402         
54403         
54404         if(this.adjustments){
54405             width += this.adjustments[0];
54406             height += this.adjustments[1];
54407         }
54408         return {"width": width, "height": height};
54409     },
54410     
54411     setSize : function(width, height){
54412         if(this.fitToFrame && !this.ignoreResize(width, height)){
54413             if(this.fitContainer && this.resizeEl != this.el){
54414                 this.el.setSize(width, height);
54415             }
54416             var size = this.adjustForComponents(width, height);
54417             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54418             this.fireEvent('resize', this, size.width, size.height);
54419         }
54420     },
54421     
54422     /**
54423      * Returns this panel's title
54424      * @return {String} 
54425      */
54426     getTitle : function(){
54427         return this.title;
54428     },
54429     
54430     /**
54431      * Set this panel's title
54432      * @param {String} title
54433      */
54434     setTitle : function(title){
54435         this.title = title;
54436         if(this.region){
54437             this.region.updatePanelTitle(this, title);
54438         }
54439     },
54440     
54441     /**
54442      * Returns true is this panel was configured to be closable
54443      * @return {Boolean} 
54444      */
54445     isClosable : function(){
54446         return this.closable;
54447     },
54448     
54449     beforeSlide : function(){
54450         this.el.clip();
54451         this.resizeEl.clip();
54452     },
54453     
54454     afterSlide : function(){
54455         this.el.unclip();
54456         this.resizeEl.unclip();
54457     },
54458     
54459     /**
54460      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54461      *   Will fail silently if the {@link #setUrl} method has not been called.
54462      *   This does not activate the panel, just updates its content.
54463      */
54464     refresh : function(){
54465         if(this.refreshDelegate){
54466            this.loaded = false;
54467            this.refreshDelegate();
54468         }
54469     },
54470     
54471     /**
54472      * Destroys this panel
54473      */
54474     destroy : function(){
54475         this.el.removeAllListeners();
54476         var tempEl = document.createElement("span");
54477         tempEl.appendChild(this.el.dom);
54478         tempEl.innerHTML = "";
54479         this.el.remove();
54480         this.el = null;
54481     },
54482     
54483     /**
54484      * form - if the content panel contains a form - this is a reference to it.
54485      * @type {Roo.form.Form}
54486      */
54487     form : false,
54488     /**
54489      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54490      *    This contains a reference to it.
54491      * @type {Roo.View}
54492      */
54493     view : false,
54494     
54495       /**
54496      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54497      * <pre><code>
54498
54499 layout.addxtype({
54500        xtype : 'Form',
54501        items: [ .... ]
54502    }
54503 );
54504
54505 </code></pre>
54506      * @param {Object} cfg Xtype definition of item to add.
54507      */
54508     
54509     addxtype : function(cfg) {
54510         // add form..
54511         if (cfg.xtype.match(/^Form$/)) {
54512             
54513             var el;
54514             //if (this.footer) {
54515             //    el = this.footer.container.insertSibling(false, 'before');
54516             //} else {
54517                 el = this.el.createChild();
54518             //}
54519
54520             this.form = new  Roo.form.Form(cfg);
54521             
54522             
54523             if ( this.form.allItems.length) {
54524                 this.form.render(el.dom);
54525             }
54526             return this.form;
54527         }
54528         // should only have one of theses..
54529         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54530             // views.. should not be just added - used named prop 'view''
54531             
54532             cfg.el = this.el.appendChild(document.createElement("div"));
54533             // factory?
54534             
54535             var ret = new Roo.factory(cfg);
54536              
54537              ret.render && ret.render(false, ''); // render blank..
54538             this.view = ret;
54539             return ret;
54540         }
54541         return false;
54542     }
54543 });
54544
54545 /**
54546  * @class Roo.GridPanel
54547  * @extends Roo.ContentPanel
54548  * @constructor
54549  * Create a new GridPanel.
54550  * @param {Roo.grid.Grid} grid The grid for this panel
54551  * @param {String/Object} config A string to set only the panel's title, or a config object
54552  */
54553 Roo.GridPanel = function(grid, config){
54554     
54555   
54556     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54557         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54558         
54559     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54560     
54561     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54562     
54563     if(this.toolbar){
54564         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54565     }
54566     // xtype created footer. - not sure if will work as we normally have to render first..
54567     if (this.footer && !this.footer.el && this.footer.xtype) {
54568         
54569         this.footer.container = this.grid.getView().getFooterPanel(true);
54570         this.footer.dataSource = this.grid.dataSource;
54571         this.footer = Roo.factory(this.footer, Roo);
54572         
54573     }
54574     
54575     grid.monitorWindowResize = false; // turn off autosizing
54576     grid.autoHeight = false;
54577     grid.autoWidth = false;
54578     this.grid = grid;
54579     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54580 };
54581
54582 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54583     getId : function(){
54584         return this.grid.id;
54585     },
54586     
54587     /**
54588      * Returns the grid for this panel
54589      * @return {Roo.grid.Grid} 
54590      */
54591     getGrid : function(){
54592         return this.grid;    
54593     },
54594     
54595     setSize : function(width, height){
54596         if(!this.ignoreResize(width, height)){
54597             var grid = this.grid;
54598             var size = this.adjustForComponents(width, height);
54599             grid.getGridEl().setSize(size.width, size.height);
54600             grid.autoSize();
54601         }
54602     },
54603     
54604     beforeSlide : function(){
54605         this.grid.getView().scroller.clip();
54606     },
54607     
54608     afterSlide : function(){
54609         this.grid.getView().scroller.unclip();
54610     },
54611     
54612     destroy : function(){
54613         this.grid.destroy();
54614         delete this.grid;
54615         Roo.GridPanel.superclass.destroy.call(this); 
54616     }
54617 });
54618
54619
54620 /**
54621  * @class Roo.NestedLayoutPanel
54622  * @extends Roo.ContentPanel
54623  * @constructor
54624  * Create a new NestedLayoutPanel.
54625  * 
54626  * 
54627  * @param {Roo.BorderLayout} layout The layout for this panel
54628  * @param {String/Object} config A string to set only the title or a config object
54629  */
54630 Roo.NestedLayoutPanel = function(layout, config)
54631 {
54632     // construct with only one argument..
54633     /* FIXME - implement nicer consturctors
54634     if (layout.layout) {
54635         config = layout;
54636         layout = config.layout;
54637         delete config.layout;
54638     }
54639     if (layout.xtype && !layout.getEl) {
54640         // then layout needs constructing..
54641         layout = Roo.factory(layout, Roo);
54642     }
54643     */
54644     
54645     
54646     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54647     
54648     layout.monitorWindowResize = false; // turn off autosizing
54649     this.layout = layout;
54650     this.layout.getEl().addClass("x-layout-nested-layout");
54651     
54652     
54653     
54654     
54655 };
54656
54657 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54658
54659     setSize : function(width, height){
54660         if(!this.ignoreResize(width, height)){
54661             var size = this.adjustForComponents(width, height);
54662             var el = this.layout.getEl();
54663             el.setSize(size.width, size.height);
54664             var touch = el.dom.offsetWidth;
54665             this.layout.layout();
54666             // ie requires a double layout on the first pass
54667             if(Roo.isIE && !this.initialized){
54668                 this.initialized = true;
54669                 this.layout.layout();
54670             }
54671         }
54672     },
54673     
54674     // activate all subpanels if not currently active..
54675     
54676     setActiveState : function(active){
54677         this.active = active;
54678         if(!active){
54679             this.fireEvent("deactivate", this);
54680             return;
54681         }
54682         
54683         this.fireEvent("activate", this);
54684         // not sure if this should happen before or after..
54685         if (!this.layout) {
54686             return; // should not happen..
54687         }
54688         var reg = false;
54689         for (var r in this.layout.regions) {
54690             reg = this.layout.getRegion(r);
54691             if (reg.getActivePanel()) {
54692                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54693                 reg.setActivePanel(reg.getActivePanel());
54694                 continue;
54695             }
54696             if (!reg.panels.length) {
54697                 continue;
54698             }
54699             reg.showPanel(reg.getPanel(0));
54700         }
54701         
54702         
54703         
54704         
54705     },
54706     
54707     /**
54708      * Returns the nested BorderLayout for this panel
54709      * @return {Roo.BorderLayout} 
54710      */
54711     getLayout : function(){
54712         return this.layout;
54713     },
54714     
54715      /**
54716      * Adds a xtype elements to the layout of the nested panel
54717      * <pre><code>
54718
54719 panel.addxtype({
54720        xtype : 'ContentPanel',
54721        region: 'west',
54722        items: [ .... ]
54723    }
54724 );
54725
54726 panel.addxtype({
54727         xtype : 'NestedLayoutPanel',
54728         region: 'west',
54729         layout: {
54730            center: { },
54731            west: { }   
54732         },
54733         items : [ ... list of content panels or nested layout panels.. ]
54734    }
54735 );
54736 </code></pre>
54737      * @param {Object} cfg Xtype definition of item to add.
54738      */
54739     addxtype : function(cfg) {
54740         return this.layout.addxtype(cfg);
54741     
54742     }
54743 });
54744
54745 Roo.ScrollPanel = function(el, config, content){
54746     config = config || {};
54747     config.fitToFrame = true;
54748     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54749     
54750     this.el.dom.style.overflow = "hidden";
54751     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54752     this.el.removeClass("x-layout-inactive-content");
54753     this.el.on("mousewheel", this.onWheel, this);
54754
54755     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54756     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54757     up.unselectable(); down.unselectable();
54758     up.on("click", this.scrollUp, this);
54759     down.on("click", this.scrollDown, this);
54760     up.addClassOnOver("x-scroller-btn-over");
54761     down.addClassOnOver("x-scroller-btn-over");
54762     up.addClassOnClick("x-scroller-btn-click");
54763     down.addClassOnClick("x-scroller-btn-click");
54764     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54765
54766     this.resizeEl = this.el;
54767     this.el = wrap; this.up = up; this.down = down;
54768 };
54769
54770 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54771     increment : 100,
54772     wheelIncrement : 5,
54773     scrollUp : function(){
54774         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54775     },
54776
54777     scrollDown : function(){
54778         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54779     },
54780
54781     afterScroll : function(){
54782         var el = this.resizeEl;
54783         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54784         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54785         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54786     },
54787
54788     setSize : function(){
54789         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54790         this.afterScroll();
54791     },
54792
54793     onWheel : function(e){
54794         var d = e.getWheelDelta();
54795         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54796         this.afterScroll();
54797         e.stopEvent();
54798     },
54799
54800     setContent : function(content, loadScripts){
54801         this.resizeEl.update(content, loadScripts);
54802     }
54803
54804 });
54805
54806
54807
54808
54809
54810
54811
54812
54813
54814 /**
54815  * @class Roo.TreePanel
54816  * @extends Roo.ContentPanel
54817  * @constructor
54818  * Create a new TreePanel. - defaults to fit/scoll contents.
54819  * @param {String/Object} config A string to set only the panel's title, or a config object
54820  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54821  */
54822 Roo.TreePanel = function(config){
54823     var el = config.el;
54824     var tree = config.tree;
54825     delete config.tree; 
54826     delete config.el; // hopefull!
54827     
54828     // wrapper for IE7 strict & safari scroll issue
54829     
54830     var treeEl = el.createChild();
54831     config.resizeEl = treeEl;
54832     
54833     
54834     
54835     Roo.TreePanel.superclass.constructor.call(this, el, config);
54836  
54837  
54838     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54839     //console.log(tree);
54840     this.on('activate', function()
54841     {
54842         if (this.tree.rendered) {
54843             return;
54844         }
54845         //console.log('render tree');
54846         this.tree.render();
54847     });
54848     // this should not be needed.. - it's actually the 'el' that resizes?
54849     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54850     
54851     //this.on('resize',  function (cp, w, h) {
54852     //        this.tree.innerCt.setWidth(w);
54853     //        this.tree.innerCt.setHeight(h);
54854     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54855     //});
54856
54857         
54858     
54859 };
54860
54861 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54862     fitToFrame : true,
54863     autoScroll : true
54864 });
54865
54866
54867
54868
54869
54870
54871
54872
54873
54874
54875
54876 /*
54877  * Based on:
54878  * Ext JS Library 1.1.1
54879  * Copyright(c) 2006-2007, Ext JS, LLC.
54880  *
54881  * Originally Released Under LGPL - original licence link has changed is not relivant.
54882  *
54883  * Fork - LGPL
54884  * <script type="text/javascript">
54885  */
54886  
54887
54888 /**
54889  * @class Roo.ReaderLayout
54890  * @extends Roo.BorderLayout
54891  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54892  * center region containing two nested regions (a top one for a list view and one for item preview below),
54893  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54894  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54895  * expedites the setup of the overall layout and regions for this common application style.
54896  * Example:
54897  <pre><code>
54898 var reader = new Roo.ReaderLayout();
54899 var CP = Roo.ContentPanel;  // shortcut for adding
54900
54901 reader.beginUpdate();
54902 reader.add("north", new CP("north", "North"));
54903 reader.add("west", new CP("west", {title: "West"}));
54904 reader.add("east", new CP("east", {title: "East"}));
54905
54906 reader.regions.listView.add(new CP("listView", "List"));
54907 reader.regions.preview.add(new CP("preview", "Preview"));
54908 reader.endUpdate();
54909 </code></pre>
54910 * @constructor
54911 * Create a new ReaderLayout
54912 * @param {Object} config Configuration options
54913 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54914 * document.body if omitted)
54915 */
54916 Roo.ReaderLayout = function(config, renderTo){
54917     var c = config || {size:{}};
54918     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54919         north: c.north !== false ? Roo.apply({
54920             split:false,
54921             initialSize: 32,
54922             titlebar: false
54923         }, c.north) : false,
54924         west: c.west !== false ? Roo.apply({
54925             split:true,
54926             initialSize: 200,
54927             minSize: 175,
54928             maxSize: 400,
54929             titlebar: true,
54930             collapsible: true,
54931             animate: true,
54932             margins:{left:5,right:0,bottom:5,top:5},
54933             cmargins:{left:5,right:5,bottom:5,top:5}
54934         }, c.west) : false,
54935         east: c.east !== false ? Roo.apply({
54936             split:true,
54937             initialSize: 200,
54938             minSize: 175,
54939             maxSize: 400,
54940             titlebar: true,
54941             collapsible: true,
54942             animate: true,
54943             margins:{left:0,right:5,bottom:5,top:5},
54944             cmargins:{left:5,right:5,bottom:5,top:5}
54945         }, c.east) : false,
54946         center: Roo.apply({
54947             tabPosition: 'top',
54948             autoScroll:false,
54949             closeOnTab: true,
54950             titlebar:false,
54951             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54952         }, c.center)
54953     });
54954
54955     this.el.addClass('x-reader');
54956
54957     this.beginUpdate();
54958
54959     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54960         south: c.preview !== false ? Roo.apply({
54961             split:true,
54962             initialSize: 200,
54963             minSize: 100,
54964             autoScroll:true,
54965             collapsible:true,
54966             titlebar: true,
54967             cmargins:{top:5,left:0, right:0, bottom:0}
54968         }, c.preview) : false,
54969         center: Roo.apply({
54970             autoScroll:false,
54971             titlebar:false,
54972             minHeight:200
54973         }, c.listView)
54974     });
54975     this.add('center', new Roo.NestedLayoutPanel(inner,
54976             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54977
54978     this.endUpdate();
54979
54980     this.regions.preview = inner.getRegion('south');
54981     this.regions.listView = inner.getRegion('center');
54982 };
54983
54984 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54985  * Based on:
54986  * Ext JS Library 1.1.1
54987  * Copyright(c) 2006-2007, Ext JS, LLC.
54988  *
54989  * Originally Released Under LGPL - original licence link has changed is not relivant.
54990  *
54991  * Fork - LGPL
54992  * <script type="text/javascript">
54993  */
54994  
54995 /**
54996  * @class Roo.grid.Grid
54997  * @extends Roo.util.Observable
54998  * This class represents the primary interface of a component based grid control.
54999  * <br><br>Usage:<pre><code>
55000  var grid = new Roo.grid.Grid("my-container-id", {
55001      ds: myDataStore,
55002      cm: myColModel,
55003      selModel: mySelectionModel,
55004      autoSizeColumns: true,
55005      monitorWindowResize: false,
55006      trackMouseOver: true
55007  });
55008  // set any options
55009  grid.render();
55010  * </code></pre>
55011  * <b>Common Problems:</b><br/>
55012  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55013  * element will correct this<br/>
55014  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55015  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55016  * are unpredictable.<br/>
55017  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55018  * grid to calculate dimensions/offsets.<br/>
55019   * @constructor
55020  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55021  * The container MUST have some type of size defined for the grid to fill. The container will be
55022  * automatically set to position relative if it isn't already.
55023  * @param {Object} config A config object that sets properties on this grid.
55024  */
55025 Roo.grid.Grid = function(container, config){
55026         // initialize the container
55027         this.container = Roo.get(container);
55028         this.container.update("");
55029         this.container.setStyle("overflow", "hidden");
55030     this.container.addClass('x-grid-container');
55031
55032     this.id = this.container.id;
55033
55034     Roo.apply(this, config);
55035     // check and correct shorthanded configs
55036     if(this.ds){
55037         this.dataSource = this.ds;
55038         delete this.ds;
55039     }
55040     if(this.cm){
55041         this.colModel = this.cm;
55042         delete this.cm;
55043     }
55044     if(this.sm){
55045         this.selModel = this.sm;
55046         delete this.sm;
55047     }
55048
55049     if (this.selModel) {
55050         this.selModel = Roo.factory(this.selModel, Roo.grid);
55051         this.sm = this.selModel;
55052         this.sm.xmodule = this.xmodule || false;
55053     }
55054     if (typeof(this.colModel.config) == 'undefined') {
55055         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55056         this.cm = this.colModel;
55057         this.cm.xmodule = this.xmodule || false;
55058     }
55059     if (this.dataSource) {
55060         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55061         this.ds = this.dataSource;
55062         this.ds.xmodule = this.xmodule || false;
55063          
55064     }
55065     
55066     
55067     
55068     if(this.width){
55069         this.container.setWidth(this.width);
55070     }
55071
55072     if(this.height){
55073         this.container.setHeight(this.height);
55074     }
55075     /** @private */
55076         this.addEvents({
55077         // raw events
55078         /**
55079          * @event click
55080          * The raw click event for the entire grid.
55081          * @param {Roo.EventObject} e
55082          */
55083         "click" : true,
55084         /**
55085          * @event dblclick
55086          * The raw dblclick event for the entire grid.
55087          * @param {Roo.EventObject} e
55088          */
55089         "dblclick" : true,
55090         /**
55091          * @event contextmenu
55092          * The raw contextmenu event for the entire grid.
55093          * @param {Roo.EventObject} e
55094          */
55095         "contextmenu" : true,
55096         /**
55097          * @event mousedown
55098          * The raw mousedown event for the entire grid.
55099          * @param {Roo.EventObject} e
55100          */
55101         "mousedown" : true,
55102         /**
55103          * @event mouseup
55104          * The raw mouseup event for the entire grid.
55105          * @param {Roo.EventObject} e
55106          */
55107         "mouseup" : true,
55108         /**
55109          * @event mouseover
55110          * The raw mouseover event for the entire grid.
55111          * @param {Roo.EventObject} e
55112          */
55113         "mouseover" : true,
55114         /**
55115          * @event mouseout
55116          * The raw mouseout event for the entire grid.
55117          * @param {Roo.EventObject} e
55118          */
55119         "mouseout" : true,
55120         /**
55121          * @event keypress
55122          * The raw keypress event for the entire grid.
55123          * @param {Roo.EventObject} e
55124          */
55125         "keypress" : true,
55126         /**
55127          * @event keydown
55128          * The raw keydown event for the entire grid.
55129          * @param {Roo.EventObject} e
55130          */
55131         "keydown" : true,
55132
55133         // custom events
55134
55135         /**
55136          * @event cellclick
55137          * Fires when a cell is clicked
55138          * @param {Grid} this
55139          * @param {Number} rowIndex
55140          * @param {Number} columnIndex
55141          * @param {Roo.EventObject} e
55142          */
55143         "cellclick" : true,
55144         /**
55145          * @event celldblclick
55146          * Fires when a cell is double clicked
55147          * @param {Grid} this
55148          * @param {Number} rowIndex
55149          * @param {Number} columnIndex
55150          * @param {Roo.EventObject} e
55151          */
55152         "celldblclick" : true,
55153         /**
55154          * @event rowclick
55155          * Fires when a row is clicked
55156          * @param {Grid} this
55157          * @param {Number} rowIndex
55158          * @param {Roo.EventObject} e
55159          */
55160         "rowclick" : true,
55161         /**
55162          * @event rowdblclick
55163          * Fires when a row is double clicked
55164          * @param {Grid} this
55165          * @param {Number} rowIndex
55166          * @param {Roo.EventObject} e
55167          */
55168         "rowdblclick" : true,
55169         /**
55170          * @event headerclick
55171          * Fires when a header is clicked
55172          * @param {Grid} this
55173          * @param {Number} columnIndex
55174          * @param {Roo.EventObject} e
55175          */
55176         "headerclick" : true,
55177         /**
55178          * @event headerdblclick
55179          * Fires when a header cell is double clicked
55180          * @param {Grid} this
55181          * @param {Number} columnIndex
55182          * @param {Roo.EventObject} e
55183          */
55184         "headerdblclick" : true,
55185         /**
55186          * @event rowcontextmenu
55187          * Fires when a row is right clicked
55188          * @param {Grid} this
55189          * @param {Number} rowIndex
55190          * @param {Roo.EventObject} e
55191          */
55192         "rowcontextmenu" : true,
55193         /**
55194          * @event cellcontextmenu
55195          * Fires when a cell is right clicked
55196          * @param {Grid} this
55197          * @param {Number} rowIndex
55198          * @param {Number} cellIndex
55199          * @param {Roo.EventObject} e
55200          */
55201          "cellcontextmenu" : true,
55202         /**
55203          * @event headercontextmenu
55204          * Fires when a header is right clicked
55205          * @param {Grid} this
55206          * @param {Number} columnIndex
55207          * @param {Roo.EventObject} e
55208          */
55209         "headercontextmenu" : true,
55210         /**
55211          * @event bodyscroll
55212          * Fires when the body element is scrolled
55213          * @param {Number} scrollLeft
55214          * @param {Number} scrollTop
55215          */
55216         "bodyscroll" : true,
55217         /**
55218          * @event columnresize
55219          * Fires when the user resizes a column
55220          * @param {Number} columnIndex
55221          * @param {Number} newSize
55222          */
55223         "columnresize" : true,
55224         /**
55225          * @event columnmove
55226          * Fires when the user moves a column
55227          * @param {Number} oldIndex
55228          * @param {Number} newIndex
55229          */
55230         "columnmove" : true,
55231         /**
55232          * @event startdrag
55233          * Fires when row(s) start being dragged
55234          * @param {Grid} this
55235          * @param {Roo.GridDD} dd The drag drop object
55236          * @param {event} e The raw browser event
55237          */
55238         "startdrag" : true,
55239         /**
55240          * @event enddrag
55241          * Fires when a drag operation is complete
55242          * @param {Grid} this
55243          * @param {Roo.GridDD} dd The drag drop object
55244          * @param {event} e The raw browser event
55245          */
55246         "enddrag" : true,
55247         /**
55248          * @event dragdrop
55249          * Fires when dragged row(s) are dropped on a valid DD target
55250          * @param {Grid} this
55251          * @param {Roo.GridDD} dd The drag drop object
55252          * @param {String} targetId The target drag drop object
55253          * @param {event} e The raw browser event
55254          */
55255         "dragdrop" : true,
55256         /**
55257          * @event dragover
55258          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55259          * @param {Grid} this
55260          * @param {Roo.GridDD} dd The drag drop object
55261          * @param {String} targetId The target drag drop object
55262          * @param {event} e The raw browser event
55263          */
55264         "dragover" : true,
55265         /**
55266          * @event dragenter
55267          *  Fires when the dragged row(s) first cross another DD target while being dragged
55268          * @param {Grid} this
55269          * @param {Roo.GridDD} dd The drag drop object
55270          * @param {String} targetId The target drag drop object
55271          * @param {event} e The raw browser event
55272          */
55273         "dragenter" : true,
55274         /**
55275          * @event dragout
55276          * Fires when the dragged row(s) leave another DD target while being dragged
55277          * @param {Grid} this
55278          * @param {Roo.GridDD} dd The drag drop object
55279          * @param {String} targetId The target drag drop object
55280          * @param {event} e The raw browser event
55281          */
55282         "dragout" : true,
55283         /**
55284          * @event rowclass
55285          * Fires when a row is rendered, so you can change add a style to it.
55286          * @param {GridView} gridview   The grid view
55287          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55288          */
55289         'rowclass' : true,
55290
55291         /**
55292          * @event render
55293          * Fires when the grid is rendered
55294          * @param {Grid} grid
55295          */
55296         'render' : true
55297     });
55298
55299     Roo.grid.Grid.superclass.constructor.call(this);
55300 };
55301 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55302     
55303     /**
55304      * @cfg {String} ddGroup - drag drop group.
55305      */
55306
55307     /**
55308      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55309      */
55310     minColumnWidth : 25,
55311
55312     /**
55313      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55314      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55315      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55316      */
55317     autoSizeColumns : false,
55318
55319     /**
55320      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55321      */
55322     autoSizeHeaders : true,
55323
55324     /**
55325      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55326      */
55327     monitorWindowResize : true,
55328
55329     /**
55330      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55331      * rows measured to get a columns size. Default is 0 (all rows).
55332      */
55333     maxRowsToMeasure : 0,
55334
55335     /**
55336      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55337      */
55338     trackMouseOver : true,
55339
55340     /**
55341     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55342     */
55343     
55344     /**
55345     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55346     */
55347     enableDragDrop : false,
55348     
55349     /**
55350     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55351     */
55352     enableColumnMove : true,
55353     
55354     /**
55355     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55356     */
55357     enableColumnHide : true,
55358     
55359     /**
55360     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55361     */
55362     enableRowHeightSync : false,
55363     
55364     /**
55365     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55366     */
55367     stripeRows : true,
55368     
55369     /**
55370     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55371     */
55372     autoHeight : false,
55373
55374     /**
55375      * @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.
55376      */
55377     autoExpandColumn : false,
55378
55379     /**
55380     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55381     * Default is 50.
55382     */
55383     autoExpandMin : 50,
55384
55385     /**
55386     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55387     */
55388     autoExpandMax : 1000,
55389
55390     /**
55391     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55392     */
55393     view : null,
55394
55395     /**
55396     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55397     */
55398     loadMask : false,
55399     /**
55400     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55401     */
55402     dropTarget: false,
55403     
55404    
55405     
55406     // private
55407     rendered : false,
55408
55409     /**
55410     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55411     * of a fixed width. Default is false.
55412     */
55413     /**
55414     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55415     */
55416     /**
55417      * Called once after all setup has been completed and the grid is ready to be rendered.
55418      * @return {Roo.grid.Grid} this
55419      */
55420     render : function()
55421     {
55422         var c = this.container;
55423         // try to detect autoHeight/width mode
55424         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55425             this.autoHeight = true;
55426         }
55427         var view = this.getView();
55428         view.init(this);
55429
55430         c.on("click", this.onClick, this);
55431         c.on("dblclick", this.onDblClick, this);
55432         c.on("contextmenu", this.onContextMenu, this);
55433         c.on("keydown", this.onKeyDown, this);
55434         if (Roo.isTouch) {
55435             c.on("touchstart", this.onTouchStart, this);
55436         }
55437
55438         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55439
55440         this.getSelectionModel().init(this);
55441
55442         view.render();
55443
55444         if(this.loadMask){
55445             this.loadMask = new Roo.LoadMask(this.container,
55446                     Roo.apply({store:this.dataSource}, this.loadMask));
55447         }
55448         
55449         
55450         if (this.toolbar && this.toolbar.xtype) {
55451             this.toolbar.container = this.getView().getHeaderPanel(true);
55452             this.toolbar = new Roo.Toolbar(this.toolbar);
55453         }
55454         if (this.footer && this.footer.xtype) {
55455             this.footer.dataSource = this.getDataSource();
55456             this.footer.container = this.getView().getFooterPanel(true);
55457             this.footer = Roo.factory(this.footer, Roo);
55458         }
55459         if (this.dropTarget && this.dropTarget.xtype) {
55460             delete this.dropTarget.xtype;
55461             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55462         }
55463         
55464         
55465         this.rendered = true;
55466         this.fireEvent('render', this);
55467         return this;
55468     },
55469
55470         /**
55471          * Reconfigures the grid to use a different Store and Column Model.
55472          * The View will be bound to the new objects and refreshed.
55473          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55474          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55475          */
55476     reconfigure : function(dataSource, colModel){
55477         if(this.loadMask){
55478             this.loadMask.destroy();
55479             this.loadMask = new Roo.LoadMask(this.container,
55480                     Roo.apply({store:dataSource}, this.loadMask));
55481         }
55482         this.view.bind(dataSource, colModel);
55483         this.dataSource = dataSource;
55484         this.colModel = colModel;
55485         this.view.refresh(true);
55486     },
55487
55488     // private
55489     onKeyDown : function(e){
55490         this.fireEvent("keydown", e);
55491     },
55492
55493     /**
55494      * Destroy this grid.
55495      * @param {Boolean} removeEl True to remove the element
55496      */
55497     destroy : function(removeEl, keepListeners){
55498         if(this.loadMask){
55499             this.loadMask.destroy();
55500         }
55501         var c = this.container;
55502         c.removeAllListeners();
55503         this.view.destroy();
55504         this.colModel.purgeListeners();
55505         if(!keepListeners){
55506             this.purgeListeners();
55507         }
55508         c.update("");
55509         if(removeEl === true){
55510             c.remove();
55511         }
55512     },
55513
55514     // private
55515     processEvent : function(name, e){
55516         // does this fire select???
55517         //Roo.log('grid:processEvent '  + name);
55518         
55519         if (name != 'touchstart' ) {
55520             this.fireEvent(name, e);    
55521         }
55522         
55523         var t = e.getTarget();
55524         var v = this.view;
55525         var header = v.findHeaderIndex(t);
55526         if(header !== false){
55527             var ename = name == 'touchstart' ? 'click' : name;
55528              
55529             this.fireEvent("header" + ename, this, header, e);
55530         }else{
55531             var row = v.findRowIndex(t);
55532             var cell = v.findCellIndex(t);
55533             if (name == 'touchstart') {
55534                 // first touch is always a click.
55535                 // hopefull this happens after selection is updated.?
55536                 name = false;
55537                 
55538                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55539                     var cs = this.selModel.getSelectedCell();
55540                     if (row == cs[0] && cell == cs[1]){
55541                         name = 'dblclick';
55542                     }
55543                 }
55544                 if (typeof(this.selModel.getSelections) != 'undefined') {
55545                     var cs = this.selModel.getSelections();
55546                     var ds = this.dataSource;
55547                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55548                         name = 'dblclick';
55549                     }
55550                 }
55551                 if (!name) {
55552                     return;
55553                 }
55554             }
55555             
55556             
55557             if(row !== false){
55558                 this.fireEvent("row" + name, this, row, e);
55559                 if(cell !== false){
55560                     this.fireEvent("cell" + name, this, row, cell, e);
55561                 }
55562             }
55563         }
55564     },
55565
55566     // private
55567     onClick : function(e){
55568         this.processEvent("click", e);
55569     },
55570    // private
55571     onTouchStart : function(e){
55572         this.processEvent("touchstart", e);
55573     },
55574
55575     // private
55576     onContextMenu : function(e, t){
55577         this.processEvent("contextmenu", e);
55578     },
55579
55580     // private
55581     onDblClick : function(e){
55582         this.processEvent("dblclick", e);
55583     },
55584
55585     // private
55586     walkCells : function(row, col, step, fn, scope){
55587         var cm = this.colModel, clen = cm.getColumnCount();
55588         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55589         if(step < 0){
55590             if(col < 0){
55591                 row--;
55592                 first = false;
55593             }
55594             while(row >= 0){
55595                 if(!first){
55596                     col = clen-1;
55597                 }
55598                 first = false;
55599                 while(col >= 0){
55600                     if(fn.call(scope || this, row, col, cm) === true){
55601                         return [row, col];
55602                     }
55603                     col--;
55604                 }
55605                 row--;
55606             }
55607         } else {
55608             if(col >= clen){
55609                 row++;
55610                 first = false;
55611             }
55612             while(row < rlen){
55613                 if(!first){
55614                     col = 0;
55615                 }
55616                 first = false;
55617                 while(col < clen){
55618                     if(fn.call(scope || this, row, col, cm) === true){
55619                         return [row, col];
55620                     }
55621                     col++;
55622                 }
55623                 row++;
55624             }
55625         }
55626         return null;
55627     },
55628
55629     // private
55630     getSelections : function(){
55631         return this.selModel.getSelections();
55632     },
55633
55634     /**
55635      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55636      * but if manual update is required this method will initiate it.
55637      */
55638     autoSize : function(){
55639         if(this.rendered){
55640             this.view.layout();
55641             if(this.view.adjustForScroll){
55642                 this.view.adjustForScroll();
55643             }
55644         }
55645     },
55646
55647     /**
55648      * Returns the grid's underlying element.
55649      * @return {Element} The element
55650      */
55651     getGridEl : function(){
55652         return this.container;
55653     },
55654
55655     // private for compatibility, overridden by editor grid
55656     stopEditing : function(){},
55657
55658     /**
55659      * Returns the grid's SelectionModel.
55660      * @return {SelectionModel}
55661      */
55662     getSelectionModel : function(){
55663         if(!this.selModel){
55664             this.selModel = new Roo.grid.RowSelectionModel();
55665         }
55666         return this.selModel;
55667     },
55668
55669     /**
55670      * Returns the grid's DataSource.
55671      * @return {DataSource}
55672      */
55673     getDataSource : function(){
55674         return this.dataSource;
55675     },
55676
55677     /**
55678      * Returns the grid's ColumnModel.
55679      * @return {ColumnModel}
55680      */
55681     getColumnModel : function(){
55682         return this.colModel;
55683     },
55684
55685     /**
55686      * Returns the grid's GridView object.
55687      * @return {GridView}
55688      */
55689     getView : function(){
55690         if(!this.view){
55691             this.view = new Roo.grid.GridView(this.viewConfig);
55692         }
55693         return this.view;
55694     },
55695     /**
55696      * Called to get grid's drag proxy text, by default returns this.ddText.
55697      * @return {String}
55698      */
55699     getDragDropText : function(){
55700         var count = this.selModel.getCount();
55701         return String.format(this.ddText, count, count == 1 ? '' : 's');
55702     }
55703 });
55704 /**
55705  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55706  * %0 is replaced with the number of selected rows.
55707  * @type String
55708  */
55709 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55710  * Based on:
55711  * Ext JS Library 1.1.1
55712  * Copyright(c) 2006-2007, Ext JS, LLC.
55713  *
55714  * Originally Released Under LGPL - original licence link has changed is not relivant.
55715  *
55716  * Fork - LGPL
55717  * <script type="text/javascript">
55718  */
55719  
55720 Roo.grid.AbstractGridView = function(){
55721         this.grid = null;
55722         
55723         this.events = {
55724             "beforerowremoved" : true,
55725             "beforerowsinserted" : true,
55726             "beforerefresh" : true,
55727             "rowremoved" : true,
55728             "rowsinserted" : true,
55729             "rowupdated" : true,
55730             "refresh" : true
55731         };
55732     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55733 };
55734
55735 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55736     rowClass : "x-grid-row",
55737     cellClass : "x-grid-cell",
55738     tdClass : "x-grid-td",
55739     hdClass : "x-grid-hd",
55740     splitClass : "x-grid-hd-split",
55741     
55742     init: function(grid){
55743         this.grid = grid;
55744                 var cid = this.grid.getGridEl().id;
55745         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55746         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55747         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55748         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55749         },
55750         
55751     getColumnRenderers : function(){
55752         var renderers = [];
55753         var cm = this.grid.colModel;
55754         var colCount = cm.getColumnCount();
55755         for(var i = 0; i < colCount; i++){
55756             renderers[i] = cm.getRenderer(i);
55757         }
55758         return renderers;
55759     },
55760     
55761     getColumnIds : function(){
55762         var ids = [];
55763         var cm = this.grid.colModel;
55764         var colCount = cm.getColumnCount();
55765         for(var i = 0; i < colCount; i++){
55766             ids[i] = cm.getColumnId(i);
55767         }
55768         return ids;
55769     },
55770     
55771     getDataIndexes : function(){
55772         if(!this.indexMap){
55773             this.indexMap = this.buildIndexMap();
55774         }
55775         return this.indexMap.colToData;
55776     },
55777     
55778     getColumnIndexByDataIndex : function(dataIndex){
55779         if(!this.indexMap){
55780             this.indexMap = this.buildIndexMap();
55781         }
55782         return this.indexMap.dataToCol[dataIndex];
55783     },
55784     
55785     /**
55786      * Set a css style for a column dynamically. 
55787      * @param {Number} colIndex The index of the column
55788      * @param {String} name The css property name
55789      * @param {String} value The css value
55790      */
55791     setCSSStyle : function(colIndex, name, value){
55792         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55793         Roo.util.CSS.updateRule(selector, name, value);
55794     },
55795     
55796     generateRules : function(cm){
55797         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55798         Roo.util.CSS.removeStyleSheet(rulesId);
55799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55800             var cid = cm.getColumnId(i);
55801             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55802                          this.tdSelector, cid, " {\n}\n",
55803                          this.hdSelector, cid, " {\n}\n",
55804                          this.splitSelector, cid, " {\n}\n");
55805         }
55806         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55807     }
55808 });/*
55809  * Based on:
55810  * Ext JS Library 1.1.1
55811  * Copyright(c) 2006-2007, Ext JS, LLC.
55812  *
55813  * Originally Released Under LGPL - original licence link has changed is not relivant.
55814  *
55815  * Fork - LGPL
55816  * <script type="text/javascript">
55817  */
55818
55819 // private
55820 // This is a support class used internally by the Grid components
55821 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55822     this.grid = grid;
55823     this.view = grid.getView();
55824     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55825     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55826     if(hd2){
55827         this.setHandleElId(Roo.id(hd));
55828         this.setOuterHandleElId(Roo.id(hd2));
55829     }
55830     this.scroll = false;
55831 };
55832 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55833     maxDragWidth: 120,
55834     getDragData : function(e){
55835         var t = Roo.lib.Event.getTarget(e);
55836         var h = this.view.findHeaderCell(t);
55837         if(h){
55838             return {ddel: h.firstChild, header:h};
55839         }
55840         return false;
55841     },
55842
55843     onInitDrag : function(e){
55844         this.view.headersDisabled = true;
55845         var clone = this.dragData.ddel.cloneNode(true);
55846         clone.id = Roo.id();
55847         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55848         this.proxy.update(clone);
55849         return true;
55850     },
55851
55852     afterValidDrop : function(){
55853         var v = this.view;
55854         setTimeout(function(){
55855             v.headersDisabled = false;
55856         }, 50);
55857     },
55858
55859     afterInvalidDrop : function(){
55860         var v = this.view;
55861         setTimeout(function(){
55862             v.headersDisabled = false;
55863         }, 50);
55864     }
55865 });
55866 /*
55867  * Based on:
55868  * Ext JS Library 1.1.1
55869  * Copyright(c) 2006-2007, Ext JS, LLC.
55870  *
55871  * Originally Released Under LGPL - original licence link has changed is not relivant.
55872  *
55873  * Fork - LGPL
55874  * <script type="text/javascript">
55875  */
55876 // private
55877 // This is a support class used internally by the Grid components
55878 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55879     this.grid = grid;
55880     this.view = grid.getView();
55881     // split the proxies so they don't interfere with mouse events
55882     this.proxyTop = Roo.DomHelper.append(document.body, {
55883         cls:"col-move-top", html:"&#160;"
55884     }, true);
55885     this.proxyBottom = Roo.DomHelper.append(document.body, {
55886         cls:"col-move-bottom", html:"&#160;"
55887     }, true);
55888     this.proxyTop.hide = this.proxyBottom.hide = function(){
55889         this.setLeftTop(-100,-100);
55890         this.setStyle("visibility", "hidden");
55891     };
55892     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55893     // temporarily disabled
55894     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55895     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55896 };
55897 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55898     proxyOffsets : [-4, -9],
55899     fly: Roo.Element.fly,
55900
55901     getTargetFromEvent : function(e){
55902         var t = Roo.lib.Event.getTarget(e);
55903         var cindex = this.view.findCellIndex(t);
55904         if(cindex !== false){
55905             return this.view.getHeaderCell(cindex);
55906         }
55907         return null;
55908     },
55909
55910     nextVisible : function(h){
55911         var v = this.view, cm = this.grid.colModel;
55912         h = h.nextSibling;
55913         while(h){
55914             if(!cm.isHidden(v.getCellIndex(h))){
55915                 return h;
55916             }
55917             h = h.nextSibling;
55918         }
55919         return null;
55920     },
55921
55922     prevVisible : function(h){
55923         var v = this.view, cm = this.grid.colModel;
55924         h = h.prevSibling;
55925         while(h){
55926             if(!cm.isHidden(v.getCellIndex(h))){
55927                 return h;
55928             }
55929             h = h.prevSibling;
55930         }
55931         return null;
55932     },
55933
55934     positionIndicator : function(h, n, e){
55935         var x = Roo.lib.Event.getPageX(e);
55936         var r = Roo.lib.Dom.getRegion(n.firstChild);
55937         var px, pt, py = r.top + this.proxyOffsets[1];
55938         if((r.right - x) <= (r.right-r.left)/2){
55939             px = r.right+this.view.borderWidth;
55940             pt = "after";
55941         }else{
55942             px = r.left;
55943             pt = "before";
55944         }
55945         var oldIndex = this.view.getCellIndex(h);
55946         var newIndex = this.view.getCellIndex(n);
55947
55948         if(this.grid.colModel.isFixed(newIndex)){
55949             return false;
55950         }
55951
55952         var locked = this.grid.colModel.isLocked(newIndex);
55953
55954         if(pt == "after"){
55955             newIndex++;
55956         }
55957         if(oldIndex < newIndex){
55958             newIndex--;
55959         }
55960         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55961             return false;
55962         }
55963         px +=  this.proxyOffsets[0];
55964         this.proxyTop.setLeftTop(px, py);
55965         this.proxyTop.show();
55966         if(!this.bottomOffset){
55967             this.bottomOffset = this.view.mainHd.getHeight();
55968         }
55969         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55970         this.proxyBottom.show();
55971         return pt;
55972     },
55973
55974     onNodeEnter : function(n, dd, e, data){
55975         if(data.header != n){
55976             this.positionIndicator(data.header, n, e);
55977         }
55978     },
55979
55980     onNodeOver : function(n, dd, e, data){
55981         var result = false;
55982         if(data.header != n){
55983             result = this.positionIndicator(data.header, n, e);
55984         }
55985         if(!result){
55986             this.proxyTop.hide();
55987             this.proxyBottom.hide();
55988         }
55989         return result ? this.dropAllowed : this.dropNotAllowed;
55990     },
55991
55992     onNodeOut : function(n, dd, e, data){
55993         this.proxyTop.hide();
55994         this.proxyBottom.hide();
55995     },
55996
55997     onNodeDrop : function(n, dd, e, data){
55998         var h = data.header;
55999         if(h != n){
56000             var cm = this.grid.colModel;
56001             var x = Roo.lib.Event.getPageX(e);
56002             var r = Roo.lib.Dom.getRegion(n.firstChild);
56003             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56004             var oldIndex = this.view.getCellIndex(h);
56005             var newIndex = this.view.getCellIndex(n);
56006             var locked = cm.isLocked(newIndex);
56007             if(pt == "after"){
56008                 newIndex++;
56009             }
56010             if(oldIndex < newIndex){
56011                 newIndex--;
56012             }
56013             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56014                 return false;
56015             }
56016             cm.setLocked(oldIndex, locked, true);
56017             cm.moveColumn(oldIndex, newIndex);
56018             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56019             return true;
56020         }
56021         return false;
56022     }
56023 });
56024 /*
56025  * Based on:
56026  * Ext JS Library 1.1.1
56027  * Copyright(c) 2006-2007, Ext JS, LLC.
56028  *
56029  * Originally Released Under LGPL - original licence link has changed is not relivant.
56030  *
56031  * Fork - LGPL
56032  * <script type="text/javascript">
56033  */
56034   
56035 /**
56036  * @class Roo.grid.GridView
56037  * @extends Roo.util.Observable
56038  *
56039  * @constructor
56040  * @param {Object} config
56041  */
56042 Roo.grid.GridView = function(config){
56043     Roo.grid.GridView.superclass.constructor.call(this);
56044     this.el = null;
56045
56046     Roo.apply(this, config);
56047 };
56048
56049 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56050
56051     unselectable :  'unselectable="on"',
56052     unselectableCls :  'x-unselectable',
56053     
56054     
56055     rowClass : "x-grid-row",
56056
56057     cellClass : "x-grid-col",
56058
56059     tdClass : "x-grid-td",
56060
56061     hdClass : "x-grid-hd",
56062
56063     splitClass : "x-grid-split",
56064
56065     sortClasses : ["sort-asc", "sort-desc"],
56066
56067     enableMoveAnim : false,
56068
56069     hlColor: "C3DAF9",
56070
56071     dh : Roo.DomHelper,
56072
56073     fly : Roo.Element.fly,
56074
56075     css : Roo.util.CSS,
56076
56077     borderWidth: 1,
56078
56079     splitOffset: 3,
56080
56081     scrollIncrement : 22,
56082
56083     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56084
56085     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56086
56087     bind : function(ds, cm){
56088         if(this.ds){
56089             this.ds.un("load", this.onLoad, this);
56090             this.ds.un("datachanged", this.onDataChange, this);
56091             this.ds.un("add", this.onAdd, this);
56092             this.ds.un("remove", this.onRemove, this);
56093             this.ds.un("update", this.onUpdate, this);
56094             this.ds.un("clear", this.onClear, this);
56095         }
56096         if(ds){
56097             ds.on("load", this.onLoad, this);
56098             ds.on("datachanged", this.onDataChange, this);
56099             ds.on("add", this.onAdd, this);
56100             ds.on("remove", this.onRemove, this);
56101             ds.on("update", this.onUpdate, this);
56102             ds.on("clear", this.onClear, this);
56103         }
56104         this.ds = ds;
56105
56106         if(this.cm){
56107             this.cm.un("widthchange", this.onColWidthChange, this);
56108             this.cm.un("headerchange", this.onHeaderChange, this);
56109             this.cm.un("hiddenchange", this.onHiddenChange, this);
56110             this.cm.un("columnmoved", this.onColumnMove, this);
56111             this.cm.un("columnlockchange", this.onColumnLock, this);
56112         }
56113         if(cm){
56114             this.generateRules(cm);
56115             cm.on("widthchange", this.onColWidthChange, this);
56116             cm.on("headerchange", this.onHeaderChange, this);
56117             cm.on("hiddenchange", this.onHiddenChange, this);
56118             cm.on("columnmoved", this.onColumnMove, this);
56119             cm.on("columnlockchange", this.onColumnLock, this);
56120         }
56121         this.cm = cm;
56122     },
56123
56124     init: function(grid){
56125         Roo.grid.GridView.superclass.init.call(this, grid);
56126
56127         this.bind(grid.dataSource, grid.colModel);
56128
56129         grid.on("headerclick", this.handleHeaderClick, this);
56130
56131         if(grid.trackMouseOver){
56132             grid.on("mouseover", this.onRowOver, this);
56133             grid.on("mouseout", this.onRowOut, this);
56134         }
56135         grid.cancelTextSelection = function(){};
56136         this.gridId = grid.id;
56137
56138         var tpls = this.templates || {};
56139
56140         if(!tpls.master){
56141             tpls.master = new Roo.Template(
56142                '<div class="x-grid" hidefocus="true">',
56143                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56144                   '<div class="x-grid-topbar"></div>',
56145                   '<div class="x-grid-scroller"><div></div></div>',
56146                   '<div class="x-grid-locked">',
56147                       '<div class="x-grid-header">{lockedHeader}</div>',
56148                       '<div class="x-grid-body">{lockedBody}</div>',
56149                   "</div>",
56150                   '<div class="x-grid-viewport">',
56151                       '<div class="x-grid-header">{header}</div>',
56152                       '<div class="x-grid-body">{body}</div>',
56153                   "</div>",
56154                   '<div class="x-grid-bottombar"></div>',
56155                  
56156                   '<div class="x-grid-resize-proxy">&#160;</div>',
56157                "</div>"
56158             );
56159             tpls.master.disableformats = true;
56160         }
56161
56162         if(!tpls.header){
56163             tpls.header = new Roo.Template(
56164                '<table border="0" cellspacing="0" cellpadding="0">',
56165                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56166                "</table>{splits}"
56167             );
56168             tpls.header.disableformats = true;
56169         }
56170         tpls.header.compile();
56171
56172         if(!tpls.hcell){
56173             tpls.hcell = new Roo.Template(
56174                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56175                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56176                 "</div></td>"
56177              );
56178              tpls.hcell.disableFormats = true;
56179         }
56180         tpls.hcell.compile();
56181
56182         if(!tpls.hsplit){
56183             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56184                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56185             tpls.hsplit.disableFormats = true;
56186         }
56187         tpls.hsplit.compile();
56188
56189         if(!tpls.body){
56190             tpls.body = new Roo.Template(
56191                '<table border="0" cellspacing="0" cellpadding="0">',
56192                "<tbody>{rows}</tbody>",
56193                "</table>"
56194             );
56195             tpls.body.disableFormats = true;
56196         }
56197         tpls.body.compile();
56198
56199         if(!tpls.row){
56200             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56201             tpls.row.disableFormats = true;
56202         }
56203         tpls.row.compile();
56204
56205         if(!tpls.cell){
56206             tpls.cell = new Roo.Template(
56207                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56208                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56209                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56210                 "</td>"
56211             );
56212             tpls.cell.disableFormats = true;
56213         }
56214         tpls.cell.compile();
56215
56216         this.templates = tpls;
56217     },
56218
56219     // remap these for backwards compat
56220     onColWidthChange : function(){
56221         this.updateColumns.apply(this, arguments);
56222     },
56223     onHeaderChange : function(){
56224         this.updateHeaders.apply(this, arguments);
56225     }, 
56226     onHiddenChange : function(){
56227         this.handleHiddenChange.apply(this, arguments);
56228     },
56229     onColumnMove : function(){
56230         this.handleColumnMove.apply(this, arguments);
56231     },
56232     onColumnLock : function(){
56233         this.handleLockChange.apply(this, arguments);
56234     },
56235
56236     onDataChange : function(){
56237         this.refresh();
56238         this.updateHeaderSortState();
56239     },
56240
56241     onClear : function(){
56242         this.refresh();
56243     },
56244
56245     onUpdate : function(ds, record){
56246         this.refreshRow(record);
56247     },
56248
56249     refreshRow : function(record){
56250         var ds = this.ds, index;
56251         if(typeof record == 'number'){
56252             index = record;
56253             record = ds.getAt(index);
56254         }else{
56255             index = ds.indexOf(record);
56256         }
56257         this.insertRows(ds, index, index, true);
56258         this.onRemove(ds, record, index+1, true);
56259         this.syncRowHeights(index, index);
56260         this.layout();
56261         this.fireEvent("rowupdated", this, index, record);
56262     },
56263
56264     onAdd : function(ds, records, index){
56265         this.insertRows(ds, index, index + (records.length-1));
56266     },
56267
56268     onRemove : function(ds, record, index, isUpdate){
56269         if(isUpdate !== true){
56270             this.fireEvent("beforerowremoved", this, index, record);
56271         }
56272         var bt = this.getBodyTable(), lt = this.getLockedTable();
56273         if(bt.rows[index]){
56274             bt.firstChild.removeChild(bt.rows[index]);
56275         }
56276         if(lt.rows[index]){
56277             lt.firstChild.removeChild(lt.rows[index]);
56278         }
56279         if(isUpdate !== true){
56280             this.stripeRows(index);
56281             this.syncRowHeights(index, index);
56282             this.layout();
56283             this.fireEvent("rowremoved", this, index, record);
56284         }
56285     },
56286
56287     onLoad : function(){
56288         this.scrollToTop();
56289     },
56290
56291     /**
56292      * Scrolls the grid to the top
56293      */
56294     scrollToTop : function(){
56295         if(this.scroller){
56296             this.scroller.dom.scrollTop = 0;
56297             this.syncScroll();
56298         }
56299     },
56300
56301     /**
56302      * Gets a panel in the header of the grid that can be used for toolbars etc.
56303      * After modifying the contents of this panel a call to grid.autoSize() may be
56304      * required to register any changes in size.
56305      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56306      * @return Roo.Element
56307      */
56308     getHeaderPanel : function(doShow){
56309         if(doShow){
56310             this.headerPanel.show();
56311         }
56312         return this.headerPanel;
56313     },
56314
56315     /**
56316      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56317      * After modifying the contents of this panel a call to grid.autoSize() may be
56318      * required to register any changes in size.
56319      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56320      * @return Roo.Element
56321      */
56322     getFooterPanel : function(doShow){
56323         if(doShow){
56324             this.footerPanel.show();
56325         }
56326         return this.footerPanel;
56327     },
56328
56329     initElements : function(){
56330         var E = Roo.Element;
56331         var el = this.grid.getGridEl().dom.firstChild;
56332         var cs = el.childNodes;
56333
56334         this.el = new E(el);
56335         
56336          this.focusEl = new E(el.firstChild);
56337         this.focusEl.swallowEvent("click", true);
56338         
56339         this.headerPanel = new E(cs[1]);
56340         this.headerPanel.enableDisplayMode("block");
56341
56342         this.scroller = new E(cs[2]);
56343         this.scrollSizer = new E(this.scroller.dom.firstChild);
56344
56345         this.lockedWrap = new E(cs[3]);
56346         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56347         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56348
56349         this.mainWrap = new E(cs[4]);
56350         this.mainHd = new E(this.mainWrap.dom.firstChild);
56351         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56352
56353         this.footerPanel = new E(cs[5]);
56354         this.footerPanel.enableDisplayMode("block");
56355
56356         this.resizeProxy = new E(cs[6]);
56357
56358         this.headerSelector = String.format(
56359            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56360            this.lockedHd.id, this.mainHd.id
56361         );
56362
56363         this.splitterSelector = String.format(
56364            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56365            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56366         );
56367     },
56368     idToCssName : function(s)
56369     {
56370         return s.replace(/[^a-z0-9]+/ig, '-');
56371     },
56372
56373     getHeaderCell : function(index){
56374         return Roo.DomQuery.select(this.headerSelector)[index];
56375     },
56376
56377     getHeaderCellMeasure : function(index){
56378         return this.getHeaderCell(index).firstChild;
56379     },
56380
56381     getHeaderCellText : function(index){
56382         return this.getHeaderCell(index).firstChild.firstChild;
56383     },
56384
56385     getLockedTable : function(){
56386         return this.lockedBody.dom.firstChild;
56387     },
56388
56389     getBodyTable : function(){
56390         return this.mainBody.dom.firstChild;
56391     },
56392
56393     getLockedRow : function(index){
56394         return this.getLockedTable().rows[index];
56395     },
56396
56397     getRow : function(index){
56398         return this.getBodyTable().rows[index];
56399     },
56400
56401     getRowComposite : function(index){
56402         if(!this.rowEl){
56403             this.rowEl = new Roo.CompositeElementLite();
56404         }
56405         var els = [], lrow, mrow;
56406         if(lrow = this.getLockedRow(index)){
56407             els.push(lrow);
56408         }
56409         if(mrow = this.getRow(index)){
56410             els.push(mrow);
56411         }
56412         this.rowEl.elements = els;
56413         return this.rowEl;
56414     },
56415     /**
56416      * Gets the 'td' of the cell
56417      * 
56418      * @param {Integer} rowIndex row to select
56419      * @param {Integer} colIndex column to select
56420      * 
56421      * @return {Object} 
56422      */
56423     getCell : function(rowIndex, colIndex){
56424         var locked = this.cm.getLockedCount();
56425         var source;
56426         if(colIndex < locked){
56427             source = this.lockedBody.dom.firstChild;
56428         }else{
56429             source = this.mainBody.dom.firstChild;
56430             colIndex -= locked;
56431         }
56432         return source.rows[rowIndex].childNodes[colIndex];
56433     },
56434
56435     getCellText : function(rowIndex, colIndex){
56436         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56437     },
56438
56439     getCellBox : function(cell){
56440         var b = this.fly(cell).getBox();
56441         if(Roo.isOpera){ // opera fails to report the Y
56442             b.y = cell.offsetTop + this.mainBody.getY();
56443         }
56444         return b;
56445     },
56446
56447     getCellIndex : function(cell){
56448         var id = String(cell.className).match(this.cellRE);
56449         if(id){
56450             return parseInt(id[1], 10);
56451         }
56452         return 0;
56453     },
56454
56455     findHeaderIndex : function(n){
56456         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56457         return r ? this.getCellIndex(r) : false;
56458     },
56459
56460     findHeaderCell : function(n){
56461         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56462         return r ? r : false;
56463     },
56464
56465     findRowIndex : function(n){
56466         if(!n){
56467             return false;
56468         }
56469         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56470         return r ? r.rowIndex : false;
56471     },
56472
56473     findCellIndex : function(node){
56474         var stop = this.el.dom;
56475         while(node && node != stop){
56476             if(this.findRE.test(node.className)){
56477                 return this.getCellIndex(node);
56478             }
56479             node = node.parentNode;
56480         }
56481         return false;
56482     },
56483
56484     getColumnId : function(index){
56485         return this.cm.getColumnId(index);
56486     },
56487
56488     getSplitters : function()
56489     {
56490         if(this.splitterSelector){
56491            return Roo.DomQuery.select(this.splitterSelector);
56492         }else{
56493             return null;
56494       }
56495     },
56496
56497     getSplitter : function(index){
56498         return this.getSplitters()[index];
56499     },
56500
56501     onRowOver : function(e, t){
56502         var row;
56503         if((row = this.findRowIndex(t)) !== false){
56504             this.getRowComposite(row).addClass("x-grid-row-over");
56505         }
56506     },
56507
56508     onRowOut : function(e, t){
56509         var row;
56510         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56511             this.getRowComposite(row).removeClass("x-grid-row-over");
56512         }
56513     },
56514
56515     renderHeaders : function(){
56516         var cm = this.cm;
56517         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56518         var cb = [], lb = [], sb = [], lsb = [], p = {};
56519         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56520             p.cellId = "x-grid-hd-0-" + i;
56521             p.splitId = "x-grid-csplit-0-" + i;
56522             p.id = cm.getColumnId(i);
56523             p.value = cm.getColumnHeader(i) || "";
56524             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56525             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56526             if(!cm.isLocked(i)){
56527                 cb[cb.length] = ct.apply(p);
56528                 sb[sb.length] = st.apply(p);
56529             }else{
56530                 lb[lb.length] = ct.apply(p);
56531                 lsb[lsb.length] = st.apply(p);
56532             }
56533         }
56534         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56535                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56536     },
56537
56538     updateHeaders : function(){
56539         var html = this.renderHeaders();
56540         this.lockedHd.update(html[0]);
56541         this.mainHd.update(html[1]);
56542     },
56543
56544     /**
56545      * Focuses the specified row.
56546      * @param {Number} row The row index
56547      */
56548     focusRow : function(row)
56549     {
56550         //Roo.log('GridView.focusRow');
56551         var x = this.scroller.dom.scrollLeft;
56552         this.focusCell(row, 0, false);
56553         this.scroller.dom.scrollLeft = x;
56554     },
56555
56556     /**
56557      * Focuses the specified cell.
56558      * @param {Number} row The row index
56559      * @param {Number} col The column index
56560      * @param {Boolean} hscroll false to disable horizontal scrolling
56561      */
56562     focusCell : function(row, col, hscroll)
56563     {
56564         //Roo.log('GridView.focusCell');
56565         var el = this.ensureVisible(row, col, hscroll);
56566         this.focusEl.alignTo(el, "tl-tl");
56567         if(Roo.isGecko){
56568             this.focusEl.focus();
56569         }else{
56570             this.focusEl.focus.defer(1, this.focusEl);
56571         }
56572     },
56573
56574     /**
56575      * Scrolls the specified cell into view
56576      * @param {Number} row The row index
56577      * @param {Number} col The column index
56578      * @param {Boolean} hscroll false to disable horizontal scrolling
56579      */
56580     ensureVisible : function(row, col, hscroll)
56581     {
56582         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56583         //return null; //disable for testing.
56584         if(typeof row != "number"){
56585             row = row.rowIndex;
56586         }
56587         if(row < 0 && row >= this.ds.getCount()){
56588             return  null;
56589         }
56590         col = (col !== undefined ? col : 0);
56591         var cm = this.grid.colModel;
56592         while(cm.isHidden(col)){
56593             col++;
56594         }
56595
56596         var el = this.getCell(row, col);
56597         if(!el){
56598             return null;
56599         }
56600         var c = this.scroller.dom;
56601
56602         var ctop = parseInt(el.offsetTop, 10);
56603         var cleft = parseInt(el.offsetLeft, 10);
56604         var cbot = ctop + el.offsetHeight;
56605         var cright = cleft + el.offsetWidth;
56606         
56607         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56608         var stop = parseInt(c.scrollTop, 10);
56609         var sleft = parseInt(c.scrollLeft, 10);
56610         var sbot = stop + ch;
56611         var sright = sleft + c.clientWidth;
56612         /*
56613         Roo.log('GridView.ensureVisible:' +
56614                 ' ctop:' + ctop +
56615                 ' c.clientHeight:' + c.clientHeight +
56616                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56617                 ' stop:' + stop +
56618                 ' cbot:' + cbot +
56619                 ' sbot:' + sbot +
56620                 ' ch:' + ch  
56621                 );
56622         */
56623         if(ctop < stop){
56624              c.scrollTop = ctop;
56625             //Roo.log("set scrolltop to ctop DISABLE?");
56626         }else if(cbot > sbot){
56627             //Roo.log("set scrolltop to cbot-ch");
56628             c.scrollTop = cbot-ch;
56629         }
56630         
56631         if(hscroll !== false){
56632             if(cleft < sleft){
56633                 c.scrollLeft = cleft;
56634             }else if(cright > sright){
56635                 c.scrollLeft = cright-c.clientWidth;
56636             }
56637         }
56638          
56639         return el;
56640     },
56641
56642     updateColumns : function(){
56643         this.grid.stopEditing();
56644         var cm = this.grid.colModel, colIds = this.getColumnIds();
56645         //var totalWidth = cm.getTotalWidth();
56646         var pos = 0;
56647         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56648             //if(cm.isHidden(i)) continue;
56649             var w = cm.getColumnWidth(i);
56650             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56651             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56652         }
56653         this.updateSplitters();
56654     },
56655
56656     generateRules : function(cm){
56657         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56658         Roo.util.CSS.removeStyleSheet(rulesId);
56659         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56660             var cid = cm.getColumnId(i);
56661             var align = '';
56662             if(cm.config[i].align){
56663                 align = 'text-align:'+cm.config[i].align+';';
56664             }
56665             var hidden = '';
56666             if(cm.isHidden(i)){
56667                 hidden = 'display:none;';
56668             }
56669             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56670             ruleBuf.push(
56671                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56672                     this.hdSelector, cid, " {\n", align, width, "}\n",
56673                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56674                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56675         }
56676         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56677     },
56678
56679     updateSplitters : function(){
56680         var cm = this.cm, s = this.getSplitters();
56681         if(s){ // splitters not created yet
56682             var pos = 0, locked = true;
56683             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56684                 if(cm.isHidden(i)) {
56685                     continue;
56686                 }
56687                 var w = cm.getColumnWidth(i); // make sure it's a number
56688                 if(!cm.isLocked(i) && locked){
56689                     pos = 0;
56690                     locked = false;
56691                 }
56692                 pos += w;
56693                 s[i].style.left = (pos-this.splitOffset) + "px";
56694             }
56695         }
56696     },
56697
56698     handleHiddenChange : function(colModel, colIndex, hidden){
56699         if(hidden){
56700             this.hideColumn(colIndex);
56701         }else{
56702             this.unhideColumn(colIndex);
56703         }
56704     },
56705
56706     hideColumn : function(colIndex){
56707         var cid = this.getColumnId(colIndex);
56708         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56709         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56710         if(Roo.isSafari){
56711             this.updateHeaders();
56712         }
56713         this.updateSplitters();
56714         this.layout();
56715     },
56716
56717     unhideColumn : function(colIndex){
56718         var cid = this.getColumnId(colIndex);
56719         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56720         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56721
56722         if(Roo.isSafari){
56723             this.updateHeaders();
56724         }
56725         this.updateSplitters();
56726         this.layout();
56727     },
56728
56729     insertRows : function(dm, firstRow, lastRow, isUpdate){
56730         if(firstRow == 0 && lastRow == dm.getCount()-1){
56731             this.refresh();
56732         }else{
56733             if(!isUpdate){
56734                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56735             }
56736             var s = this.getScrollState();
56737             var markup = this.renderRows(firstRow, lastRow);
56738             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56739             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56740             this.restoreScroll(s);
56741             if(!isUpdate){
56742                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56743                 this.syncRowHeights(firstRow, lastRow);
56744                 this.stripeRows(firstRow);
56745                 this.layout();
56746             }
56747         }
56748     },
56749
56750     bufferRows : function(markup, target, index){
56751         var before = null, trows = target.rows, tbody = target.tBodies[0];
56752         if(index < trows.length){
56753             before = trows[index];
56754         }
56755         var b = document.createElement("div");
56756         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56757         var rows = b.firstChild.rows;
56758         for(var i = 0, len = rows.length; i < len; i++){
56759             if(before){
56760                 tbody.insertBefore(rows[0], before);
56761             }else{
56762                 tbody.appendChild(rows[0]);
56763             }
56764         }
56765         b.innerHTML = "";
56766         b = null;
56767     },
56768
56769     deleteRows : function(dm, firstRow, lastRow){
56770         if(dm.getRowCount()<1){
56771             this.fireEvent("beforerefresh", this);
56772             this.mainBody.update("");
56773             this.lockedBody.update("");
56774             this.fireEvent("refresh", this);
56775         }else{
56776             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56777             var bt = this.getBodyTable();
56778             var tbody = bt.firstChild;
56779             var rows = bt.rows;
56780             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56781                 tbody.removeChild(rows[firstRow]);
56782             }
56783             this.stripeRows(firstRow);
56784             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56785         }
56786     },
56787
56788     updateRows : function(dataSource, firstRow, lastRow){
56789         var s = this.getScrollState();
56790         this.refresh();
56791         this.restoreScroll(s);
56792     },
56793
56794     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56795         if(!noRefresh){
56796            this.refresh();
56797         }
56798         this.updateHeaderSortState();
56799     },
56800
56801     getScrollState : function(){
56802         
56803         var sb = this.scroller.dom;
56804         return {left: sb.scrollLeft, top: sb.scrollTop};
56805     },
56806
56807     stripeRows : function(startRow){
56808         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56809             return;
56810         }
56811         startRow = startRow || 0;
56812         var rows = this.getBodyTable().rows;
56813         var lrows = this.getLockedTable().rows;
56814         var cls = ' x-grid-row-alt ';
56815         for(var i = startRow, len = rows.length; i < len; i++){
56816             var row = rows[i], lrow = lrows[i];
56817             var isAlt = ((i+1) % 2 == 0);
56818             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56819             if(isAlt == hasAlt){
56820                 continue;
56821             }
56822             if(isAlt){
56823                 row.className += " x-grid-row-alt";
56824             }else{
56825                 row.className = row.className.replace("x-grid-row-alt", "");
56826             }
56827             if(lrow){
56828                 lrow.className = row.className;
56829             }
56830         }
56831     },
56832
56833     restoreScroll : function(state){
56834         //Roo.log('GridView.restoreScroll');
56835         var sb = this.scroller.dom;
56836         sb.scrollLeft = state.left;
56837         sb.scrollTop = state.top;
56838         this.syncScroll();
56839     },
56840
56841     syncScroll : function(){
56842         //Roo.log('GridView.syncScroll');
56843         var sb = this.scroller.dom;
56844         var sh = this.mainHd.dom;
56845         var bs = this.mainBody.dom;
56846         var lv = this.lockedBody.dom;
56847         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56848         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56849     },
56850
56851     handleScroll : function(e){
56852         this.syncScroll();
56853         var sb = this.scroller.dom;
56854         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56855         e.stopEvent();
56856     },
56857
56858     handleWheel : function(e){
56859         var d = e.getWheelDelta();
56860         this.scroller.dom.scrollTop -= d*22;
56861         // set this here to prevent jumpy scrolling on large tables
56862         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56863         e.stopEvent();
56864     },
56865
56866     renderRows : function(startRow, endRow){
56867         // pull in all the crap needed to render rows
56868         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56869         var colCount = cm.getColumnCount();
56870
56871         if(ds.getCount() < 1){
56872             return ["", ""];
56873         }
56874
56875         // build a map for all the columns
56876         var cs = [];
56877         for(var i = 0; i < colCount; i++){
56878             var name = cm.getDataIndex(i);
56879             cs[i] = {
56880                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56881                 renderer : cm.getRenderer(i),
56882                 id : cm.getColumnId(i),
56883                 locked : cm.isLocked(i),
56884                 has_editor : cm.isCellEditable(i)
56885             };
56886         }
56887
56888         startRow = startRow || 0;
56889         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56890
56891         // records to render
56892         var rs = ds.getRange(startRow, endRow);
56893
56894         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56895     },
56896
56897     // As much as I hate to duplicate code, this was branched because FireFox really hates
56898     // [].join("") on strings. The performance difference was substantial enough to
56899     // branch this function
56900     doRender : Roo.isGecko ?
56901             function(cs, rs, ds, startRow, colCount, stripe){
56902                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56903                 // buffers
56904                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56905                 
56906                 var hasListener = this.grid.hasListener('rowclass');
56907                 var rowcfg = {};
56908                 for(var j = 0, len = rs.length; j < len; j++){
56909                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56910                     for(var i = 0; i < colCount; i++){
56911                         c = cs[i];
56912                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56913                         p.id = c.id;
56914                         p.css = p.attr = "";
56915                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56916                         if(p.value == undefined || p.value === "") {
56917                             p.value = "&#160;";
56918                         }
56919                         if(c.has_editor){
56920                             p.css += ' x-grid-editable-cell';
56921                         }
56922                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56923                             p.css +=  ' x-grid-dirty-cell';
56924                         }
56925                         var markup = ct.apply(p);
56926                         if(!c.locked){
56927                             cb+= markup;
56928                         }else{
56929                             lcb+= markup;
56930                         }
56931                     }
56932                     var alt = [];
56933                     if(stripe && ((rowIndex+1) % 2 == 0)){
56934                         alt.push("x-grid-row-alt")
56935                     }
56936                     if(r.dirty){
56937                         alt.push(  " x-grid-dirty-row");
56938                     }
56939                     rp.cells = lcb;
56940                     if(this.getRowClass){
56941                         alt.push(this.getRowClass(r, rowIndex));
56942                     }
56943                     if (hasListener) {
56944                         rowcfg = {
56945                              
56946                             record: r,
56947                             rowIndex : rowIndex,
56948                             rowClass : ''
56949                         };
56950                         this.grid.fireEvent('rowclass', this, rowcfg);
56951                         alt.push(rowcfg.rowClass);
56952                     }
56953                     rp.alt = alt.join(" ");
56954                     lbuf+= rt.apply(rp);
56955                     rp.cells = cb;
56956                     buf+=  rt.apply(rp);
56957                 }
56958                 return [lbuf, buf];
56959             } :
56960             function(cs, rs, ds, startRow, colCount, stripe){
56961                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56962                 // buffers
56963                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56964                 var hasListener = this.grid.hasListener('rowclass');
56965  
56966                 var rowcfg = {};
56967                 for(var j = 0, len = rs.length; j < len; j++){
56968                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56969                     for(var i = 0; i < colCount; i++){
56970                         c = cs[i];
56971                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56972                         p.id = c.id;
56973                         p.css = p.attr = "";
56974                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56975                         if(p.value == undefined || p.value === "") {
56976                             p.value = "&#160;";
56977                         }
56978                         //Roo.log(c);
56979                          if(c.has_editor){
56980                             p.css += ' x-grid-editable-cell';
56981                         }
56982                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56983                             p.css += ' x-grid-dirty-cell' 
56984                         }
56985                         
56986                         var markup = ct.apply(p);
56987                         if(!c.locked){
56988                             cb[cb.length] = markup;
56989                         }else{
56990                             lcb[lcb.length] = markup;
56991                         }
56992                     }
56993                     var alt = [];
56994                     if(stripe && ((rowIndex+1) % 2 == 0)){
56995                         alt.push( "x-grid-row-alt");
56996                     }
56997                     if(r.dirty){
56998                         alt.push(" x-grid-dirty-row");
56999                     }
57000                     rp.cells = lcb;
57001                     if(this.getRowClass){
57002                         alt.push( this.getRowClass(r, rowIndex));
57003                     }
57004                     if (hasListener) {
57005                         rowcfg = {
57006                              
57007                             record: r,
57008                             rowIndex : rowIndex,
57009                             rowClass : ''
57010                         };
57011                         this.grid.fireEvent('rowclass', this, rowcfg);
57012                         alt.push(rowcfg.rowClass);
57013                     }
57014                     
57015                     rp.alt = alt.join(" ");
57016                     rp.cells = lcb.join("");
57017                     lbuf[lbuf.length] = rt.apply(rp);
57018                     rp.cells = cb.join("");
57019                     buf[buf.length] =  rt.apply(rp);
57020                 }
57021                 return [lbuf.join(""), buf.join("")];
57022             },
57023
57024     renderBody : function(){
57025         var markup = this.renderRows();
57026         var bt = this.templates.body;
57027         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57028     },
57029
57030     /**
57031      * Refreshes the grid
57032      * @param {Boolean} headersToo
57033      */
57034     refresh : function(headersToo){
57035         this.fireEvent("beforerefresh", this);
57036         this.grid.stopEditing();
57037         var result = this.renderBody();
57038         this.lockedBody.update(result[0]);
57039         this.mainBody.update(result[1]);
57040         if(headersToo === true){
57041             this.updateHeaders();
57042             this.updateColumns();
57043             this.updateSplitters();
57044             this.updateHeaderSortState();
57045         }
57046         this.syncRowHeights();
57047         this.layout();
57048         this.fireEvent("refresh", this);
57049     },
57050
57051     handleColumnMove : function(cm, oldIndex, newIndex){
57052         this.indexMap = null;
57053         var s = this.getScrollState();
57054         this.refresh(true);
57055         this.restoreScroll(s);
57056         this.afterMove(newIndex);
57057     },
57058
57059     afterMove : function(colIndex){
57060         if(this.enableMoveAnim && Roo.enableFx){
57061             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57062         }
57063         // if multisort - fix sortOrder, and reload..
57064         if (this.grid.dataSource.multiSort) {
57065             // the we can call sort again..
57066             var dm = this.grid.dataSource;
57067             var cm = this.grid.colModel;
57068             var so = [];
57069             for(var i = 0; i < cm.config.length; i++ ) {
57070                 
57071                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57072                     continue; // dont' bother, it's not in sort list or being set.
57073                 }
57074                 
57075                 so.push(cm.config[i].dataIndex);
57076             };
57077             dm.sortOrder = so;
57078             dm.load(dm.lastOptions);
57079             
57080             
57081         }
57082         
57083     },
57084
57085     updateCell : function(dm, rowIndex, dataIndex){
57086         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57087         if(typeof colIndex == "undefined"){ // not present in grid
57088             return;
57089         }
57090         var cm = this.grid.colModel;
57091         var cell = this.getCell(rowIndex, colIndex);
57092         var cellText = this.getCellText(rowIndex, colIndex);
57093
57094         var p = {
57095             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57096             id : cm.getColumnId(colIndex),
57097             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57098         };
57099         var renderer = cm.getRenderer(colIndex);
57100         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57101         if(typeof val == "undefined" || val === "") {
57102             val = "&#160;";
57103         }
57104         cellText.innerHTML = val;
57105         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57106         this.syncRowHeights(rowIndex, rowIndex);
57107     },
57108
57109     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57110         var maxWidth = 0;
57111         if(this.grid.autoSizeHeaders){
57112             var h = this.getHeaderCellMeasure(colIndex);
57113             maxWidth = Math.max(maxWidth, h.scrollWidth);
57114         }
57115         var tb, index;
57116         if(this.cm.isLocked(colIndex)){
57117             tb = this.getLockedTable();
57118             index = colIndex;
57119         }else{
57120             tb = this.getBodyTable();
57121             index = colIndex - this.cm.getLockedCount();
57122         }
57123         if(tb && tb.rows){
57124             var rows = tb.rows;
57125             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57126             for(var i = 0; i < stopIndex; i++){
57127                 var cell = rows[i].childNodes[index].firstChild;
57128                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57129             }
57130         }
57131         return maxWidth + /*margin for error in IE*/ 5;
57132     },
57133     /**
57134      * Autofit a column to its content.
57135      * @param {Number} colIndex
57136      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57137      */
57138      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57139          if(this.cm.isHidden(colIndex)){
57140              return; // can't calc a hidden column
57141          }
57142         if(forceMinSize){
57143             var cid = this.cm.getColumnId(colIndex);
57144             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57145            if(this.grid.autoSizeHeaders){
57146                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57147            }
57148         }
57149         var newWidth = this.calcColumnWidth(colIndex);
57150         this.cm.setColumnWidth(colIndex,
57151             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57152         if(!suppressEvent){
57153             this.grid.fireEvent("columnresize", colIndex, newWidth);
57154         }
57155     },
57156
57157     /**
57158      * Autofits all columns to their content and then expands to fit any extra space in the grid
57159      */
57160      autoSizeColumns : function(){
57161         var cm = this.grid.colModel;
57162         var colCount = cm.getColumnCount();
57163         for(var i = 0; i < colCount; i++){
57164             this.autoSizeColumn(i, true, true);
57165         }
57166         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57167             this.fitColumns();
57168         }else{
57169             this.updateColumns();
57170             this.layout();
57171         }
57172     },
57173
57174     /**
57175      * Autofits all columns to the grid's width proportionate with their current size
57176      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57177      */
57178     fitColumns : function(reserveScrollSpace){
57179         var cm = this.grid.colModel;
57180         var colCount = cm.getColumnCount();
57181         var cols = [];
57182         var width = 0;
57183         var i, w;
57184         for (i = 0; i < colCount; i++){
57185             if(!cm.isHidden(i) && !cm.isFixed(i)){
57186                 w = cm.getColumnWidth(i);
57187                 cols.push(i);
57188                 cols.push(w);
57189                 width += w;
57190             }
57191         }
57192         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57193         if(reserveScrollSpace){
57194             avail -= 17;
57195         }
57196         var frac = (avail - cm.getTotalWidth())/width;
57197         while (cols.length){
57198             w = cols.pop();
57199             i = cols.pop();
57200             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57201         }
57202         this.updateColumns();
57203         this.layout();
57204     },
57205
57206     onRowSelect : function(rowIndex){
57207         var row = this.getRowComposite(rowIndex);
57208         row.addClass("x-grid-row-selected");
57209     },
57210
57211     onRowDeselect : function(rowIndex){
57212         var row = this.getRowComposite(rowIndex);
57213         row.removeClass("x-grid-row-selected");
57214     },
57215
57216     onCellSelect : function(row, col){
57217         var cell = this.getCell(row, col);
57218         if(cell){
57219             Roo.fly(cell).addClass("x-grid-cell-selected");
57220         }
57221     },
57222
57223     onCellDeselect : function(row, col){
57224         var cell = this.getCell(row, col);
57225         if(cell){
57226             Roo.fly(cell).removeClass("x-grid-cell-selected");
57227         }
57228     },
57229
57230     updateHeaderSortState : function(){
57231         
57232         // sort state can be single { field: xxx, direction : yyy}
57233         // or   { xxx=>ASC , yyy : DESC ..... }
57234         
57235         var mstate = {};
57236         if (!this.ds.multiSort) { 
57237             var state = this.ds.getSortState();
57238             if(!state){
57239                 return;
57240             }
57241             mstate[state.field] = state.direction;
57242             // FIXME... - this is not used here.. but might be elsewhere..
57243             this.sortState = state;
57244             
57245         } else {
57246             mstate = this.ds.sortToggle;
57247         }
57248         //remove existing sort classes..
57249         
57250         var sc = this.sortClasses;
57251         var hds = this.el.select(this.headerSelector).removeClass(sc);
57252         
57253         for(var f in mstate) {
57254         
57255             var sortColumn = this.cm.findColumnIndex(f);
57256             
57257             if(sortColumn != -1){
57258                 var sortDir = mstate[f];        
57259                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57260             }
57261         }
57262         
57263          
57264         
57265     },
57266
57267
57268     handleHeaderClick : function(g, index,e){
57269         
57270         Roo.log("header click");
57271         
57272         if (Roo.isTouch) {
57273             // touch events on header are handled by context
57274             this.handleHdCtx(g,index,e);
57275             return;
57276         }
57277         
57278         
57279         if(this.headersDisabled){
57280             return;
57281         }
57282         var dm = g.dataSource, cm = g.colModel;
57283         if(!cm.isSortable(index)){
57284             return;
57285         }
57286         g.stopEditing();
57287         
57288         if (dm.multiSort) {
57289             // update the sortOrder
57290             var so = [];
57291             for(var i = 0; i < cm.config.length; i++ ) {
57292                 
57293                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57294                     continue; // dont' bother, it's not in sort list or being set.
57295                 }
57296                 
57297                 so.push(cm.config[i].dataIndex);
57298             };
57299             dm.sortOrder = so;
57300         }
57301         
57302         
57303         dm.sort(cm.getDataIndex(index));
57304     },
57305
57306
57307     destroy : function(){
57308         if(this.colMenu){
57309             this.colMenu.removeAll();
57310             Roo.menu.MenuMgr.unregister(this.colMenu);
57311             this.colMenu.getEl().remove();
57312             delete this.colMenu;
57313         }
57314         if(this.hmenu){
57315             this.hmenu.removeAll();
57316             Roo.menu.MenuMgr.unregister(this.hmenu);
57317             this.hmenu.getEl().remove();
57318             delete this.hmenu;
57319         }
57320         if(this.grid.enableColumnMove){
57321             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57322             if(dds){
57323                 for(var dd in dds){
57324                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57325                         var elid = dds[dd].dragElId;
57326                         dds[dd].unreg();
57327                         Roo.get(elid).remove();
57328                     } else if(dds[dd].config.isTarget){
57329                         dds[dd].proxyTop.remove();
57330                         dds[dd].proxyBottom.remove();
57331                         dds[dd].unreg();
57332                     }
57333                     if(Roo.dd.DDM.locationCache[dd]){
57334                         delete Roo.dd.DDM.locationCache[dd];
57335                     }
57336                 }
57337                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57338             }
57339         }
57340         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57341         this.bind(null, null);
57342         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57343     },
57344
57345     handleLockChange : function(){
57346         this.refresh(true);
57347     },
57348
57349     onDenyColumnLock : function(){
57350
57351     },
57352
57353     onDenyColumnHide : function(){
57354
57355     },
57356
57357     handleHdMenuClick : function(item){
57358         var index = this.hdCtxIndex;
57359         var cm = this.cm, ds = this.ds;
57360         switch(item.id){
57361             case "asc":
57362                 ds.sort(cm.getDataIndex(index), "ASC");
57363                 break;
57364             case "desc":
57365                 ds.sort(cm.getDataIndex(index), "DESC");
57366                 break;
57367             case "lock":
57368                 var lc = cm.getLockedCount();
57369                 if(cm.getColumnCount(true) <= lc+1){
57370                     this.onDenyColumnLock();
57371                     return;
57372                 }
57373                 if(lc != index){
57374                     cm.setLocked(index, true, true);
57375                     cm.moveColumn(index, lc);
57376                     this.grid.fireEvent("columnmove", index, lc);
57377                 }else{
57378                     cm.setLocked(index, true);
57379                 }
57380             break;
57381             case "unlock":
57382                 var lc = cm.getLockedCount();
57383                 if((lc-1) != index){
57384                     cm.setLocked(index, false, true);
57385                     cm.moveColumn(index, lc-1);
57386                     this.grid.fireEvent("columnmove", index, lc-1);
57387                 }else{
57388                     cm.setLocked(index, false);
57389                 }
57390             break;
57391             case 'wider': // used to expand cols on touch..
57392             case 'narrow':
57393                 var cw = cm.getColumnWidth(index);
57394                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57395                 cw = Math.max(0, cw);
57396                 cw = Math.min(cw,4000);
57397                 cm.setColumnWidth(index, cw);
57398                 break;
57399                 
57400             default:
57401                 index = cm.getIndexById(item.id.substr(4));
57402                 if(index != -1){
57403                     if(item.checked && cm.getColumnCount(true) <= 1){
57404                         this.onDenyColumnHide();
57405                         return false;
57406                     }
57407                     cm.setHidden(index, item.checked);
57408                 }
57409         }
57410         return true;
57411     },
57412
57413     beforeColMenuShow : function(){
57414         var cm = this.cm,  colCount = cm.getColumnCount();
57415         this.colMenu.removeAll();
57416         for(var i = 0; i < colCount; i++){
57417             this.colMenu.add(new Roo.menu.CheckItem({
57418                 id: "col-"+cm.getColumnId(i),
57419                 text: cm.getColumnHeader(i),
57420                 checked: !cm.isHidden(i),
57421                 hideOnClick:false
57422             }));
57423         }
57424     },
57425
57426     handleHdCtx : function(g, index, e){
57427         e.stopEvent();
57428         var hd = this.getHeaderCell(index);
57429         this.hdCtxIndex = index;
57430         var ms = this.hmenu.items, cm = this.cm;
57431         ms.get("asc").setDisabled(!cm.isSortable(index));
57432         ms.get("desc").setDisabled(!cm.isSortable(index));
57433         if(this.grid.enableColLock !== false){
57434             ms.get("lock").setDisabled(cm.isLocked(index));
57435             ms.get("unlock").setDisabled(!cm.isLocked(index));
57436         }
57437         this.hmenu.show(hd, "tl-bl");
57438     },
57439
57440     handleHdOver : function(e){
57441         var hd = this.findHeaderCell(e.getTarget());
57442         if(hd && !this.headersDisabled){
57443             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57444                this.fly(hd).addClass("x-grid-hd-over");
57445             }
57446         }
57447     },
57448
57449     handleHdOut : function(e){
57450         var hd = this.findHeaderCell(e.getTarget());
57451         if(hd){
57452             this.fly(hd).removeClass("x-grid-hd-over");
57453         }
57454     },
57455
57456     handleSplitDblClick : function(e, t){
57457         var i = this.getCellIndex(t);
57458         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57459             this.autoSizeColumn(i, true);
57460             this.layout();
57461         }
57462     },
57463
57464     render : function(){
57465
57466         var cm = this.cm;
57467         var colCount = cm.getColumnCount();
57468
57469         if(this.grid.monitorWindowResize === true){
57470             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57471         }
57472         var header = this.renderHeaders();
57473         var body = this.templates.body.apply({rows:""});
57474         var html = this.templates.master.apply({
57475             lockedBody: body,
57476             body: body,
57477             lockedHeader: header[0],
57478             header: header[1]
57479         });
57480
57481         //this.updateColumns();
57482
57483         this.grid.getGridEl().dom.innerHTML = html;
57484
57485         this.initElements();
57486         
57487         // a kludge to fix the random scolling effect in webkit
57488         this.el.on("scroll", function() {
57489             this.el.dom.scrollTop=0; // hopefully not recursive..
57490         },this);
57491
57492         this.scroller.on("scroll", this.handleScroll, this);
57493         this.lockedBody.on("mousewheel", this.handleWheel, this);
57494         this.mainBody.on("mousewheel", this.handleWheel, this);
57495
57496         this.mainHd.on("mouseover", this.handleHdOver, this);
57497         this.mainHd.on("mouseout", this.handleHdOut, this);
57498         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57499                 {delegate: "."+this.splitClass});
57500
57501         this.lockedHd.on("mouseover", this.handleHdOver, this);
57502         this.lockedHd.on("mouseout", this.handleHdOut, this);
57503         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57504                 {delegate: "."+this.splitClass});
57505
57506         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57507             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57508         }
57509
57510         this.updateSplitters();
57511
57512         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57513             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57514             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57515         }
57516
57517         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57518             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57519             this.hmenu.add(
57520                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57521                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57522             );
57523             if(this.grid.enableColLock !== false){
57524                 this.hmenu.add('-',
57525                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57526                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57527                 );
57528             }
57529             if (Roo.isTouch) {
57530                  this.hmenu.add('-',
57531                     {id:"wider", text: this.columnsWiderText},
57532                     {id:"narrow", text: this.columnsNarrowText }
57533                 );
57534                 
57535                  
57536             }
57537             
57538             if(this.grid.enableColumnHide !== false){
57539
57540                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57541                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57542                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57543
57544                 this.hmenu.add('-',
57545                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57546                 );
57547             }
57548             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57549
57550             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57551         }
57552
57553         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57554             this.dd = new Roo.grid.GridDragZone(this.grid, {
57555                 ddGroup : this.grid.ddGroup || 'GridDD'
57556             });
57557             
57558         }
57559
57560         /*
57561         for(var i = 0; i < colCount; i++){
57562             if(cm.isHidden(i)){
57563                 this.hideColumn(i);
57564             }
57565             if(cm.config[i].align){
57566                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57567                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57568             }
57569         }*/
57570         
57571         this.updateHeaderSortState();
57572
57573         this.beforeInitialResize();
57574         this.layout(true);
57575
57576         // two part rendering gives faster view to the user
57577         this.renderPhase2.defer(1, this);
57578     },
57579
57580     renderPhase2 : function(){
57581         // render the rows now
57582         this.refresh();
57583         if(this.grid.autoSizeColumns){
57584             this.autoSizeColumns();
57585         }
57586     },
57587
57588     beforeInitialResize : function(){
57589
57590     },
57591
57592     onColumnSplitterMoved : function(i, w){
57593         this.userResized = true;
57594         var cm = this.grid.colModel;
57595         cm.setColumnWidth(i, w, true);
57596         var cid = cm.getColumnId(i);
57597         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57598         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57599         this.updateSplitters();
57600         this.layout();
57601         this.grid.fireEvent("columnresize", i, w);
57602     },
57603
57604     syncRowHeights : function(startIndex, endIndex){
57605         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57606             startIndex = startIndex || 0;
57607             var mrows = this.getBodyTable().rows;
57608             var lrows = this.getLockedTable().rows;
57609             var len = mrows.length-1;
57610             endIndex = Math.min(endIndex || len, len);
57611             for(var i = startIndex; i <= endIndex; i++){
57612                 var m = mrows[i], l = lrows[i];
57613                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57614                 m.style.height = l.style.height = h + "px";
57615             }
57616         }
57617     },
57618
57619     layout : function(initialRender, is2ndPass){
57620         var g = this.grid;
57621         var auto = g.autoHeight;
57622         var scrollOffset = 16;
57623         var c = g.getGridEl(), cm = this.cm,
57624                 expandCol = g.autoExpandColumn,
57625                 gv = this;
57626         //c.beginMeasure();
57627
57628         if(!c.dom.offsetWidth){ // display:none?
57629             if(initialRender){
57630                 this.lockedWrap.show();
57631                 this.mainWrap.show();
57632             }
57633             return;
57634         }
57635
57636         var hasLock = this.cm.isLocked(0);
57637
57638         var tbh = this.headerPanel.getHeight();
57639         var bbh = this.footerPanel.getHeight();
57640
57641         if(auto){
57642             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57643             var newHeight = ch + c.getBorderWidth("tb");
57644             if(g.maxHeight){
57645                 newHeight = Math.min(g.maxHeight, newHeight);
57646             }
57647             c.setHeight(newHeight);
57648         }
57649
57650         if(g.autoWidth){
57651             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57652         }
57653
57654         var s = this.scroller;
57655
57656         var csize = c.getSize(true);
57657
57658         this.el.setSize(csize.width, csize.height);
57659
57660         this.headerPanel.setWidth(csize.width);
57661         this.footerPanel.setWidth(csize.width);
57662
57663         var hdHeight = this.mainHd.getHeight();
57664         var vw = csize.width;
57665         var vh = csize.height - (tbh + bbh);
57666
57667         s.setSize(vw, vh);
57668
57669         var bt = this.getBodyTable();
57670         
57671         if(cm.getLockedCount() == cm.config.length){
57672             bt = this.getLockedTable();
57673         }
57674         
57675         var ltWidth = hasLock ?
57676                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57677
57678         var scrollHeight = bt.offsetHeight;
57679         var scrollWidth = ltWidth + bt.offsetWidth;
57680         var vscroll = false, hscroll = false;
57681
57682         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57683
57684         var lw = this.lockedWrap, mw = this.mainWrap;
57685         var lb = this.lockedBody, mb = this.mainBody;
57686
57687         setTimeout(function(){
57688             var t = s.dom.offsetTop;
57689             var w = s.dom.clientWidth,
57690                 h = s.dom.clientHeight;
57691
57692             lw.setTop(t);
57693             lw.setSize(ltWidth, h);
57694
57695             mw.setLeftTop(ltWidth, t);
57696             mw.setSize(w-ltWidth, h);
57697
57698             lb.setHeight(h-hdHeight);
57699             mb.setHeight(h-hdHeight);
57700
57701             if(is2ndPass !== true && !gv.userResized && expandCol){
57702                 // high speed resize without full column calculation
57703                 
57704                 var ci = cm.getIndexById(expandCol);
57705                 if (ci < 0) {
57706                     ci = cm.findColumnIndex(expandCol);
57707                 }
57708                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57709                 var expandId = cm.getColumnId(ci);
57710                 var  tw = cm.getTotalWidth(false);
57711                 var currentWidth = cm.getColumnWidth(ci);
57712                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57713                 if(currentWidth != cw){
57714                     cm.setColumnWidth(ci, cw, true);
57715                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57716                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57717                     gv.updateSplitters();
57718                     gv.layout(false, true);
57719                 }
57720             }
57721
57722             if(initialRender){
57723                 lw.show();
57724                 mw.show();
57725             }
57726             //c.endMeasure();
57727         }, 10);
57728     },
57729
57730     onWindowResize : function(){
57731         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57732             return;
57733         }
57734         this.layout();
57735     },
57736
57737     appendFooter : function(parentEl){
57738         return null;
57739     },
57740
57741     sortAscText : "Sort Ascending",
57742     sortDescText : "Sort Descending",
57743     lockText : "Lock Column",
57744     unlockText : "Unlock Column",
57745     columnsText : "Columns",
57746  
57747     columnsWiderText : "Wider",
57748     columnsNarrowText : "Thinner"
57749 });
57750
57751
57752 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57753     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57754     this.proxy.el.addClass('x-grid3-col-dd');
57755 };
57756
57757 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57758     handleMouseDown : function(e){
57759
57760     },
57761
57762     callHandleMouseDown : function(e){
57763         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57764     }
57765 });
57766 /*
57767  * Based on:
57768  * Ext JS Library 1.1.1
57769  * Copyright(c) 2006-2007, Ext JS, LLC.
57770  *
57771  * Originally Released Under LGPL - original licence link has changed is not relivant.
57772  *
57773  * Fork - LGPL
57774  * <script type="text/javascript">
57775  */
57776  
57777 // private
57778 // This is a support class used internally by the Grid components
57779 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57780     this.grid = grid;
57781     this.view = grid.getView();
57782     this.proxy = this.view.resizeProxy;
57783     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57784         "gridSplitters" + this.grid.getGridEl().id, {
57785         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57786     });
57787     this.setHandleElId(Roo.id(hd));
57788     this.setOuterHandleElId(Roo.id(hd2));
57789     this.scroll = false;
57790 };
57791 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57792     fly: Roo.Element.fly,
57793
57794     b4StartDrag : function(x, y){
57795         this.view.headersDisabled = true;
57796         this.proxy.setHeight(this.view.mainWrap.getHeight());
57797         var w = this.cm.getColumnWidth(this.cellIndex);
57798         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57799         this.resetConstraints();
57800         this.setXConstraint(minw, 1000);
57801         this.setYConstraint(0, 0);
57802         this.minX = x - minw;
57803         this.maxX = x + 1000;
57804         this.startPos = x;
57805         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57806     },
57807
57808
57809     handleMouseDown : function(e){
57810         ev = Roo.EventObject.setEvent(e);
57811         var t = this.fly(ev.getTarget());
57812         if(t.hasClass("x-grid-split")){
57813             this.cellIndex = this.view.getCellIndex(t.dom);
57814             this.split = t.dom;
57815             this.cm = this.grid.colModel;
57816             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57817                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57818             }
57819         }
57820     },
57821
57822     endDrag : function(e){
57823         this.view.headersDisabled = false;
57824         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57825         var diff = endX - this.startPos;
57826         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57827     },
57828
57829     autoOffset : function(){
57830         this.setDelta(0,0);
57831     }
57832 });/*
57833  * Based on:
57834  * Ext JS Library 1.1.1
57835  * Copyright(c) 2006-2007, Ext JS, LLC.
57836  *
57837  * Originally Released Under LGPL - original licence link has changed is not relivant.
57838  *
57839  * Fork - LGPL
57840  * <script type="text/javascript">
57841  */
57842  
57843 // private
57844 // This is a support class used internally by the Grid components
57845 Roo.grid.GridDragZone = function(grid, config){
57846     this.view = grid.getView();
57847     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57848     if(this.view.lockedBody){
57849         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57850         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57851     }
57852     this.scroll = false;
57853     this.grid = grid;
57854     this.ddel = document.createElement('div');
57855     this.ddel.className = 'x-grid-dd-wrap';
57856 };
57857
57858 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57859     ddGroup : "GridDD",
57860
57861     getDragData : function(e){
57862         var t = Roo.lib.Event.getTarget(e);
57863         var rowIndex = this.view.findRowIndex(t);
57864         var sm = this.grid.selModel;
57865             
57866         //Roo.log(rowIndex);
57867         
57868         if (sm.getSelectedCell) {
57869             // cell selection..
57870             if (!sm.getSelectedCell()) {
57871                 return false;
57872             }
57873             if (rowIndex != sm.getSelectedCell()[0]) {
57874                 return false;
57875             }
57876         
57877         }
57878         
57879         if(rowIndex !== false){
57880             
57881             // if editorgrid.. 
57882             
57883             
57884             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57885                
57886             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57887               //  
57888             //}
57889             if (e.hasModifier()){
57890                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57891             }
57892             
57893             Roo.log("getDragData");
57894             
57895             return {
57896                 grid: this.grid,
57897                 ddel: this.ddel,
57898                 rowIndex: rowIndex,
57899                 selections:sm.getSelections ? sm.getSelections() : (
57900                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57901                 )
57902             };
57903         }
57904         return false;
57905     },
57906
57907     onInitDrag : function(e){
57908         var data = this.dragData;
57909         this.ddel.innerHTML = this.grid.getDragDropText();
57910         this.proxy.update(this.ddel);
57911         // fire start drag?
57912     },
57913
57914     afterRepair : function(){
57915         this.dragging = false;
57916     },
57917
57918     getRepairXY : function(e, data){
57919         return false;
57920     },
57921
57922     onEndDrag : function(data, e){
57923         // fire end drag?
57924     },
57925
57926     onValidDrop : function(dd, e, id){
57927         // fire drag drop?
57928         this.hideProxy();
57929     },
57930
57931     beforeInvalidDrop : function(e, id){
57932
57933     }
57934 });/*
57935  * Based on:
57936  * Ext JS Library 1.1.1
57937  * Copyright(c) 2006-2007, Ext JS, LLC.
57938  *
57939  * Originally Released Under LGPL - original licence link has changed is not relivant.
57940  *
57941  * Fork - LGPL
57942  * <script type="text/javascript">
57943  */
57944  
57945
57946 /**
57947  * @class Roo.grid.ColumnModel
57948  * @extends Roo.util.Observable
57949  * This is the default implementation of a ColumnModel used by the Grid. It defines
57950  * the columns in the grid.
57951  * <br>Usage:<br>
57952  <pre><code>
57953  var colModel = new Roo.grid.ColumnModel([
57954         {header: "Ticker", width: 60, sortable: true, locked: true},
57955         {header: "Company Name", width: 150, sortable: true},
57956         {header: "Market Cap.", width: 100, sortable: true},
57957         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57958         {header: "Employees", width: 100, sortable: true, resizable: false}
57959  ]);
57960  </code></pre>
57961  * <p>
57962  
57963  * The config options listed for this class are options which may appear in each
57964  * individual column definition.
57965  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57966  * @constructor
57967  * @param {Object} config An Array of column config objects. See this class's
57968  * config objects for details.
57969 */
57970 Roo.grid.ColumnModel = function(config){
57971         /**
57972      * The config passed into the constructor
57973      */
57974     this.config = config;
57975     this.lookup = {};
57976
57977     // if no id, create one
57978     // if the column does not have a dataIndex mapping,
57979     // map it to the order it is in the config
57980     for(var i = 0, len = config.length; i < len; i++){
57981         var c = config[i];
57982         if(typeof c.dataIndex == "undefined"){
57983             c.dataIndex = i;
57984         }
57985         if(typeof c.renderer == "string"){
57986             c.renderer = Roo.util.Format[c.renderer];
57987         }
57988         if(typeof c.id == "undefined"){
57989             c.id = Roo.id();
57990         }
57991         if(c.editor && c.editor.xtype){
57992             c.editor  = Roo.factory(c.editor, Roo.grid);
57993         }
57994         if(c.editor && c.editor.isFormField){
57995             c.editor = new Roo.grid.GridEditor(c.editor);
57996         }
57997         this.lookup[c.id] = c;
57998     }
57999
58000     /**
58001      * The width of columns which have no width specified (defaults to 100)
58002      * @type Number
58003      */
58004     this.defaultWidth = 100;
58005
58006     /**
58007      * Default sortable of columns which have no sortable specified (defaults to false)
58008      * @type Boolean
58009      */
58010     this.defaultSortable = false;
58011
58012     this.addEvents({
58013         /**
58014              * @event widthchange
58015              * Fires when the width of a column changes.
58016              * @param {ColumnModel} this
58017              * @param {Number} columnIndex The column index
58018              * @param {Number} newWidth The new width
58019              */
58020             "widthchange": true,
58021         /**
58022              * @event headerchange
58023              * Fires when the text of a header changes.
58024              * @param {ColumnModel} this
58025              * @param {Number} columnIndex The column index
58026              * @param {Number} newText The new header text
58027              */
58028             "headerchange": true,
58029         /**
58030              * @event hiddenchange
58031              * Fires when a column is hidden or "unhidden".
58032              * @param {ColumnModel} this
58033              * @param {Number} columnIndex The column index
58034              * @param {Boolean} hidden true if hidden, false otherwise
58035              */
58036             "hiddenchange": true,
58037             /**
58038          * @event columnmoved
58039          * Fires when a column is moved.
58040          * @param {ColumnModel} this
58041          * @param {Number} oldIndex
58042          * @param {Number} newIndex
58043          */
58044         "columnmoved" : true,
58045         /**
58046          * @event columlockchange
58047          * Fires when a column's locked state is changed
58048          * @param {ColumnModel} this
58049          * @param {Number} colIndex
58050          * @param {Boolean} locked true if locked
58051          */
58052         "columnlockchange" : true
58053     });
58054     Roo.grid.ColumnModel.superclass.constructor.call(this);
58055 };
58056 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58057     /**
58058      * @cfg {String} header The header text to display in the Grid view.
58059      */
58060     /**
58061      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58062      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58063      * specified, the column's index is used as an index into the Record's data Array.
58064      */
58065     /**
58066      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58067      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58068      */
58069     /**
58070      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58071      * Defaults to the value of the {@link #defaultSortable} property.
58072      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58073      */
58074     /**
58075      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58076      */
58077     /**
58078      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58079      */
58080     /**
58081      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58082      */
58083     /**
58084      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58085      */
58086     /**
58087      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58088      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58089      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58090      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58091      */
58092        /**
58093      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58094      */
58095     /**
58096      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58097      */
58098     /**
58099      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58100      */
58101     /**
58102      * @cfg {String} cursor (Optional)
58103      */
58104     /**
58105      * @cfg {String} tooltip (Optional)
58106      */
58107     /**
58108      * @cfg {Number} xs (Optional)
58109      */
58110     /**
58111      * @cfg {Number} sm (Optional)
58112      */
58113     /**
58114      * @cfg {Number} md (Optional)
58115      */
58116     /**
58117      * @cfg {Number} lg (Optional)
58118      */
58119     /**
58120      * Returns the id of the column at the specified index.
58121      * @param {Number} index The column index
58122      * @return {String} the id
58123      */
58124     getColumnId : function(index){
58125         return this.config[index].id;
58126     },
58127
58128     /**
58129      * Returns the column for a specified id.
58130      * @param {String} id The column id
58131      * @return {Object} the column
58132      */
58133     getColumnById : function(id){
58134         return this.lookup[id];
58135     },
58136
58137     
58138     /**
58139      * Returns the column for a specified dataIndex.
58140      * @param {String} dataIndex The column dataIndex
58141      * @return {Object|Boolean} the column or false if not found
58142      */
58143     getColumnByDataIndex: function(dataIndex){
58144         var index = this.findColumnIndex(dataIndex);
58145         return index > -1 ? this.config[index] : false;
58146     },
58147     
58148     /**
58149      * Returns the index for a specified column id.
58150      * @param {String} id The column id
58151      * @return {Number} the index, or -1 if not found
58152      */
58153     getIndexById : function(id){
58154         for(var i = 0, len = this.config.length; i < len; i++){
58155             if(this.config[i].id == id){
58156                 return i;
58157             }
58158         }
58159         return -1;
58160     },
58161     
58162     /**
58163      * Returns the index for a specified column dataIndex.
58164      * @param {String} dataIndex The column dataIndex
58165      * @return {Number} the index, or -1 if not found
58166      */
58167     
58168     findColumnIndex : function(dataIndex){
58169         for(var i = 0, len = this.config.length; i < len; i++){
58170             if(this.config[i].dataIndex == dataIndex){
58171                 return i;
58172             }
58173         }
58174         return -1;
58175     },
58176     
58177     
58178     moveColumn : function(oldIndex, newIndex){
58179         var c = this.config[oldIndex];
58180         this.config.splice(oldIndex, 1);
58181         this.config.splice(newIndex, 0, c);
58182         this.dataMap = null;
58183         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58184     },
58185
58186     isLocked : function(colIndex){
58187         return this.config[colIndex].locked === true;
58188     },
58189
58190     setLocked : function(colIndex, value, suppressEvent){
58191         if(this.isLocked(colIndex) == value){
58192             return;
58193         }
58194         this.config[colIndex].locked = value;
58195         if(!suppressEvent){
58196             this.fireEvent("columnlockchange", this, colIndex, value);
58197         }
58198     },
58199
58200     getTotalLockedWidth : function(){
58201         var totalWidth = 0;
58202         for(var i = 0; i < this.config.length; i++){
58203             if(this.isLocked(i) && !this.isHidden(i)){
58204                 this.totalWidth += this.getColumnWidth(i);
58205             }
58206         }
58207         return totalWidth;
58208     },
58209
58210     getLockedCount : function(){
58211         for(var i = 0, len = this.config.length; i < len; i++){
58212             if(!this.isLocked(i)){
58213                 return i;
58214             }
58215         }
58216         
58217         return this.config.length;
58218     },
58219
58220     /**
58221      * Returns the number of columns.
58222      * @return {Number}
58223      */
58224     getColumnCount : function(visibleOnly){
58225         if(visibleOnly === true){
58226             var c = 0;
58227             for(var i = 0, len = this.config.length; i < len; i++){
58228                 if(!this.isHidden(i)){
58229                     c++;
58230                 }
58231             }
58232             return c;
58233         }
58234         return this.config.length;
58235     },
58236
58237     /**
58238      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58239      * @param {Function} fn
58240      * @param {Object} scope (optional)
58241      * @return {Array} result
58242      */
58243     getColumnsBy : function(fn, scope){
58244         var r = [];
58245         for(var i = 0, len = this.config.length; i < len; i++){
58246             var c = this.config[i];
58247             if(fn.call(scope||this, c, i) === true){
58248                 r[r.length] = c;
58249             }
58250         }
58251         return r;
58252     },
58253
58254     /**
58255      * Returns true if the specified column is sortable.
58256      * @param {Number} col The column index
58257      * @return {Boolean}
58258      */
58259     isSortable : function(col){
58260         if(typeof this.config[col].sortable == "undefined"){
58261             return this.defaultSortable;
58262         }
58263         return this.config[col].sortable;
58264     },
58265
58266     /**
58267      * Returns the rendering (formatting) function defined for the column.
58268      * @param {Number} col The column index.
58269      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58270      */
58271     getRenderer : function(col){
58272         if(!this.config[col].renderer){
58273             return Roo.grid.ColumnModel.defaultRenderer;
58274         }
58275         return this.config[col].renderer;
58276     },
58277
58278     /**
58279      * Sets the rendering (formatting) function for a column.
58280      * @param {Number} col The column index
58281      * @param {Function} fn The function to use to process the cell's raw data
58282      * to return HTML markup for the grid view. The render function is called with
58283      * the following parameters:<ul>
58284      * <li>Data value.</li>
58285      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58286      * <li>css A CSS style string to apply to the table cell.</li>
58287      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58288      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58289      * <li>Row index</li>
58290      * <li>Column index</li>
58291      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58292      */
58293     setRenderer : function(col, fn){
58294         this.config[col].renderer = fn;
58295     },
58296
58297     /**
58298      * Returns the width for the specified column.
58299      * @param {Number} col The column index
58300      * @return {Number}
58301      */
58302     getColumnWidth : function(col){
58303         return this.config[col].width * 1 || this.defaultWidth;
58304     },
58305
58306     /**
58307      * Sets the width for a column.
58308      * @param {Number} col The column index
58309      * @param {Number} width The new width
58310      */
58311     setColumnWidth : function(col, width, suppressEvent){
58312         this.config[col].width = width;
58313         this.totalWidth = null;
58314         if(!suppressEvent){
58315              this.fireEvent("widthchange", this, col, width);
58316         }
58317     },
58318
58319     /**
58320      * Returns the total width of all columns.
58321      * @param {Boolean} includeHidden True to include hidden column widths
58322      * @return {Number}
58323      */
58324     getTotalWidth : function(includeHidden){
58325         if(!this.totalWidth){
58326             this.totalWidth = 0;
58327             for(var i = 0, len = this.config.length; i < len; i++){
58328                 if(includeHidden || !this.isHidden(i)){
58329                     this.totalWidth += this.getColumnWidth(i);
58330                 }
58331             }
58332         }
58333         return this.totalWidth;
58334     },
58335
58336     /**
58337      * Returns the header for the specified column.
58338      * @param {Number} col The column index
58339      * @return {String}
58340      */
58341     getColumnHeader : function(col){
58342         return this.config[col].header;
58343     },
58344
58345     /**
58346      * Sets the header for a column.
58347      * @param {Number} col The column index
58348      * @param {String} header The new header
58349      */
58350     setColumnHeader : function(col, header){
58351         this.config[col].header = header;
58352         this.fireEvent("headerchange", this, col, header);
58353     },
58354
58355     /**
58356      * Returns the tooltip for the specified column.
58357      * @param {Number} col The column index
58358      * @return {String}
58359      */
58360     getColumnTooltip : function(col){
58361             return this.config[col].tooltip;
58362     },
58363     /**
58364      * Sets the tooltip for a column.
58365      * @param {Number} col The column index
58366      * @param {String} tooltip The new tooltip
58367      */
58368     setColumnTooltip : function(col, tooltip){
58369             this.config[col].tooltip = tooltip;
58370     },
58371
58372     /**
58373      * Returns the dataIndex for the specified column.
58374      * @param {Number} col The column index
58375      * @return {Number}
58376      */
58377     getDataIndex : function(col){
58378         return this.config[col].dataIndex;
58379     },
58380
58381     /**
58382      * Sets the dataIndex for a column.
58383      * @param {Number} col The column index
58384      * @param {Number} dataIndex The new dataIndex
58385      */
58386     setDataIndex : function(col, dataIndex){
58387         this.config[col].dataIndex = dataIndex;
58388     },
58389
58390     
58391     
58392     /**
58393      * Returns true if the cell is editable.
58394      * @param {Number} colIndex The column index
58395      * @param {Number} rowIndex The row index - this is nto actually used..?
58396      * @return {Boolean}
58397      */
58398     isCellEditable : function(colIndex, rowIndex){
58399         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58400     },
58401
58402     /**
58403      * Returns the editor defined for the cell/column.
58404      * return false or null to disable editing.
58405      * @param {Number} colIndex The column index
58406      * @param {Number} rowIndex The row index
58407      * @return {Object}
58408      */
58409     getCellEditor : function(colIndex, rowIndex){
58410         return this.config[colIndex].editor;
58411     },
58412
58413     /**
58414      * Sets if a column is editable.
58415      * @param {Number} col The column index
58416      * @param {Boolean} editable True if the column is editable
58417      */
58418     setEditable : function(col, editable){
58419         this.config[col].editable = editable;
58420     },
58421
58422
58423     /**
58424      * Returns true if the column is hidden.
58425      * @param {Number} colIndex The column index
58426      * @return {Boolean}
58427      */
58428     isHidden : function(colIndex){
58429         return this.config[colIndex].hidden;
58430     },
58431
58432
58433     /**
58434      * Returns true if the column width cannot be changed
58435      */
58436     isFixed : function(colIndex){
58437         return this.config[colIndex].fixed;
58438     },
58439
58440     /**
58441      * Returns true if the column can be resized
58442      * @return {Boolean}
58443      */
58444     isResizable : function(colIndex){
58445         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58446     },
58447     /**
58448      * Sets if a column is hidden.
58449      * @param {Number} colIndex The column index
58450      * @param {Boolean} hidden True if the column is hidden
58451      */
58452     setHidden : function(colIndex, hidden){
58453         this.config[colIndex].hidden = hidden;
58454         this.totalWidth = null;
58455         this.fireEvent("hiddenchange", this, colIndex, hidden);
58456     },
58457
58458     /**
58459      * Sets the editor for a column.
58460      * @param {Number} col The column index
58461      * @param {Object} editor The editor object
58462      */
58463     setEditor : function(col, editor){
58464         this.config[col].editor = editor;
58465     }
58466 });
58467
58468 Roo.grid.ColumnModel.defaultRenderer = function(value)
58469 {
58470     if(typeof value == "object") {
58471         return value;
58472     }
58473         if(typeof value == "string" && value.length < 1){
58474             return "&#160;";
58475         }
58476     
58477         return String.format("{0}", value);
58478 };
58479
58480 // Alias for backwards compatibility
58481 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58482 /*
58483  * Based on:
58484  * Ext JS Library 1.1.1
58485  * Copyright(c) 2006-2007, Ext JS, LLC.
58486  *
58487  * Originally Released Under LGPL - original licence link has changed is not relivant.
58488  *
58489  * Fork - LGPL
58490  * <script type="text/javascript">
58491  */
58492
58493 /**
58494  * @class Roo.grid.AbstractSelectionModel
58495  * @extends Roo.util.Observable
58496  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58497  * implemented by descendant classes.  This class should not be directly instantiated.
58498  * @constructor
58499  */
58500 Roo.grid.AbstractSelectionModel = function(){
58501     this.locked = false;
58502     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58503 };
58504
58505 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58506     /** @ignore Called by the grid automatically. Do not call directly. */
58507     init : function(grid){
58508         this.grid = grid;
58509         this.initEvents();
58510     },
58511
58512     /**
58513      * Locks the selections.
58514      */
58515     lock : function(){
58516         this.locked = true;
58517     },
58518
58519     /**
58520      * Unlocks the selections.
58521      */
58522     unlock : function(){
58523         this.locked = false;
58524     },
58525
58526     /**
58527      * Returns true if the selections are locked.
58528      * @return {Boolean}
58529      */
58530     isLocked : function(){
58531         return this.locked;
58532     }
58533 });/*
58534  * Based on:
58535  * Ext JS Library 1.1.1
58536  * Copyright(c) 2006-2007, Ext JS, LLC.
58537  *
58538  * Originally Released Under LGPL - original licence link has changed is not relivant.
58539  *
58540  * Fork - LGPL
58541  * <script type="text/javascript">
58542  */
58543 /**
58544  * @extends Roo.grid.AbstractSelectionModel
58545  * @class Roo.grid.RowSelectionModel
58546  * The default SelectionModel used by {@link Roo.grid.Grid}.
58547  * It supports multiple selections and keyboard selection/navigation. 
58548  * @constructor
58549  * @param {Object} config
58550  */
58551 Roo.grid.RowSelectionModel = function(config){
58552     Roo.apply(this, config);
58553     this.selections = new Roo.util.MixedCollection(false, function(o){
58554         return o.id;
58555     });
58556
58557     this.last = false;
58558     this.lastActive = false;
58559
58560     this.addEvents({
58561         /**
58562              * @event selectionchange
58563              * Fires when the selection changes
58564              * @param {SelectionModel} this
58565              */
58566             "selectionchange" : true,
58567         /**
58568              * @event afterselectionchange
58569              * Fires after the selection changes (eg. by key press or clicking)
58570              * @param {SelectionModel} this
58571              */
58572             "afterselectionchange" : true,
58573         /**
58574              * @event beforerowselect
58575              * Fires when a row is selected being selected, return false to cancel.
58576              * @param {SelectionModel} this
58577              * @param {Number} rowIndex The selected index
58578              * @param {Boolean} keepExisting False if other selections will be cleared
58579              */
58580             "beforerowselect" : true,
58581         /**
58582              * @event rowselect
58583              * Fires when a row is selected.
58584              * @param {SelectionModel} this
58585              * @param {Number} rowIndex The selected index
58586              * @param {Roo.data.Record} r The record
58587              */
58588             "rowselect" : true,
58589         /**
58590              * @event rowdeselect
58591              * Fires when a row is deselected.
58592              * @param {SelectionModel} this
58593              * @param {Number} rowIndex The selected index
58594              */
58595         "rowdeselect" : true
58596     });
58597     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58598     this.locked = false;
58599 };
58600
58601 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58602     /**
58603      * @cfg {Boolean} singleSelect
58604      * True to allow selection of only one row at a time (defaults to false)
58605      */
58606     singleSelect : false,
58607
58608     // private
58609     initEvents : function(){
58610
58611         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58612             this.grid.on("mousedown", this.handleMouseDown, this);
58613         }else{ // allow click to work like normal
58614             this.grid.on("rowclick", this.handleDragableRowClick, this);
58615         }
58616
58617         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58618             "up" : function(e){
58619                 if(!e.shiftKey){
58620                     this.selectPrevious(e.shiftKey);
58621                 }else if(this.last !== false && this.lastActive !== false){
58622                     var last = this.last;
58623                     this.selectRange(this.last,  this.lastActive-1);
58624                     this.grid.getView().focusRow(this.lastActive);
58625                     if(last !== false){
58626                         this.last = last;
58627                     }
58628                 }else{
58629                     this.selectFirstRow();
58630                 }
58631                 this.fireEvent("afterselectionchange", this);
58632             },
58633             "down" : function(e){
58634                 if(!e.shiftKey){
58635                     this.selectNext(e.shiftKey);
58636                 }else if(this.last !== false && this.lastActive !== false){
58637                     var last = this.last;
58638                     this.selectRange(this.last,  this.lastActive+1);
58639                     this.grid.getView().focusRow(this.lastActive);
58640                     if(last !== false){
58641                         this.last = last;
58642                     }
58643                 }else{
58644                     this.selectFirstRow();
58645                 }
58646                 this.fireEvent("afterselectionchange", this);
58647             },
58648             scope: this
58649         });
58650
58651         var view = this.grid.view;
58652         view.on("refresh", this.onRefresh, this);
58653         view.on("rowupdated", this.onRowUpdated, this);
58654         view.on("rowremoved", this.onRemove, this);
58655     },
58656
58657     // private
58658     onRefresh : function(){
58659         var ds = this.grid.dataSource, i, v = this.grid.view;
58660         var s = this.selections;
58661         s.each(function(r){
58662             if((i = ds.indexOfId(r.id)) != -1){
58663                 v.onRowSelect(i);
58664                 s.add(ds.getAt(i)); // updating the selection relate data
58665             }else{
58666                 s.remove(r);
58667             }
58668         });
58669     },
58670
58671     // private
58672     onRemove : function(v, index, r){
58673         this.selections.remove(r);
58674     },
58675
58676     // private
58677     onRowUpdated : function(v, index, r){
58678         if(this.isSelected(r)){
58679             v.onRowSelect(index);
58680         }
58681     },
58682
58683     /**
58684      * Select records.
58685      * @param {Array} records The records to select
58686      * @param {Boolean} keepExisting (optional) True to keep existing selections
58687      */
58688     selectRecords : function(records, keepExisting){
58689         if(!keepExisting){
58690             this.clearSelections();
58691         }
58692         var ds = this.grid.dataSource;
58693         for(var i = 0, len = records.length; i < len; i++){
58694             this.selectRow(ds.indexOf(records[i]), true);
58695         }
58696     },
58697
58698     /**
58699      * Gets the number of selected rows.
58700      * @return {Number}
58701      */
58702     getCount : function(){
58703         return this.selections.length;
58704     },
58705
58706     /**
58707      * Selects the first row in the grid.
58708      */
58709     selectFirstRow : function(){
58710         this.selectRow(0);
58711     },
58712
58713     /**
58714      * Select the last row.
58715      * @param {Boolean} keepExisting (optional) True to keep existing selections
58716      */
58717     selectLastRow : function(keepExisting){
58718         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58719     },
58720
58721     /**
58722      * Selects the row immediately following the last selected row.
58723      * @param {Boolean} keepExisting (optional) True to keep existing selections
58724      */
58725     selectNext : function(keepExisting){
58726         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58727             this.selectRow(this.last+1, keepExisting);
58728             this.grid.getView().focusRow(this.last);
58729         }
58730     },
58731
58732     /**
58733      * Selects the row that precedes the last selected row.
58734      * @param {Boolean} keepExisting (optional) True to keep existing selections
58735      */
58736     selectPrevious : function(keepExisting){
58737         if(this.last){
58738             this.selectRow(this.last-1, keepExisting);
58739             this.grid.getView().focusRow(this.last);
58740         }
58741     },
58742
58743     /**
58744      * Returns the selected records
58745      * @return {Array} Array of selected records
58746      */
58747     getSelections : function(){
58748         return [].concat(this.selections.items);
58749     },
58750
58751     /**
58752      * Returns the first selected record.
58753      * @return {Record}
58754      */
58755     getSelected : function(){
58756         return this.selections.itemAt(0);
58757     },
58758
58759
58760     /**
58761      * Clears all selections.
58762      */
58763     clearSelections : function(fast){
58764         if(this.locked) {
58765             return;
58766         }
58767         if(fast !== true){
58768             var ds = this.grid.dataSource;
58769             var s = this.selections;
58770             s.each(function(r){
58771                 this.deselectRow(ds.indexOfId(r.id));
58772             }, this);
58773             s.clear();
58774         }else{
58775             this.selections.clear();
58776         }
58777         this.last = false;
58778     },
58779
58780
58781     /**
58782      * Selects all rows.
58783      */
58784     selectAll : function(){
58785         if(this.locked) {
58786             return;
58787         }
58788         this.selections.clear();
58789         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58790             this.selectRow(i, true);
58791         }
58792     },
58793
58794     /**
58795      * Returns True if there is a selection.
58796      * @return {Boolean}
58797      */
58798     hasSelection : function(){
58799         return this.selections.length > 0;
58800     },
58801
58802     /**
58803      * Returns True if the specified row is selected.
58804      * @param {Number/Record} record The record or index of the record to check
58805      * @return {Boolean}
58806      */
58807     isSelected : function(index){
58808         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58809         return (r && this.selections.key(r.id) ? true : false);
58810     },
58811
58812     /**
58813      * Returns True if the specified record id is selected.
58814      * @param {String} id The id of record to check
58815      * @return {Boolean}
58816      */
58817     isIdSelected : function(id){
58818         return (this.selections.key(id) ? true : false);
58819     },
58820
58821     // private
58822     handleMouseDown : function(e, t){
58823         var view = this.grid.getView(), rowIndex;
58824         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58825             return;
58826         };
58827         if(e.shiftKey && this.last !== false){
58828             var last = this.last;
58829             this.selectRange(last, rowIndex, e.ctrlKey);
58830             this.last = last; // reset the last
58831             view.focusRow(rowIndex);
58832         }else{
58833             var isSelected = this.isSelected(rowIndex);
58834             if(e.button !== 0 && isSelected){
58835                 view.focusRow(rowIndex);
58836             }else if(e.ctrlKey && isSelected){
58837                 this.deselectRow(rowIndex);
58838             }else if(!isSelected){
58839                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58840                 view.focusRow(rowIndex);
58841             }
58842         }
58843         this.fireEvent("afterselectionchange", this);
58844     },
58845     // private
58846     handleDragableRowClick :  function(grid, rowIndex, e) 
58847     {
58848         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58849             this.selectRow(rowIndex, false);
58850             grid.view.focusRow(rowIndex);
58851              this.fireEvent("afterselectionchange", this);
58852         }
58853     },
58854     
58855     /**
58856      * Selects multiple rows.
58857      * @param {Array} rows Array of the indexes of the row to select
58858      * @param {Boolean} keepExisting (optional) True to keep existing selections
58859      */
58860     selectRows : function(rows, keepExisting){
58861         if(!keepExisting){
58862             this.clearSelections();
58863         }
58864         for(var i = 0, len = rows.length; i < len; i++){
58865             this.selectRow(rows[i], true);
58866         }
58867     },
58868
58869     /**
58870      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58871      * @param {Number} startRow The index of the first row in the range
58872      * @param {Number} endRow The index of the last row in the range
58873      * @param {Boolean} keepExisting (optional) True to retain existing selections
58874      */
58875     selectRange : function(startRow, endRow, keepExisting){
58876         if(this.locked) {
58877             return;
58878         }
58879         if(!keepExisting){
58880             this.clearSelections();
58881         }
58882         if(startRow <= endRow){
58883             for(var i = startRow; i <= endRow; i++){
58884                 this.selectRow(i, true);
58885             }
58886         }else{
58887             for(var i = startRow; i >= endRow; i--){
58888                 this.selectRow(i, true);
58889             }
58890         }
58891     },
58892
58893     /**
58894      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58895      * @param {Number} startRow The index of the first row in the range
58896      * @param {Number} endRow The index of the last row in the range
58897      */
58898     deselectRange : function(startRow, endRow, preventViewNotify){
58899         if(this.locked) {
58900             return;
58901         }
58902         for(var i = startRow; i <= endRow; i++){
58903             this.deselectRow(i, preventViewNotify);
58904         }
58905     },
58906
58907     /**
58908      * Selects a row.
58909      * @param {Number} row The index of the row to select
58910      * @param {Boolean} keepExisting (optional) True to keep existing selections
58911      */
58912     selectRow : function(index, keepExisting, preventViewNotify){
58913         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58914             return;
58915         }
58916         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58917             if(!keepExisting || this.singleSelect){
58918                 this.clearSelections();
58919             }
58920             var r = this.grid.dataSource.getAt(index);
58921             this.selections.add(r);
58922             this.last = this.lastActive = index;
58923             if(!preventViewNotify){
58924                 this.grid.getView().onRowSelect(index);
58925             }
58926             this.fireEvent("rowselect", this, index, r);
58927             this.fireEvent("selectionchange", this);
58928         }
58929     },
58930
58931     /**
58932      * Deselects a row.
58933      * @param {Number} row The index of the row to deselect
58934      */
58935     deselectRow : function(index, preventViewNotify){
58936         if(this.locked) {
58937             return;
58938         }
58939         if(this.last == index){
58940             this.last = false;
58941         }
58942         if(this.lastActive == index){
58943             this.lastActive = false;
58944         }
58945         var r = this.grid.dataSource.getAt(index);
58946         this.selections.remove(r);
58947         if(!preventViewNotify){
58948             this.grid.getView().onRowDeselect(index);
58949         }
58950         this.fireEvent("rowdeselect", this, index);
58951         this.fireEvent("selectionchange", this);
58952     },
58953
58954     // private
58955     restoreLast : function(){
58956         if(this._last){
58957             this.last = this._last;
58958         }
58959     },
58960
58961     // private
58962     acceptsNav : function(row, col, cm){
58963         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58964     },
58965
58966     // private
58967     onEditorKey : function(field, e){
58968         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58969         if(k == e.TAB){
58970             e.stopEvent();
58971             ed.completeEdit();
58972             if(e.shiftKey){
58973                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58974             }else{
58975                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58976             }
58977         }else if(k == e.ENTER && !e.ctrlKey){
58978             e.stopEvent();
58979             ed.completeEdit();
58980             if(e.shiftKey){
58981                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58982             }else{
58983                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58984             }
58985         }else if(k == e.ESC){
58986             ed.cancelEdit();
58987         }
58988         if(newCell){
58989             g.startEditing(newCell[0], newCell[1]);
58990         }
58991     }
58992 });/*
58993  * Based on:
58994  * Ext JS Library 1.1.1
58995  * Copyright(c) 2006-2007, Ext JS, LLC.
58996  *
58997  * Originally Released Under LGPL - original licence link has changed is not relivant.
58998  *
58999  * Fork - LGPL
59000  * <script type="text/javascript">
59001  */
59002 /**
59003  * @class Roo.grid.CellSelectionModel
59004  * @extends Roo.grid.AbstractSelectionModel
59005  * This class provides the basic implementation for cell selection in a grid.
59006  * @constructor
59007  * @param {Object} config The object containing the configuration of this model.
59008  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59009  */
59010 Roo.grid.CellSelectionModel = function(config){
59011     Roo.apply(this, config);
59012
59013     this.selection = null;
59014
59015     this.addEvents({
59016         /**
59017              * @event beforerowselect
59018              * Fires before a cell is selected.
59019              * @param {SelectionModel} this
59020              * @param {Number} rowIndex The selected row index
59021              * @param {Number} colIndex The selected cell index
59022              */
59023             "beforecellselect" : true,
59024         /**
59025              * @event cellselect
59026              * Fires when a cell is selected.
59027              * @param {SelectionModel} this
59028              * @param {Number} rowIndex The selected row index
59029              * @param {Number} colIndex The selected cell index
59030              */
59031             "cellselect" : true,
59032         /**
59033              * @event selectionchange
59034              * Fires when the active selection changes.
59035              * @param {SelectionModel} this
59036              * @param {Object} selection null for no selection or an object (o) with two properties
59037                 <ul>
59038                 <li>o.record: the record object for the row the selection is in</li>
59039                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59040                 </ul>
59041              */
59042             "selectionchange" : true,
59043         /**
59044              * @event tabend
59045              * Fires when the tab (or enter) was pressed on the last editable cell
59046              * You can use this to trigger add new row.
59047              * @param {SelectionModel} this
59048              */
59049             "tabend" : true,
59050          /**
59051              * @event beforeeditnext
59052              * Fires before the next editable sell is made active
59053              * You can use this to skip to another cell or fire the tabend
59054              *    if you set cell to false
59055              * @param {Object} eventdata object : { cell : [ row, col ] } 
59056              */
59057             "beforeeditnext" : true
59058     });
59059     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59060 };
59061
59062 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59063     
59064     enter_is_tab: false,
59065
59066     /** @ignore */
59067     initEvents : function(){
59068         this.grid.on("mousedown", this.handleMouseDown, this);
59069         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59070         var view = this.grid.view;
59071         view.on("refresh", this.onViewChange, this);
59072         view.on("rowupdated", this.onRowUpdated, this);
59073         view.on("beforerowremoved", this.clearSelections, this);
59074         view.on("beforerowsinserted", this.clearSelections, this);
59075         if(this.grid.isEditor){
59076             this.grid.on("beforeedit", this.beforeEdit,  this);
59077         }
59078     },
59079
59080         //private
59081     beforeEdit : function(e){
59082         this.select(e.row, e.column, false, true, e.record);
59083     },
59084
59085         //private
59086     onRowUpdated : function(v, index, r){
59087         if(this.selection && this.selection.record == r){
59088             v.onCellSelect(index, this.selection.cell[1]);
59089         }
59090     },
59091
59092         //private
59093     onViewChange : function(){
59094         this.clearSelections(true);
59095     },
59096
59097         /**
59098          * Returns the currently selected cell,.
59099          * @return {Array} The selected cell (row, column) or null if none selected.
59100          */
59101     getSelectedCell : function(){
59102         return this.selection ? this.selection.cell : null;
59103     },
59104
59105     /**
59106      * Clears all selections.
59107      * @param {Boolean} true to prevent the gridview from being notified about the change.
59108      */
59109     clearSelections : function(preventNotify){
59110         var s = this.selection;
59111         if(s){
59112             if(preventNotify !== true){
59113                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59114             }
59115             this.selection = null;
59116             this.fireEvent("selectionchange", this, null);
59117         }
59118     },
59119
59120     /**
59121      * Returns true if there is a selection.
59122      * @return {Boolean}
59123      */
59124     hasSelection : function(){
59125         return this.selection ? true : false;
59126     },
59127
59128     /** @ignore */
59129     handleMouseDown : function(e, t){
59130         var v = this.grid.getView();
59131         if(this.isLocked()){
59132             return;
59133         };
59134         var row = v.findRowIndex(t);
59135         var cell = v.findCellIndex(t);
59136         if(row !== false && cell !== false){
59137             this.select(row, cell);
59138         }
59139     },
59140
59141     /**
59142      * Selects a cell.
59143      * @param {Number} rowIndex
59144      * @param {Number} collIndex
59145      */
59146     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59147         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59148             this.clearSelections();
59149             r = r || this.grid.dataSource.getAt(rowIndex);
59150             this.selection = {
59151                 record : r,
59152                 cell : [rowIndex, colIndex]
59153             };
59154             if(!preventViewNotify){
59155                 var v = this.grid.getView();
59156                 v.onCellSelect(rowIndex, colIndex);
59157                 if(preventFocus !== true){
59158                     v.focusCell(rowIndex, colIndex);
59159                 }
59160             }
59161             this.fireEvent("cellselect", this, rowIndex, colIndex);
59162             this.fireEvent("selectionchange", this, this.selection);
59163         }
59164     },
59165
59166         //private
59167     isSelectable : function(rowIndex, colIndex, cm){
59168         return !cm.isHidden(colIndex);
59169     },
59170
59171     /** @ignore */
59172     handleKeyDown : function(e){
59173         //Roo.log('Cell Sel Model handleKeyDown');
59174         if(!e.isNavKeyPress()){
59175             return;
59176         }
59177         var g = this.grid, s = this.selection;
59178         if(!s){
59179             e.stopEvent();
59180             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59181             if(cell){
59182                 this.select(cell[0], cell[1]);
59183             }
59184             return;
59185         }
59186         var sm = this;
59187         var walk = function(row, col, step){
59188             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59189         };
59190         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59191         var newCell;
59192
59193       
59194
59195         switch(k){
59196             case e.TAB:
59197                 // handled by onEditorKey
59198                 if (g.isEditor && g.editing) {
59199                     return;
59200                 }
59201                 if(e.shiftKey) {
59202                     newCell = walk(r, c-1, -1);
59203                 } else {
59204                     newCell = walk(r, c+1, 1);
59205                 }
59206                 break;
59207             
59208             case e.DOWN:
59209                newCell = walk(r+1, c, 1);
59210                 break;
59211             
59212             case e.UP:
59213                 newCell = walk(r-1, c, -1);
59214                 break;
59215             
59216             case e.RIGHT:
59217                 newCell = walk(r, c+1, 1);
59218                 break;
59219             
59220             case e.LEFT:
59221                 newCell = walk(r, c-1, -1);
59222                 break;
59223             
59224             case e.ENTER:
59225                 
59226                 if(g.isEditor && !g.editing){
59227                    g.startEditing(r, c);
59228                    e.stopEvent();
59229                    return;
59230                 }
59231                 
59232                 
59233              break;
59234         };
59235         if(newCell){
59236             this.select(newCell[0], newCell[1]);
59237             e.stopEvent();
59238             
59239         }
59240     },
59241
59242     acceptsNav : function(row, col, cm){
59243         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59244     },
59245     /**
59246      * Selects a cell.
59247      * @param {Number} field (not used) - as it's normally used as a listener
59248      * @param {Number} e - event - fake it by using
59249      *
59250      * var e = Roo.EventObjectImpl.prototype;
59251      * e.keyCode = e.TAB
59252      *
59253      * 
59254      */
59255     onEditorKey : function(field, e){
59256         
59257         var k = e.getKey(),
59258             newCell,
59259             g = this.grid,
59260             ed = g.activeEditor,
59261             forward = false;
59262         ///Roo.log('onEditorKey' + k);
59263         
59264         
59265         if (this.enter_is_tab && k == e.ENTER) {
59266             k = e.TAB;
59267         }
59268         
59269         if(k == e.TAB){
59270             if(e.shiftKey){
59271                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59272             }else{
59273                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59274                 forward = true;
59275             }
59276             
59277             e.stopEvent();
59278             
59279         } else if(k == e.ENTER &&  !e.ctrlKey){
59280             ed.completeEdit();
59281             e.stopEvent();
59282             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59283         
59284                 } else if(k == e.ESC){
59285             ed.cancelEdit();
59286         }
59287                 
59288         if (newCell) {
59289             var ecall = { cell : newCell, forward : forward };
59290             this.fireEvent('beforeeditnext', ecall );
59291             newCell = ecall.cell;
59292                         forward = ecall.forward;
59293         }
59294                 
59295         if(newCell){
59296             //Roo.log('next cell after edit');
59297             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59298         } else if (forward) {
59299             // tabbed past last
59300             this.fireEvent.defer(100, this, ['tabend',this]);
59301         }
59302     }
59303 });/*
59304  * Based on:
59305  * Ext JS Library 1.1.1
59306  * Copyright(c) 2006-2007, Ext JS, LLC.
59307  *
59308  * Originally Released Under LGPL - original licence link has changed is not relivant.
59309  *
59310  * Fork - LGPL
59311  * <script type="text/javascript">
59312  */
59313  
59314 /**
59315  * @class Roo.grid.EditorGrid
59316  * @extends Roo.grid.Grid
59317  * Class for creating and editable grid.
59318  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59319  * The container MUST have some type of size defined for the grid to fill. The container will be 
59320  * automatically set to position relative if it isn't already.
59321  * @param {Object} dataSource The data model to bind to
59322  * @param {Object} colModel The column model with info about this grid's columns
59323  */
59324 Roo.grid.EditorGrid = function(container, config){
59325     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59326     this.getGridEl().addClass("xedit-grid");
59327
59328     if(!this.selModel){
59329         this.selModel = new Roo.grid.CellSelectionModel();
59330     }
59331
59332     this.activeEditor = null;
59333
59334         this.addEvents({
59335             /**
59336              * @event beforeedit
59337              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59338              * <ul style="padding:5px;padding-left:16px;">
59339              * <li>grid - This grid</li>
59340              * <li>record - The record being edited</li>
59341              * <li>field - The field name being edited</li>
59342              * <li>value - The value for the field being edited.</li>
59343              * <li>row - The grid row index</li>
59344              * <li>column - The grid column index</li>
59345              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59346              * </ul>
59347              * @param {Object} e An edit event (see above for description)
59348              */
59349             "beforeedit" : true,
59350             /**
59351              * @event afteredit
59352              * Fires after a cell is edited. <br />
59353              * <ul style="padding:5px;padding-left:16px;">
59354              * <li>grid - This grid</li>
59355              * <li>record - The record being edited</li>
59356              * <li>field - The field name being edited</li>
59357              * <li>value - The value being set</li>
59358              * <li>originalValue - The original value for the field, before the edit.</li>
59359              * <li>row - The grid row index</li>
59360              * <li>column - The grid column index</li>
59361              * </ul>
59362              * @param {Object} e An edit event (see above for description)
59363              */
59364             "afteredit" : true,
59365             /**
59366              * @event validateedit
59367              * Fires after a cell is edited, but before the value is set in the record. 
59368          * You can use this to modify the value being set in the field, Return false
59369              * to cancel the change. The edit event object has the following properties <br />
59370              * <ul style="padding:5px;padding-left:16px;">
59371          * <li>editor - This editor</li>
59372              * <li>grid - This grid</li>
59373              * <li>record - The record being edited</li>
59374              * <li>field - The field name being edited</li>
59375              * <li>value - The value being set</li>
59376              * <li>originalValue - The original value for the field, before the edit.</li>
59377              * <li>row - The grid row index</li>
59378              * <li>column - The grid column index</li>
59379              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59380              * </ul>
59381              * @param {Object} e An edit event (see above for description)
59382              */
59383             "validateedit" : true
59384         });
59385     this.on("bodyscroll", this.stopEditing,  this);
59386     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59387 };
59388
59389 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59390     /**
59391      * @cfg {Number} clicksToEdit
59392      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59393      */
59394     clicksToEdit: 2,
59395
59396     // private
59397     isEditor : true,
59398     // private
59399     trackMouseOver: false, // causes very odd FF errors
59400
59401     onCellDblClick : function(g, row, col){
59402         this.startEditing(row, col);
59403     },
59404
59405     onEditComplete : function(ed, value, startValue){
59406         this.editing = false;
59407         this.activeEditor = null;
59408         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59409         var r = ed.record;
59410         var field = this.colModel.getDataIndex(ed.col);
59411         var e = {
59412             grid: this,
59413             record: r,
59414             field: field,
59415             originalValue: startValue,
59416             value: value,
59417             row: ed.row,
59418             column: ed.col,
59419             cancel:false,
59420             editor: ed
59421         };
59422         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59423         cell.show();
59424           
59425         if(String(value) !== String(startValue)){
59426             
59427             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59428                 r.set(field, e.value);
59429                 // if we are dealing with a combo box..
59430                 // then we also set the 'name' colum to be the displayField
59431                 if (ed.field.displayField && ed.field.name) {
59432                     r.set(ed.field.name, ed.field.el.dom.value);
59433                 }
59434                 
59435                 delete e.cancel; //?? why!!!
59436                 this.fireEvent("afteredit", e);
59437             }
59438         } else {
59439             this.fireEvent("afteredit", e); // always fire it!
59440         }
59441         this.view.focusCell(ed.row, ed.col);
59442     },
59443
59444     /**
59445      * Starts editing the specified for the specified row/column
59446      * @param {Number} rowIndex
59447      * @param {Number} colIndex
59448      */
59449     startEditing : function(row, col){
59450         this.stopEditing();
59451         if(this.colModel.isCellEditable(col, row)){
59452             this.view.ensureVisible(row, col, true);
59453           
59454             var r = this.dataSource.getAt(row);
59455             var field = this.colModel.getDataIndex(col);
59456             var cell = Roo.get(this.view.getCell(row,col));
59457             var e = {
59458                 grid: this,
59459                 record: r,
59460                 field: field,
59461                 value: r.data[field],
59462                 row: row,
59463                 column: col,
59464                 cancel:false 
59465             };
59466             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59467                 this.editing = true;
59468                 var ed = this.colModel.getCellEditor(col, row);
59469                 
59470                 if (!ed) {
59471                     return;
59472                 }
59473                 if(!ed.rendered){
59474                     ed.render(ed.parentEl || document.body);
59475                 }
59476                 ed.field.reset();
59477                
59478                 cell.hide();
59479                 
59480                 (function(){ // complex but required for focus issues in safari, ie and opera
59481                     ed.row = row;
59482                     ed.col = col;
59483                     ed.record = r;
59484                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59485                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59486                     this.activeEditor = ed;
59487                     var v = r.data[field];
59488                     ed.startEdit(this.view.getCell(row, col), v);
59489                     // combo's with 'displayField and name set
59490                     if (ed.field.displayField && ed.field.name) {
59491                         ed.field.el.dom.value = r.data[ed.field.name];
59492                     }
59493                     
59494                     
59495                 }).defer(50, this);
59496             }
59497         }
59498     },
59499         
59500     /**
59501      * Stops any active editing
59502      */
59503     stopEditing : function(){
59504         if(this.activeEditor){
59505             this.activeEditor.completeEdit();
59506         }
59507         this.activeEditor = null;
59508     },
59509         
59510          /**
59511      * Called to get grid's drag proxy text, by default returns this.ddText.
59512      * @return {String}
59513      */
59514     getDragDropText : function(){
59515         var count = this.selModel.getSelectedCell() ? 1 : 0;
59516         return String.format(this.ddText, count, count == 1 ? '' : 's');
59517     }
59518         
59519 });/*
59520  * Based on:
59521  * Ext JS Library 1.1.1
59522  * Copyright(c) 2006-2007, Ext JS, LLC.
59523  *
59524  * Originally Released Under LGPL - original licence link has changed is not relivant.
59525  *
59526  * Fork - LGPL
59527  * <script type="text/javascript">
59528  */
59529
59530 // private - not really -- you end up using it !
59531 // This is a support class used internally by the Grid components
59532
59533 /**
59534  * @class Roo.grid.GridEditor
59535  * @extends Roo.Editor
59536  * Class for creating and editable grid elements.
59537  * @param {Object} config any settings (must include field)
59538  */
59539 Roo.grid.GridEditor = function(field, config){
59540     if (!config && field.field) {
59541         config = field;
59542         field = Roo.factory(config.field, Roo.form);
59543     }
59544     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59545     field.monitorTab = false;
59546 };
59547
59548 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59549     
59550     /**
59551      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59552      */
59553     
59554     alignment: "tl-tl",
59555     autoSize: "width",
59556     hideEl : false,
59557     cls: "x-small-editor x-grid-editor",
59558     shim:false,
59559     shadow:"frame"
59560 });/*
59561  * Based on:
59562  * Ext JS Library 1.1.1
59563  * Copyright(c) 2006-2007, Ext JS, LLC.
59564  *
59565  * Originally Released Under LGPL - original licence link has changed is not relivant.
59566  *
59567  * Fork - LGPL
59568  * <script type="text/javascript">
59569  */
59570   
59571
59572   
59573 Roo.grid.PropertyRecord = Roo.data.Record.create([
59574     {name:'name',type:'string'},  'value'
59575 ]);
59576
59577
59578 Roo.grid.PropertyStore = function(grid, source){
59579     this.grid = grid;
59580     this.store = new Roo.data.Store({
59581         recordType : Roo.grid.PropertyRecord
59582     });
59583     this.store.on('update', this.onUpdate,  this);
59584     if(source){
59585         this.setSource(source);
59586     }
59587     Roo.grid.PropertyStore.superclass.constructor.call(this);
59588 };
59589
59590
59591
59592 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59593     setSource : function(o){
59594         this.source = o;
59595         this.store.removeAll();
59596         var data = [];
59597         for(var k in o){
59598             if(this.isEditableValue(o[k])){
59599                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59600             }
59601         }
59602         this.store.loadRecords({records: data}, {}, true);
59603     },
59604
59605     onUpdate : function(ds, record, type){
59606         if(type == Roo.data.Record.EDIT){
59607             var v = record.data['value'];
59608             var oldValue = record.modified['value'];
59609             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59610                 this.source[record.id] = v;
59611                 record.commit();
59612                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59613             }else{
59614                 record.reject();
59615             }
59616         }
59617     },
59618
59619     getProperty : function(row){
59620        return this.store.getAt(row);
59621     },
59622
59623     isEditableValue: function(val){
59624         if(val && val instanceof Date){
59625             return true;
59626         }else if(typeof val == 'object' || typeof val == 'function'){
59627             return false;
59628         }
59629         return true;
59630     },
59631
59632     setValue : function(prop, value){
59633         this.source[prop] = value;
59634         this.store.getById(prop).set('value', value);
59635     },
59636
59637     getSource : function(){
59638         return this.source;
59639     }
59640 });
59641
59642 Roo.grid.PropertyColumnModel = function(grid, store){
59643     this.grid = grid;
59644     var g = Roo.grid;
59645     g.PropertyColumnModel.superclass.constructor.call(this, [
59646         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59647         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59648     ]);
59649     this.store = store;
59650     this.bselect = Roo.DomHelper.append(document.body, {
59651         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59652             {tag: 'option', value: 'true', html: 'true'},
59653             {tag: 'option', value: 'false', html: 'false'}
59654         ]
59655     });
59656     Roo.id(this.bselect);
59657     var f = Roo.form;
59658     this.editors = {
59659         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59660         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59661         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59662         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59663         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59664     };
59665     this.renderCellDelegate = this.renderCell.createDelegate(this);
59666     this.renderPropDelegate = this.renderProp.createDelegate(this);
59667 };
59668
59669 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59670     
59671     
59672     nameText : 'Name',
59673     valueText : 'Value',
59674     
59675     dateFormat : 'm/j/Y',
59676     
59677     
59678     renderDate : function(dateVal){
59679         return dateVal.dateFormat(this.dateFormat);
59680     },
59681
59682     renderBool : function(bVal){
59683         return bVal ? 'true' : 'false';
59684     },
59685
59686     isCellEditable : function(colIndex, rowIndex){
59687         return colIndex == 1;
59688     },
59689
59690     getRenderer : function(col){
59691         return col == 1 ?
59692             this.renderCellDelegate : this.renderPropDelegate;
59693     },
59694
59695     renderProp : function(v){
59696         return this.getPropertyName(v);
59697     },
59698
59699     renderCell : function(val){
59700         var rv = val;
59701         if(val instanceof Date){
59702             rv = this.renderDate(val);
59703         }else if(typeof val == 'boolean'){
59704             rv = this.renderBool(val);
59705         }
59706         return Roo.util.Format.htmlEncode(rv);
59707     },
59708
59709     getPropertyName : function(name){
59710         var pn = this.grid.propertyNames;
59711         return pn && pn[name] ? pn[name] : name;
59712     },
59713
59714     getCellEditor : function(colIndex, rowIndex){
59715         var p = this.store.getProperty(rowIndex);
59716         var n = p.data['name'], val = p.data['value'];
59717         
59718         if(typeof(this.grid.customEditors[n]) == 'string'){
59719             return this.editors[this.grid.customEditors[n]];
59720         }
59721         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59722             return this.grid.customEditors[n];
59723         }
59724         if(val instanceof Date){
59725             return this.editors['date'];
59726         }else if(typeof val == 'number'){
59727             return this.editors['number'];
59728         }else if(typeof val == 'boolean'){
59729             return this.editors['boolean'];
59730         }else{
59731             return this.editors['string'];
59732         }
59733     }
59734 });
59735
59736 /**
59737  * @class Roo.grid.PropertyGrid
59738  * @extends Roo.grid.EditorGrid
59739  * This class represents the  interface of a component based property grid control.
59740  * <br><br>Usage:<pre><code>
59741  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59742       
59743  });
59744  // set any options
59745  grid.render();
59746  * </code></pre>
59747   
59748  * @constructor
59749  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59750  * The container MUST have some type of size defined for the grid to fill. The container will be
59751  * automatically set to position relative if it isn't already.
59752  * @param {Object} config A config object that sets properties on this grid.
59753  */
59754 Roo.grid.PropertyGrid = function(container, config){
59755     config = config || {};
59756     var store = new Roo.grid.PropertyStore(this);
59757     this.store = store;
59758     var cm = new Roo.grid.PropertyColumnModel(this, store);
59759     store.store.sort('name', 'ASC');
59760     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59761         ds: store.store,
59762         cm: cm,
59763         enableColLock:false,
59764         enableColumnMove:false,
59765         stripeRows:false,
59766         trackMouseOver: false,
59767         clicksToEdit:1
59768     }, config));
59769     this.getGridEl().addClass('x-props-grid');
59770     this.lastEditRow = null;
59771     this.on('columnresize', this.onColumnResize, this);
59772     this.addEvents({
59773          /**
59774              * @event beforepropertychange
59775              * Fires before a property changes (return false to stop?)
59776              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59777              * @param {String} id Record Id
59778              * @param {String} newval New Value
59779          * @param {String} oldval Old Value
59780              */
59781         "beforepropertychange": true,
59782         /**
59783              * @event propertychange
59784              * Fires after a property changes
59785              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59786              * @param {String} id Record Id
59787              * @param {String} newval New Value
59788          * @param {String} oldval Old Value
59789              */
59790         "propertychange": true
59791     });
59792     this.customEditors = this.customEditors || {};
59793 };
59794 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59795     
59796      /**
59797      * @cfg {Object} customEditors map of colnames=> custom editors.
59798      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59799      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59800      * false disables editing of the field.
59801          */
59802     
59803       /**
59804      * @cfg {Object} propertyNames map of property Names to their displayed value
59805          */
59806     
59807     render : function(){
59808         Roo.grid.PropertyGrid.superclass.render.call(this);
59809         this.autoSize.defer(100, this);
59810     },
59811
59812     autoSize : function(){
59813         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59814         if(this.view){
59815             this.view.fitColumns();
59816         }
59817     },
59818
59819     onColumnResize : function(){
59820         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59821         this.autoSize();
59822     },
59823     /**
59824      * Sets the data for the Grid
59825      * accepts a Key => Value object of all the elements avaiable.
59826      * @param {Object} data  to appear in grid.
59827      */
59828     setSource : function(source){
59829         this.store.setSource(source);
59830         //this.autoSize();
59831     },
59832     /**
59833      * Gets all the data from the grid.
59834      * @return {Object} data  data stored in grid
59835      */
59836     getSource : function(){
59837         return this.store.getSource();
59838     }
59839 });/*
59840   
59841  * Licence LGPL
59842  
59843  */
59844  
59845 /**
59846  * @class Roo.grid.Calendar
59847  * @extends Roo.util.Grid
59848  * This class extends the Grid to provide a calendar widget
59849  * <br><br>Usage:<pre><code>
59850  var grid = new Roo.grid.Calendar("my-container-id", {
59851      ds: myDataStore,
59852      cm: myColModel,
59853      selModel: mySelectionModel,
59854      autoSizeColumns: true,
59855      monitorWindowResize: false,
59856      trackMouseOver: true
59857      eventstore : real data store..
59858  });
59859  // set any options
59860  grid.render();
59861   
59862   * @constructor
59863  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59864  * The container MUST have some type of size defined for the grid to fill. The container will be
59865  * automatically set to position relative if it isn't already.
59866  * @param {Object} config A config object that sets properties on this grid.
59867  */
59868 Roo.grid.Calendar = function(container, config){
59869         // initialize the container
59870         this.container = Roo.get(container);
59871         this.container.update("");
59872         this.container.setStyle("overflow", "hidden");
59873     this.container.addClass('x-grid-container');
59874
59875     this.id = this.container.id;
59876
59877     Roo.apply(this, config);
59878     // check and correct shorthanded configs
59879     
59880     var rows = [];
59881     var d =1;
59882     for (var r = 0;r < 6;r++) {
59883         
59884         rows[r]=[];
59885         for (var c =0;c < 7;c++) {
59886             rows[r][c]= '';
59887         }
59888     }
59889     if (this.eventStore) {
59890         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59891         this.eventStore.on('load',this.onLoad, this);
59892         this.eventStore.on('beforeload',this.clearEvents, this);
59893          
59894     }
59895     
59896     this.dataSource = new Roo.data.Store({
59897             proxy: new Roo.data.MemoryProxy(rows),
59898             reader: new Roo.data.ArrayReader({}, [
59899                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59900     });
59901
59902     this.dataSource.load();
59903     this.ds = this.dataSource;
59904     this.ds.xmodule = this.xmodule || false;
59905     
59906     
59907     var cellRender = function(v,x,r)
59908     {
59909         return String.format(
59910             '<div class="fc-day  fc-widget-content"><div>' +
59911                 '<div class="fc-event-container"></div>' +
59912                 '<div class="fc-day-number">{0}</div>'+
59913                 
59914                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59915             '</div></div>', v);
59916     
59917     }
59918     
59919     
59920     this.colModel = new Roo.grid.ColumnModel( [
59921         {
59922             xtype: 'ColumnModel',
59923             xns: Roo.grid,
59924             dataIndex : 'weekday0',
59925             header : 'Sunday',
59926             renderer : cellRender
59927         },
59928         {
59929             xtype: 'ColumnModel',
59930             xns: Roo.grid,
59931             dataIndex : 'weekday1',
59932             header : 'Monday',
59933             renderer : cellRender
59934         },
59935         {
59936             xtype: 'ColumnModel',
59937             xns: Roo.grid,
59938             dataIndex : 'weekday2',
59939             header : 'Tuesday',
59940             renderer : cellRender
59941         },
59942         {
59943             xtype: 'ColumnModel',
59944             xns: Roo.grid,
59945             dataIndex : 'weekday3',
59946             header : 'Wednesday',
59947             renderer : cellRender
59948         },
59949         {
59950             xtype: 'ColumnModel',
59951             xns: Roo.grid,
59952             dataIndex : 'weekday4',
59953             header : 'Thursday',
59954             renderer : cellRender
59955         },
59956         {
59957             xtype: 'ColumnModel',
59958             xns: Roo.grid,
59959             dataIndex : 'weekday5',
59960             header : 'Friday',
59961             renderer : cellRender
59962         },
59963         {
59964             xtype: 'ColumnModel',
59965             xns: Roo.grid,
59966             dataIndex : 'weekday6',
59967             header : 'Saturday',
59968             renderer : cellRender
59969         }
59970     ]);
59971     this.cm = this.colModel;
59972     this.cm.xmodule = this.xmodule || false;
59973  
59974         
59975           
59976     //this.selModel = new Roo.grid.CellSelectionModel();
59977     //this.sm = this.selModel;
59978     //this.selModel.init(this);
59979     
59980     
59981     if(this.width){
59982         this.container.setWidth(this.width);
59983     }
59984
59985     if(this.height){
59986         this.container.setHeight(this.height);
59987     }
59988     /** @private */
59989         this.addEvents({
59990         // raw events
59991         /**
59992          * @event click
59993          * The raw click event for the entire grid.
59994          * @param {Roo.EventObject} e
59995          */
59996         "click" : true,
59997         /**
59998          * @event dblclick
59999          * The raw dblclick event for the entire grid.
60000          * @param {Roo.EventObject} e
60001          */
60002         "dblclick" : true,
60003         /**
60004          * @event contextmenu
60005          * The raw contextmenu event for the entire grid.
60006          * @param {Roo.EventObject} e
60007          */
60008         "contextmenu" : true,
60009         /**
60010          * @event mousedown
60011          * The raw mousedown event for the entire grid.
60012          * @param {Roo.EventObject} e
60013          */
60014         "mousedown" : true,
60015         /**
60016          * @event mouseup
60017          * The raw mouseup event for the entire grid.
60018          * @param {Roo.EventObject} e
60019          */
60020         "mouseup" : true,
60021         /**
60022          * @event mouseover
60023          * The raw mouseover event for the entire grid.
60024          * @param {Roo.EventObject} e
60025          */
60026         "mouseover" : true,
60027         /**
60028          * @event mouseout
60029          * The raw mouseout event for the entire grid.
60030          * @param {Roo.EventObject} e
60031          */
60032         "mouseout" : true,
60033         /**
60034          * @event keypress
60035          * The raw keypress event for the entire grid.
60036          * @param {Roo.EventObject} e
60037          */
60038         "keypress" : true,
60039         /**
60040          * @event keydown
60041          * The raw keydown event for the entire grid.
60042          * @param {Roo.EventObject} e
60043          */
60044         "keydown" : true,
60045
60046         // custom events
60047
60048         /**
60049          * @event cellclick
60050          * Fires when a cell is clicked
60051          * @param {Grid} this
60052          * @param {Number} rowIndex
60053          * @param {Number} columnIndex
60054          * @param {Roo.EventObject} e
60055          */
60056         "cellclick" : true,
60057         /**
60058          * @event celldblclick
60059          * Fires when a cell is double clicked
60060          * @param {Grid} this
60061          * @param {Number} rowIndex
60062          * @param {Number} columnIndex
60063          * @param {Roo.EventObject} e
60064          */
60065         "celldblclick" : true,
60066         /**
60067          * @event rowclick
60068          * Fires when a row is clicked
60069          * @param {Grid} this
60070          * @param {Number} rowIndex
60071          * @param {Roo.EventObject} e
60072          */
60073         "rowclick" : true,
60074         /**
60075          * @event rowdblclick
60076          * Fires when a row is double clicked
60077          * @param {Grid} this
60078          * @param {Number} rowIndex
60079          * @param {Roo.EventObject} e
60080          */
60081         "rowdblclick" : true,
60082         /**
60083          * @event headerclick
60084          * Fires when a header is clicked
60085          * @param {Grid} this
60086          * @param {Number} columnIndex
60087          * @param {Roo.EventObject} e
60088          */
60089         "headerclick" : true,
60090         /**
60091          * @event headerdblclick
60092          * Fires when a header cell is double clicked
60093          * @param {Grid} this
60094          * @param {Number} columnIndex
60095          * @param {Roo.EventObject} e
60096          */
60097         "headerdblclick" : true,
60098         /**
60099          * @event rowcontextmenu
60100          * Fires when a row is right clicked
60101          * @param {Grid} this
60102          * @param {Number} rowIndex
60103          * @param {Roo.EventObject} e
60104          */
60105         "rowcontextmenu" : true,
60106         /**
60107          * @event cellcontextmenu
60108          * Fires when a cell is right clicked
60109          * @param {Grid} this
60110          * @param {Number} rowIndex
60111          * @param {Number} cellIndex
60112          * @param {Roo.EventObject} e
60113          */
60114          "cellcontextmenu" : true,
60115         /**
60116          * @event headercontextmenu
60117          * Fires when a header is right clicked
60118          * @param {Grid} this
60119          * @param {Number} columnIndex
60120          * @param {Roo.EventObject} e
60121          */
60122         "headercontextmenu" : true,
60123         /**
60124          * @event bodyscroll
60125          * Fires when the body element is scrolled
60126          * @param {Number} scrollLeft
60127          * @param {Number} scrollTop
60128          */
60129         "bodyscroll" : true,
60130         /**
60131          * @event columnresize
60132          * Fires when the user resizes a column
60133          * @param {Number} columnIndex
60134          * @param {Number} newSize
60135          */
60136         "columnresize" : true,
60137         /**
60138          * @event columnmove
60139          * Fires when the user moves a column
60140          * @param {Number} oldIndex
60141          * @param {Number} newIndex
60142          */
60143         "columnmove" : true,
60144         /**
60145          * @event startdrag
60146          * Fires when row(s) start being dragged
60147          * @param {Grid} this
60148          * @param {Roo.GridDD} dd The drag drop object
60149          * @param {event} e The raw browser event
60150          */
60151         "startdrag" : true,
60152         /**
60153          * @event enddrag
60154          * Fires when a drag operation is complete
60155          * @param {Grid} this
60156          * @param {Roo.GridDD} dd The drag drop object
60157          * @param {event} e The raw browser event
60158          */
60159         "enddrag" : true,
60160         /**
60161          * @event dragdrop
60162          * Fires when dragged row(s) are dropped on a valid DD target
60163          * @param {Grid} this
60164          * @param {Roo.GridDD} dd The drag drop object
60165          * @param {String} targetId The target drag drop object
60166          * @param {event} e The raw browser event
60167          */
60168         "dragdrop" : true,
60169         /**
60170          * @event dragover
60171          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60172          * @param {Grid} this
60173          * @param {Roo.GridDD} dd The drag drop object
60174          * @param {String} targetId The target drag drop object
60175          * @param {event} e The raw browser event
60176          */
60177         "dragover" : true,
60178         /**
60179          * @event dragenter
60180          *  Fires when the dragged row(s) first cross another DD target while being dragged
60181          * @param {Grid} this
60182          * @param {Roo.GridDD} dd The drag drop object
60183          * @param {String} targetId The target drag drop object
60184          * @param {event} e The raw browser event
60185          */
60186         "dragenter" : true,
60187         /**
60188          * @event dragout
60189          * Fires when the dragged row(s) leave another DD target while being dragged
60190          * @param {Grid} this
60191          * @param {Roo.GridDD} dd The drag drop object
60192          * @param {String} targetId The target drag drop object
60193          * @param {event} e The raw browser event
60194          */
60195         "dragout" : true,
60196         /**
60197          * @event rowclass
60198          * Fires when a row is rendered, so you can change add a style to it.
60199          * @param {GridView} gridview   The grid view
60200          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60201          */
60202         'rowclass' : true,
60203
60204         /**
60205          * @event render
60206          * Fires when the grid is rendered
60207          * @param {Grid} grid
60208          */
60209         'render' : true,
60210             /**
60211              * @event select
60212              * Fires when a date is selected
60213              * @param {DatePicker} this
60214              * @param {Date} date The selected date
60215              */
60216         'select': true,
60217         /**
60218              * @event monthchange
60219              * Fires when the displayed month changes 
60220              * @param {DatePicker} this
60221              * @param {Date} date The selected month
60222              */
60223         'monthchange': true,
60224         /**
60225              * @event evententer
60226              * Fires when mouse over an event
60227              * @param {Calendar} this
60228              * @param {event} Event
60229              */
60230         'evententer': true,
60231         /**
60232              * @event eventleave
60233              * Fires when the mouse leaves an
60234              * @param {Calendar} this
60235              * @param {event}
60236              */
60237         'eventleave': true,
60238         /**
60239              * @event eventclick
60240              * Fires when the mouse click an
60241              * @param {Calendar} this
60242              * @param {event}
60243              */
60244         'eventclick': true,
60245         /**
60246              * @event eventrender
60247              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60248              * @param {Calendar} this
60249              * @param {data} data to be modified
60250              */
60251         'eventrender': true
60252         
60253     });
60254
60255     Roo.grid.Grid.superclass.constructor.call(this);
60256     this.on('render', function() {
60257         this.view.el.addClass('x-grid-cal'); 
60258         
60259         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60260
60261     },this);
60262     
60263     if (!Roo.grid.Calendar.style) {
60264         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60265             
60266             
60267             '.x-grid-cal .x-grid-col' :  {
60268                 height: 'auto !important',
60269                 'vertical-align': 'top'
60270             },
60271             '.x-grid-cal  .fc-event-hori' : {
60272                 height: '14px'
60273             }
60274              
60275             
60276         }, Roo.id());
60277     }
60278
60279     
60280     
60281 };
60282 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60283     /**
60284      * @cfg {Store} eventStore The store that loads events.
60285      */
60286     eventStore : 25,
60287
60288      
60289     activeDate : false,
60290     startDay : 0,
60291     autoWidth : true,
60292     monitorWindowResize : false,
60293
60294     
60295     resizeColumns : function() {
60296         var col = (this.view.el.getWidth() / 7) - 3;
60297         // loop through cols, and setWidth
60298         for(var i =0 ; i < 7 ; i++){
60299             this.cm.setColumnWidth(i, col);
60300         }
60301     },
60302      setDate :function(date) {
60303         
60304         Roo.log('setDate?');
60305         
60306         this.resizeColumns();
60307         var vd = this.activeDate;
60308         this.activeDate = date;
60309 //        if(vd && this.el){
60310 //            var t = date.getTime();
60311 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60312 //                Roo.log('using add remove');
60313 //                
60314 //                this.fireEvent('monthchange', this, date);
60315 //                
60316 //                this.cells.removeClass("fc-state-highlight");
60317 //                this.cells.each(function(c){
60318 //                   if(c.dateValue == t){
60319 //                       c.addClass("fc-state-highlight");
60320 //                       setTimeout(function(){
60321 //                            try{c.dom.firstChild.focus();}catch(e){}
60322 //                       }, 50);
60323 //                       return false;
60324 //                   }
60325 //                   return true;
60326 //                });
60327 //                return;
60328 //            }
60329 //        }
60330         
60331         var days = date.getDaysInMonth();
60332         
60333         var firstOfMonth = date.getFirstDateOfMonth();
60334         var startingPos = firstOfMonth.getDay()-this.startDay;
60335         
60336         if(startingPos < this.startDay){
60337             startingPos += 7;
60338         }
60339         
60340         var pm = date.add(Date.MONTH, -1);
60341         var prevStart = pm.getDaysInMonth()-startingPos;
60342 //        
60343         
60344         
60345         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60346         
60347         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60348         //this.cells.addClassOnOver('fc-state-hover');
60349         
60350         var cells = this.cells.elements;
60351         var textEls = this.textNodes;
60352         
60353         //Roo.each(cells, function(cell){
60354         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60355         //});
60356         
60357         days += startingPos;
60358
60359         // convert everything to numbers so it's fast
60360         var day = 86400000;
60361         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60362         //Roo.log(d);
60363         //Roo.log(pm);
60364         //Roo.log(prevStart);
60365         
60366         var today = new Date().clearTime().getTime();
60367         var sel = date.clearTime().getTime();
60368         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60369         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60370         var ddMatch = this.disabledDatesRE;
60371         var ddText = this.disabledDatesText;
60372         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60373         var ddaysText = this.disabledDaysText;
60374         var format = this.format;
60375         
60376         var setCellClass = function(cal, cell){
60377             
60378             //Roo.log('set Cell Class');
60379             cell.title = "";
60380             var t = d.getTime();
60381             
60382             //Roo.log(d);
60383             
60384             
60385             cell.dateValue = t;
60386             if(t == today){
60387                 cell.className += " fc-today";
60388                 cell.className += " fc-state-highlight";
60389                 cell.title = cal.todayText;
60390             }
60391             if(t == sel){
60392                 // disable highlight in other month..
60393                 cell.className += " fc-state-highlight";
60394                 
60395             }
60396             // disabling
60397             if(t < min) {
60398                 //cell.className = " fc-state-disabled";
60399                 cell.title = cal.minText;
60400                 return;
60401             }
60402             if(t > max) {
60403                 //cell.className = " fc-state-disabled";
60404                 cell.title = cal.maxText;
60405                 return;
60406             }
60407             if(ddays){
60408                 if(ddays.indexOf(d.getDay()) != -1){
60409                     // cell.title = ddaysText;
60410                    // cell.className = " fc-state-disabled";
60411                 }
60412             }
60413             if(ddMatch && format){
60414                 var fvalue = d.dateFormat(format);
60415                 if(ddMatch.test(fvalue)){
60416                     cell.title = ddText.replace("%0", fvalue);
60417                    cell.className = " fc-state-disabled";
60418                 }
60419             }
60420             
60421             if (!cell.initialClassName) {
60422                 cell.initialClassName = cell.dom.className;
60423             }
60424             
60425             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60426         };
60427
60428         var i = 0;
60429         
60430         for(; i < startingPos; i++) {
60431             cells[i].dayName =  (++prevStart);
60432             Roo.log(textEls[i]);
60433             d.setDate(d.getDate()+1);
60434             
60435             //cells[i].className = "fc-past fc-other-month";
60436             setCellClass(this, cells[i]);
60437         }
60438         
60439         var intDay = 0;
60440         
60441         for(; i < days; i++){
60442             intDay = i - startingPos + 1;
60443             cells[i].dayName =  (intDay);
60444             d.setDate(d.getDate()+1);
60445             
60446             cells[i].className = ''; // "x-date-active";
60447             setCellClass(this, cells[i]);
60448         }
60449         var extraDays = 0;
60450         
60451         for(; i < 42; i++) {
60452             //textEls[i].innerHTML = (++extraDays);
60453             
60454             d.setDate(d.getDate()+1);
60455             cells[i].dayName = (++extraDays);
60456             cells[i].className = "fc-future fc-other-month";
60457             setCellClass(this, cells[i]);
60458         }
60459         
60460         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60461         
60462         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60463         
60464         // this will cause all the cells to mis
60465         var rows= [];
60466         var i =0;
60467         for (var r = 0;r < 6;r++) {
60468             for (var c =0;c < 7;c++) {
60469                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60470             }    
60471         }
60472         
60473         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60474         for(i=0;i<cells.length;i++) {
60475             
60476             this.cells.elements[i].dayName = cells[i].dayName ;
60477             this.cells.elements[i].className = cells[i].className;
60478             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60479             this.cells.elements[i].title = cells[i].title ;
60480             this.cells.elements[i].dateValue = cells[i].dateValue ;
60481         }
60482         
60483         
60484         
60485         
60486         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60487         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60488         
60489         ////if(totalRows != 6){
60490             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60491            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60492        // }
60493         
60494         this.fireEvent('monthchange', this, date);
60495         
60496         
60497     },
60498  /**
60499      * Returns the grid's SelectionModel.
60500      * @return {SelectionModel}
60501      */
60502     getSelectionModel : function(){
60503         if(!this.selModel){
60504             this.selModel = new Roo.grid.CellSelectionModel();
60505         }
60506         return this.selModel;
60507     },
60508
60509     load: function() {
60510         this.eventStore.load()
60511         
60512         
60513         
60514     },
60515     
60516     findCell : function(dt) {
60517         dt = dt.clearTime().getTime();
60518         var ret = false;
60519         this.cells.each(function(c){
60520             //Roo.log("check " +c.dateValue + '?=' + dt);
60521             if(c.dateValue == dt){
60522                 ret = c;
60523                 return false;
60524             }
60525             return true;
60526         });
60527         
60528         return ret;
60529     },
60530     
60531     findCells : function(rec) {
60532         var s = rec.data.start_dt.clone().clearTime().getTime();
60533        // Roo.log(s);
60534         var e= rec.data.end_dt.clone().clearTime().getTime();
60535        // Roo.log(e);
60536         var ret = [];
60537         this.cells.each(function(c){
60538              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60539             
60540             if(c.dateValue > e){
60541                 return ;
60542             }
60543             if(c.dateValue < s){
60544                 return ;
60545             }
60546             ret.push(c);
60547         });
60548         
60549         return ret;    
60550     },
60551     
60552     findBestRow: function(cells)
60553     {
60554         var ret = 0;
60555         
60556         for (var i =0 ; i < cells.length;i++) {
60557             ret  = Math.max(cells[i].rows || 0,ret);
60558         }
60559         return ret;
60560         
60561     },
60562     
60563     
60564     addItem : function(rec)
60565     {
60566         // look for vertical location slot in
60567         var cells = this.findCells(rec);
60568         
60569         rec.row = this.findBestRow(cells);
60570         
60571         // work out the location.
60572         
60573         var crow = false;
60574         var rows = [];
60575         for(var i =0; i < cells.length; i++) {
60576             if (!crow) {
60577                 crow = {
60578                     start : cells[i],
60579                     end :  cells[i]
60580                 };
60581                 continue;
60582             }
60583             if (crow.start.getY() == cells[i].getY()) {
60584                 // on same row.
60585                 crow.end = cells[i];
60586                 continue;
60587             }
60588             // different row.
60589             rows.push(crow);
60590             crow = {
60591                 start: cells[i],
60592                 end : cells[i]
60593             };
60594             
60595         }
60596         
60597         rows.push(crow);
60598         rec.els = [];
60599         rec.rows = rows;
60600         rec.cells = cells;
60601         for (var i = 0; i < cells.length;i++) {
60602             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60603             
60604         }
60605         
60606         
60607     },
60608     
60609     clearEvents: function() {
60610         
60611         if (!this.eventStore.getCount()) {
60612             return;
60613         }
60614         // reset number of rows in cells.
60615         Roo.each(this.cells.elements, function(c){
60616             c.rows = 0;
60617         });
60618         
60619         this.eventStore.each(function(e) {
60620             this.clearEvent(e);
60621         },this);
60622         
60623     },
60624     
60625     clearEvent : function(ev)
60626     {
60627         if (ev.els) {
60628             Roo.each(ev.els, function(el) {
60629                 el.un('mouseenter' ,this.onEventEnter, this);
60630                 el.un('mouseleave' ,this.onEventLeave, this);
60631                 el.remove();
60632             },this);
60633             ev.els = [];
60634         }
60635     },
60636     
60637     
60638     renderEvent : function(ev,ctr) {
60639         if (!ctr) {
60640              ctr = this.view.el.select('.fc-event-container',true).first();
60641         }
60642         
60643          
60644         this.clearEvent(ev);
60645             //code
60646        
60647         
60648         
60649         ev.els = [];
60650         var cells = ev.cells;
60651         var rows = ev.rows;
60652         this.fireEvent('eventrender', this, ev);
60653         
60654         for(var i =0; i < rows.length; i++) {
60655             
60656             cls = '';
60657             if (i == 0) {
60658                 cls += ' fc-event-start';
60659             }
60660             if ((i+1) == rows.length) {
60661                 cls += ' fc-event-end';
60662             }
60663             
60664             //Roo.log(ev.data);
60665             // how many rows should it span..
60666             var cg = this.eventTmpl.append(ctr,Roo.apply({
60667                 fccls : cls
60668                 
60669             }, ev.data) , true);
60670             
60671             
60672             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60673             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60674             cg.on('click', this.onEventClick, this, ev);
60675             
60676             ev.els.push(cg);
60677             
60678             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60679             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60680             //Roo.log(cg);
60681              
60682             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60683             cg.setWidth(ebox.right - sbox.x -2);
60684         }
60685     },
60686     
60687     renderEvents: function()
60688     {   
60689         // first make sure there is enough space..
60690         
60691         if (!this.eventTmpl) {
60692             this.eventTmpl = new Roo.Template(
60693                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60694                     '<div class="fc-event-inner">' +
60695                         '<span class="fc-event-time">{time}</span>' +
60696                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60697                     '</div>' +
60698                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60699                 '</div>'
60700             );
60701                 
60702         }
60703                
60704         
60705         
60706         this.cells.each(function(c) {
60707             //Roo.log(c.select('.fc-day-content div',true).first());
60708             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60709         });
60710         
60711         var ctr = this.view.el.select('.fc-event-container',true).first();
60712         
60713         var cls;
60714         this.eventStore.each(function(ev){
60715             
60716             this.renderEvent(ev);
60717              
60718              
60719         }, this);
60720         this.view.layout();
60721         
60722     },
60723     
60724     onEventEnter: function (e, el,event,d) {
60725         this.fireEvent('evententer', this, el, event);
60726     },
60727     
60728     onEventLeave: function (e, el,event,d) {
60729         this.fireEvent('eventleave', this, el, event);
60730     },
60731     
60732     onEventClick: function (e, el,event,d) {
60733         this.fireEvent('eventclick', this, el, event);
60734     },
60735     
60736     onMonthChange: function () {
60737         this.store.load();
60738     },
60739     
60740     onLoad: function () {
60741         
60742         //Roo.log('calendar onload');
60743 //         
60744         if(this.eventStore.getCount() > 0){
60745             
60746            
60747             
60748             this.eventStore.each(function(d){
60749                 
60750                 
60751                 // FIXME..
60752                 var add =   d.data;
60753                 if (typeof(add.end_dt) == 'undefined')  {
60754                     Roo.log("Missing End time in calendar data: ");
60755                     Roo.log(d);
60756                     return;
60757                 }
60758                 if (typeof(add.start_dt) == 'undefined')  {
60759                     Roo.log("Missing Start time in calendar data: ");
60760                     Roo.log(d);
60761                     return;
60762                 }
60763                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60764                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60765                 add.id = add.id || d.id;
60766                 add.title = add.title || '??';
60767                 
60768                 this.addItem(d);
60769                 
60770              
60771             },this);
60772         }
60773         
60774         this.renderEvents();
60775     }
60776     
60777
60778 });
60779 /*
60780  grid : {
60781                 xtype: 'Grid',
60782                 xns: Roo.grid,
60783                 listeners : {
60784                     render : function ()
60785                     {
60786                         _this.grid = this;
60787                         
60788                         if (!this.view.el.hasClass('course-timesheet')) {
60789                             this.view.el.addClass('course-timesheet');
60790                         }
60791                         if (this.tsStyle) {
60792                             this.ds.load({});
60793                             return; 
60794                         }
60795                         Roo.log('width');
60796                         Roo.log(_this.grid.view.el.getWidth());
60797                         
60798                         
60799                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60800                             '.course-timesheet .x-grid-row' : {
60801                                 height: '80px'
60802                             },
60803                             '.x-grid-row td' : {
60804                                 'vertical-align' : 0
60805                             },
60806                             '.course-edit-link' : {
60807                                 'color' : 'blue',
60808                                 'text-overflow' : 'ellipsis',
60809                                 'overflow' : 'hidden',
60810                                 'white-space' : 'nowrap',
60811                                 'cursor' : 'pointer'
60812                             },
60813                             '.sub-link' : {
60814                                 'color' : 'green'
60815                             },
60816                             '.de-act-sup-link' : {
60817                                 'color' : 'purple',
60818                                 'text-decoration' : 'line-through'
60819                             },
60820                             '.de-act-link' : {
60821                                 'color' : 'red',
60822                                 'text-decoration' : 'line-through'
60823                             },
60824                             '.course-timesheet .course-highlight' : {
60825                                 'border-top-style': 'dashed !important',
60826                                 'border-bottom-bottom': 'dashed !important'
60827                             },
60828                             '.course-timesheet .course-item' : {
60829                                 'font-family'   : 'tahoma, arial, helvetica',
60830                                 'font-size'     : '11px',
60831                                 'overflow'      : 'hidden',
60832                                 'padding-left'  : '10px',
60833                                 'padding-right' : '10px',
60834                                 'padding-top' : '10px' 
60835                             }
60836                             
60837                         }, Roo.id());
60838                                 this.ds.load({});
60839                     }
60840                 },
60841                 autoWidth : true,
60842                 monitorWindowResize : false,
60843                 cellrenderer : function(v,x,r)
60844                 {
60845                     return v;
60846                 },
60847                 sm : {
60848                     xtype: 'CellSelectionModel',
60849                     xns: Roo.grid
60850                 },
60851                 dataSource : {
60852                     xtype: 'Store',
60853                     xns: Roo.data,
60854                     listeners : {
60855                         beforeload : function (_self, options)
60856                         {
60857                             options.params = options.params || {};
60858                             options.params._month = _this.monthField.getValue();
60859                             options.params.limit = 9999;
60860                             options.params['sort'] = 'when_dt';    
60861                             options.params['dir'] = 'ASC';    
60862                             this.proxy.loadResponse = this.loadResponse;
60863                             Roo.log("load?");
60864                             //this.addColumns();
60865                         },
60866                         load : function (_self, records, options)
60867                         {
60868                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60869                                 // if you click on the translation.. you can edit it...
60870                                 var el = Roo.get(this);
60871                                 var id = el.dom.getAttribute('data-id');
60872                                 var d = el.dom.getAttribute('data-date');
60873                                 var t = el.dom.getAttribute('data-time');
60874                                 //var id = this.child('span').dom.textContent;
60875                                 
60876                                 //Roo.log(this);
60877                                 Pman.Dialog.CourseCalendar.show({
60878                                     id : id,
60879                                     when_d : d,
60880                                     when_t : t,
60881                                     productitem_active : id ? 1 : 0
60882                                 }, function() {
60883                                     _this.grid.ds.load({});
60884                                 });
60885                            
60886                            });
60887                            
60888                            _this.panel.fireEvent('resize', [ '', '' ]);
60889                         }
60890                     },
60891                     loadResponse : function(o, success, response){
60892                             // this is overridden on before load..
60893                             
60894                             Roo.log("our code?");       
60895                             //Roo.log(success);
60896                             //Roo.log(response)
60897                             delete this.activeRequest;
60898                             if(!success){
60899                                 this.fireEvent("loadexception", this, o, response);
60900                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60901                                 return;
60902                             }
60903                             var result;
60904                             try {
60905                                 result = o.reader.read(response);
60906                             }catch(e){
60907                                 Roo.log("load exception?");
60908                                 this.fireEvent("loadexception", this, o, response, e);
60909                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60910                                 return;
60911                             }
60912                             Roo.log("ready...");        
60913                             // loop through result.records;
60914                             // and set this.tdate[date] = [] << array of records..
60915                             _this.tdata  = {};
60916                             Roo.each(result.records, function(r){
60917                                 //Roo.log(r.data);
60918                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60919                                     _this.tdata[r.data.when_dt.format('j')] = [];
60920                                 }
60921                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60922                             });
60923                             
60924                             //Roo.log(_this.tdata);
60925                             
60926                             result.records = [];
60927                             result.totalRecords = 6;
60928                     
60929                             // let's generate some duumy records for the rows.
60930                             //var st = _this.dateField.getValue();
60931                             
60932                             // work out monday..
60933                             //st = st.add(Date.DAY, -1 * st.format('w'));
60934                             
60935                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60936                             
60937                             var firstOfMonth = date.getFirstDayOfMonth();
60938                             var days = date.getDaysInMonth();
60939                             var d = 1;
60940                             var firstAdded = false;
60941                             for (var i = 0; i < result.totalRecords ; i++) {
60942                                 //var d= st.add(Date.DAY, i);
60943                                 var row = {};
60944                                 var added = 0;
60945                                 for(var w = 0 ; w < 7 ; w++){
60946                                     if(!firstAdded && firstOfMonth != w){
60947                                         continue;
60948                                     }
60949                                     if(d > days){
60950                                         continue;
60951                                     }
60952                                     firstAdded = true;
60953                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60954                                     row['weekday'+w] = String.format(
60955                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60956                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60957                                                     d,
60958                                                     date.format('Y-m-')+dd
60959                                                 );
60960                                     added++;
60961                                     if(typeof(_this.tdata[d]) != 'undefined'){
60962                                         Roo.each(_this.tdata[d], function(r){
60963                                             var is_sub = '';
60964                                             var deactive = '';
60965                                             var id = r.id;
60966                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60967                                             if(r.parent_id*1>0){
60968                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60969                                                 id = r.parent_id;
60970                                             }
60971                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60972                                                 deactive = 'de-act-link';
60973                                             }
60974                                             
60975                                             row['weekday'+w] += String.format(
60976                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60977                                                     id, //0
60978                                                     r.product_id_name, //1
60979                                                     r.when_dt.format('h:ia'), //2
60980                                                     is_sub, //3
60981                                                     deactive, //4
60982                                                     desc // 5
60983                                             );
60984                                         });
60985                                     }
60986                                     d++;
60987                                 }
60988                                 
60989                                 // only do this if something added..
60990                                 if(added > 0){ 
60991                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60992                                 }
60993                                 
60994                                 
60995                                 // push it twice. (second one with an hour..
60996                                 
60997                             }
60998                             //Roo.log(result);
60999                             this.fireEvent("load", this, o, o.request.arg);
61000                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61001                         },
61002                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61003                     proxy : {
61004                         xtype: 'HttpProxy',
61005                         xns: Roo.data,
61006                         method : 'GET',
61007                         url : baseURL + '/Roo/Shop_course.php'
61008                     },
61009                     reader : {
61010                         xtype: 'JsonReader',
61011                         xns: Roo.data,
61012                         id : 'id',
61013                         fields : [
61014                             {
61015                                 'name': 'id',
61016                                 'type': 'int'
61017                             },
61018                             {
61019                                 'name': 'when_dt',
61020                                 'type': 'string'
61021                             },
61022                             {
61023                                 'name': 'end_dt',
61024                                 'type': 'string'
61025                             },
61026                             {
61027                                 'name': 'parent_id',
61028                                 'type': 'int'
61029                             },
61030                             {
61031                                 'name': 'product_id',
61032                                 'type': 'int'
61033                             },
61034                             {
61035                                 'name': 'productitem_id',
61036                                 'type': 'int'
61037                             },
61038                             {
61039                                 'name': 'guid',
61040                                 'type': 'int'
61041                             }
61042                         ]
61043                     }
61044                 },
61045                 toolbar : {
61046                     xtype: 'Toolbar',
61047                     xns: Roo,
61048                     items : [
61049                         {
61050                             xtype: 'Button',
61051                             xns: Roo.Toolbar,
61052                             listeners : {
61053                                 click : function (_self, e)
61054                                 {
61055                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61056                                     sd.setMonth(sd.getMonth()-1);
61057                                     _this.monthField.setValue(sd.format('Y-m-d'));
61058                                     _this.grid.ds.load({});
61059                                 }
61060                             },
61061                             text : "Back"
61062                         },
61063                         {
61064                             xtype: 'Separator',
61065                             xns: Roo.Toolbar
61066                         },
61067                         {
61068                             xtype: 'MonthField',
61069                             xns: Roo.form,
61070                             listeners : {
61071                                 render : function (_self)
61072                                 {
61073                                     _this.monthField = _self;
61074                                    // _this.monthField.set  today
61075                                 },
61076                                 select : function (combo, date)
61077                                 {
61078                                     _this.grid.ds.load({});
61079                                 }
61080                             },
61081                             value : (function() { return new Date(); })()
61082                         },
61083                         {
61084                             xtype: 'Separator',
61085                             xns: Roo.Toolbar
61086                         },
61087                         {
61088                             xtype: 'TextItem',
61089                             xns: Roo.Toolbar,
61090                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61091                         },
61092                         {
61093                             xtype: 'Fill',
61094                             xns: Roo.Toolbar
61095                         },
61096                         {
61097                             xtype: 'Button',
61098                             xns: Roo.Toolbar,
61099                             listeners : {
61100                                 click : function (_self, e)
61101                                 {
61102                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61103                                     sd.setMonth(sd.getMonth()+1);
61104                                     _this.monthField.setValue(sd.format('Y-m-d'));
61105                                     _this.grid.ds.load({});
61106                                 }
61107                             },
61108                             text : "Next"
61109                         }
61110                     ]
61111                 },
61112                  
61113             }
61114         };
61115         
61116         *//*
61117  * Based on:
61118  * Ext JS Library 1.1.1
61119  * Copyright(c) 2006-2007, Ext JS, LLC.
61120  *
61121  * Originally Released Under LGPL - original licence link has changed is not relivant.
61122  *
61123  * Fork - LGPL
61124  * <script type="text/javascript">
61125  */
61126  
61127 /**
61128  * @class Roo.LoadMask
61129  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61130  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61131  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61132  * element's UpdateManager load indicator and will be destroyed after the initial load.
61133  * @constructor
61134  * Create a new LoadMask
61135  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61136  * @param {Object} config The config object
61137  */
61138 Roo.LoadMask = function(el, config){
61139     this.el = Roo.get(el);
61140     Roo.apply(this, config);
61141     if(this.store){
61142         this.store.on('beforeload', this.onBeforeLoad, this);
61143         this.store.on('load', this.onLoad, this);
61144         this.store.on('loadexception', this.onLoadException, this);
61145         this.removeMask = false;
61146     }else{
61147         var um = this.el.getUpdateManager();
61148         um.showLoadIndicator = false; // disable the default indicator
61149         um.on('beforeupdate', this.onBeforeLoad, this);
61150         um.on('update', this.onLoad, this);
61151         um.on('failure', this.onLoad, this);
61152         this.removeMask = true;
61153     }
61154 };
61155
61156 Roo.LoadMask.prototype = {
61157     /**
61158      * @cfg {Boolean} removeMask
61159      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61160      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61161      */
61162     /**
61163      * @cfg {String} msg
61164      * The text to display in a centered loading message box (defaults to 'Loading...')
61165      */
61166     msg : 'Loading...',
61167     /**
61168      * @cfg {String} msgCls
61169      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61170      */
61171     msgCls : 'x-mask-loading',
61172
61173     /**
61174      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61175      * @type Boolean
61176      */
61177     disabled: false,
61178
61179     /**
61180      * Disables the mask to prevent it from being displayed
61181      */
61182     disable : function(){
61183        this.disabled = true;
61184     },
61185
61186     /**
61187      * Enables the mask so that it can be displayed
61188      */
61189     enable : function(){
61190         this.disabled = false;
61191     },
61192     
61193     onLoadException : function()
61194     {
61195         Roo.log(arguments);
61196         
61197         if (typeof(arguments[3]) != 'undefined') {
61198             Roo.MessageBox.alert("Error loading",arguments[3]);
61199         } 
61200         /*
61201         try {
61202             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61203                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61204             }   
61205         } catch(e) {
61206             
61207         }
61208         */
61209     
61210         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61211     },
61212     // private
61213     onLoad : function()
61214     {
61215         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61216     },
61217
61218     // private
61219     onBeforeLoad : function(){
61220         if(!this.disabled){
61221             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61222         }
61223     },
61224
61225     // private
61226     destroy : function(){
61227         if(this.store){
61228             this.store.un('beforeload', this.onBeforeLoad, this);
61229             this.store.un('load', this.onLoad, this);
61230             this.store.un('loadexception', this.onLoadException, this);
61231         }else{
61232             var um = this.el.getUpdateManager();
61233             um.un('beforeupdate', this.onBeforeLoad, this);
61234             um.un('update', this.onLoad, this);
61235             um.un('failure', this.onLoad, this);
61236         }
61237     }
61238 };/*
61239  * Based on:
61240  * Ext JS Library 1.1.1
61241  * Copyright(c) 2006-2007, Ext JS, LLC.
61242  *
61243  * Originally Released Under LGPL - original licence link has changed is not relivant.
61244  *
61245  * Fork - LGPL
61246  * <script type="text/javascript">
61247  */
61248
61249
61250 /**
61251  * @class Roo.XTemplate
61252  * @extends Roo.Template
61253  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61254 <pre><code>
61255 var t = new Roo.XTemplate(
61256         '&lt;select name="{name}"&gt;',
61257                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61258         '&lt;/select&gt;'
61259 );
61260  
61261 // then append, applying the master template values
61262  </code></pre>
61263  *
61264  * Supported features:
61265  *
61266  *  Tags:
61267
61268 <pre><code>
61269       {a_variable} - output encoded.
61270       {a_variable.format:("Y-m-d")} - call a method on the variable
61271       {a_variable:raw} - unencoded output
61272       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61273       {a_variable:this.method_on_template(...)} - call a method on the template object.
61274  
61275 </code></pre>
61276  *  The tpl tag:
61277 <pre><code>
61278         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61279         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61280         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61281         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61282   
61283         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61284         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61285 </code></pre>
61286  *      
61287  */
61288 Roo.XTemplate = function()
61289 {
61290     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61291     if (this.html) {
61292         this.compile();
61293     }
61294 };
61295
61296
61297 Roo.extend(Roo.XTemplate, Roo.Template, {
61298
61299     /**
61300      * The various sub templates
61301      */
61302     tpls : false,
61303     /**
61304      *
61305      * basic tag replacing syntax
61306      * WORD:WORD()
61307      *
61308      * // you can fake an object call by doing this
61309      *  x.t:(test,tesT) 
61310      * 
61311      */
61312     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61313
61314     /**
61315      * compile the template
61316      *
61317      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61318      *
61319      */
61320     compile: function()
61321     {
61322         var s = this.html;
61323      
61324         s = ['<tpl>', s, '</tpl>'].join('');
61325     
61326         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61327             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61328             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61329             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61330             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61331             m,
61332             id     = 0,
61333             tpls   = [];
61334     
61335         while(true == !!(m = s.match(re))){
61336             var forMatch   = m[0].match(nameRe),
61337                 ifMatch   = m[0].match(ifRe),
61338                 execMatch   = m[0].match(execRe),
61339                 namedMatch   = m[0].match(namedRe),
61340                 
61341                 exp  = null, 
61342                 fn   = null,
61343                 exec = null,
61344                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61345                 
61346             if (ifMatch) {
61347                 // if - puts fn into test..
61348                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61349                 if(exp){
61350                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61351                 }
61352             }
61353             
61354             if (execMatch) {
61355                 // exec - calls a function... returns empty if true is  returned.
61356                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61357                 if(exp){
61358                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61359                 }
61360             }
61361             
61362             
61363             if (name) {
61364                 // for = 
61365                 switch(name){
61366                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61367                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61368                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61369                 }
61370             }
61371             var uid = namedMatch ? namedMatch[1] : id;
61372             
61373             
61374             tpls.push({
61375                 id:     namedMatch ? namedMatch[1] : id,
61376                 target: name,
61377                 exec:   exec,
61378                 test:   fn,
61379                 body:   m[1] || ''
61380             });
61381             if (namedMatch) {
61382                 s = s.replace(m[0], '');
61383             } else { 
61384                 s = s.replace(m[0], '{xtpl'+ id + '}');
61385             }
61386             ++id;
61387         }
61388         this.tpls = [];
61389         for(var i = tpls.length-1; i >= 0; --i){
61390             this.compileTpl(tpls[i]);
61391             this.tpls[tpls[i].id] = tpls[i];
61392         }
61393         this.master = tpls[tpls.length-1];
61394         return this;
61395     },
61396     /**
61397      * same as applyTemplate, except it's done to one of the subTemplates
61398      * when using named templates, you can do:
61399      *
61400      * var str = pl.applySubTemplate('your-name', values);
61401      *
61402      * 
61403      * @param {Number} id of the template
61404      * @param {Object} values to apply to template
61405      * @param {Object} parent (normaly the instance of this object)
61406      */
61407     applySubTemplate : function(id, values, parent)
61408     {
61409         
61410         
61411         var t = this.tpls[id];
61412         
61413         
61414         try { 
61415             if(t.test && !t.test.call(this, values, parent)){
61416                 return '';
61417             }
61418         } catch(e) {
61419             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61420             Roo.log(e.toString());
61421             Roo.log(t.test);
61422             return ''
61423         }
61424         try { 
61425             
61426             if(t.exec && t.exec.call(this, values, parent)){
61427                 return '';
61428             }
61429         } catch(e) {
61430             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61431             Roo.log(e.toString());
61432             Roo.log(t.exec);
61433             return ''
61434         }
61435         try {
61436             var vs = t.target ? t.target.call(this, values, parent) : values;
61437             parent = t.target ? values : parent;
61438             if(t.target && vs instanceof Array){
61439                 var buf = [];
61440                 for(var i = 0, len = vs.length; i < len; i++){
61441                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61442                 }
61443                 return buf.join('');
61444             }
61445             return t.compiled.call(this, vs, parent);
61446         } catch (e) {
61447             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61448             Roo.log(e.toString());
61449             Roo.log(t.compiled);
61450             return '';
61451         }
61452     },
61453
61454     compileTpl : function(tpl)
61455     {
61456         var fm = Roo.util.Format;
61457         var useF = this.disableFormats !== true;
61458         var sep = Roo.isGecko ? "+" : ",";
61459         var undef = function(str) {
61460             Roo.log("Property not found :"  + str);
61461             return '';
61462         };
61463         
61464         var fn = function(m, name, format, args)
61465         {
61466             //Roo.log(arguments);
61467             args = args ? args.replace(/\\'/g,"'") : args;
61468             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61469             if (typeof(format) == 'undefined') {
61470                 format= 'htmlEncode';
61471             }
61472             if (format == 'raw' ) {
61473                 format = false;
61474             }
61475             
61476             if(name.substr(0, 4) == 'xtpl'){
61477                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61478             }
61479             
61480             // build an array of options to determine if value is undefined..
61481             
61482             // basically get 'xxxx.yyyy' then do
61483             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61484             //    (function () { Roo.log("Property not found"); return ''; })() :
61485             //    ......
61486             
61487             var udef_ar = [];
61488             var lookfor = '';
61489             Roo.each(name.split('.'), function(st) {
61490                 lookfor += (lookfor.length ? '.': '') + st;
61491                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61492             });
61493             
61494             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61495             
61496             
61497             if(format && useF){
61498                 
61499                 args = args ? ',' + args : "";
61500                  
61501                 if(format.substr(0, 5) != "this."){
61502                     format = "fm." + format + '(';
61503                 }else{
61504                     format = 'this.call("'+ format.substr(5) + '", ';
61505                     args = ", values";
61506                 }
61507                 
61508                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61509             }
61510              
61511             if (args.length) {
61512                 // called with xxyx.yuu:(test,test)
61513                 // change to ()
61514                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61515             }
61516             // raw.. - :raw modifier..
61517             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61518             
61519         };
61520         var body;
61521         // branched to use + in gecko and [].join() in others
61522         if(Roo.isGecko){
61523             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61524                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61525                     "';};};";
61526         }else{
61527             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61528             body.push(tpl.body.replace(/(\r\n|\n)/g,
61529                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61530             body.push("'].join('');};};");
61531             body = body.join('');
61532         }
61533         
61534         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61535        
61536         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61537         eval(body);
61538         
61539         return this;
61540     },
61541
61542     applyTemplate : function(values){
61543         return this.master.compiled.call(this, values, {});
61544         //var s = this.subs;
61545     },
61546
61547     apply : function(){
61548         return this.applyTemplate.apply(this, arguments);
61549     }
61550
61551  });
61552
61553 Roo.XTemplate.from = function(el){
61554     el = Roo.getDom(el);
61555     return new Roo.XTemplate(el.value || el.innerHTML);
61556 };