merge changes for seperator change
[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' for forms you probably want to set this to fieldEl 
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      * @cfg {String} seperator    The value seperator normally ',' 
42323      */
42324     seperator : ',',
42325     
42326     // private the array of items that are displayed..
42327     items  : false,
42328     // private - the hidden field el.
42329     hiddenEl : false,
42330     // private - the filed el..
42331     el : false,
42332     
42333     //validateValue : function() { return true; }, // all values are ok!
42334     //onAddClick: function() { },
42335     
42336     onRender : function(ct, position) 
42337     {
42338         
42339         // create the standard hidden element
42340         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42341         
42342         
42343         // give fake names to child combo;
42344         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42345         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42346         
42347         this.combo = Roo.factory(this.combo, Roo.form);
42348         this.combo.onRender(ct, position);
42349         if (typeof(this.combo.width) != 'undefined') {
42350             this.combo.onResize(this.combo.width,0);
42351         }
42352         
42353         this.combo.initEvents();
42354         
42355         // assigned so form know we need to do this..
42356         this.store          = this.combo.store;
42357         this.valueField     = this.combo.valueField;
42358         this.displayField   = this.combo.displayField ;
42359         
42360         
42361         this.combo.wrap.addClass('x-cbarray-grp');
42362         
42363         var cbwrap = this.combo.wrap.createChild(
42364             {tag: 'div', cls: 'x-cbarray-cb'},
42365             this.combo.el.dom
42366         );
42367         
42368              
42369         this.hiddenEl = this.combo.wrap.createChild({
42370             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42371         });
42372         this.el = this.combo.wrap.createChild({
42373             tag: 'input',  type:'hidden' , name: this.name, value : ''
42374         });
42375          //   this.el.dom.removeAttribute("name");
42376         
42377         
42378         this.outerWrap = this.combo.wrap;
42379         this.wrap = cbwrap;
42380         
42381         this.outerWrap.setWidth(this.width);
42382         this.outerWrap.dom.removeChild(this.el.dom);
42383         
42384         this.wrap.dom.appendChild(this.el.dom);
42385         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42386         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42387         
42388         this.combo.trigger.setStyle('position','relative');
42389         this.combo.trigger.setStyle('left', '0px');
42390         this.combo.trigger.setStyle('top', '2px');
42391         
42392         this.combo.el.setStyle('vertical-align', 'text-bottom');
42393         
42394         //this.trigger.setStyle('vertical-align', 'top');
42395         
42396         // this should use the code from combo really... on('add' ....)
42397         if (this.adder) {
42398             
42399         
42400             this.adder = this.outerWrap.createChild(
42401                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42402             var _t = this;
42403             this.adder.on('click', function(e) {
42404                 _t.fireEvent('adderclick', this, e);
42405             }, _t);
42406         }
42407         //var _t = this;
42408         //this.adder.on('click', this.onAddClick, _t);
42409         
42410         
42411         this.combo.on('select', function(cb, rec, ix) {
42412             this.addItem(rec.data);
42413             
42414             cb.setValue('');
42415             cb.el.dom.value = '';
42416             //cb.lastData = rec.data;
42417             // add to list
42418             
42419         }, this);
42420         
42421         
42422     },
42423     
42424     
42425     getName: function()
42426     {
42427         // returns hidden if it's set..
42428         if (!this.rendered) {return ''};
42429         return  this.hiddenName ? this.hiddenName : this.name;
42430         
42431     },
42432     
42433     
42434     onResize: function(w, h){
42435         
42436         return;
42437         // not sure if this is needed..
42438         //this.combo.onResize(w,h);
42439         
42440         if(typeof w != 'number'){
42441             // we do not handle it!?!?
42442             return;
42443         }
42444         var tw = this.combo.trigger.getWidth();
42445         tw += this.addicon ? this.addicon.getWidth() : 0;
42446         tw += this.editicon ? this.editicon.getWidth() : 0;
42447         var x = w - tw;
42448         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42449             
42450         this.combo.trigger.setStyle('left', '0px');
42451         
42452         if(this.list && this.listWidth === undefined){
42453             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42454             this.list.setWidth(lw);
42455             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42456         }
42457         
42458     
42459         
42460     },
42461     
42462     addItem: function(rec)
42463     {
42464         var valueField = this.combo.valueField;
42465         var displayField = this.combo.displayField;
42466         
42467         if (this.items.indexOfKey(rec[valueField]) > -1) {
42468             //console.log("GOT " + rec.data.id);
42469             return;
42470         }
42471         
42472         var x = new Roo.form.ComboBoxArray.Item({
42473             //id : rec[this.idField],
42474             data : rec,
42475             displayField : displayField ,
42476             tipField : displayField ,
42477             cb : this
42478         });
42479         // use the 
42480         this.items.add(rec[valueField],x);
42481         // add it before the element..
42482         this.updateHiddenEl();
42483         x.render(this.outerWrap, this.wrap.dom);
42484         // add the image handler..
42485     },
42486     
42487     updateHiddenEl : function()
42488     {
42489         this.validate();
42490         if (!this.hiddenEl) {
42491             return;
42492         }
42493         var ar = [];
42494         var idField = this.combo.valueField;
42495         
42496         this.items.each(function(f) {
42497             ar.push(f.data[idField]);
42498         });
42499         this.hiddenEl.dom.value = ar.join(this.seperator);
42500         this.validate();
42501     },
42502     
42503     reset : function()
42504     {
42505         this.items.clear();
42506         
42507         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42508            el.remove();
42509         });
42510         
42511         this.el.dom.value = '';
42512         if (this.hiddenEl) {
42513             this.hiddenEl.dom.value = '';
42514         }
42515         
42516     },
42517     getValue: function()
42518     {
42519         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42520     },
42521     setValue: function(v) // not a valid action - must use addItems..
42522     {
42523         
42524         this.reset();
42525          
42526         if (this.store.isLocal && (typeof(v) == 'string')) {
42527             // then we can use the store to find the values..
42528             // comma seperated at present.. this needs to allow JSON based encoding..
42529             this.hiddenEl.value  = v;
42530             var v_ar = [];
42531             Roo.each(v.split(this.seperator), function(k) {
42532                 Roo.log("CHECK " + this.valueField + ',' + k);
42533                 var li = this.store.query(this.valueField, k);
42534                 if (!li.length) {
42535                     return;
42536                 }
42537                 var add = {};
42538                 add[this.valueField] = k;
42539                 add[this.displayField] = li.item(0).data[this.displayField];
42540                 
42541                 this.addItem(add);
42542             }, this) 
42543              
42544         }
42545         if (typeof(v) == 'object' ) {
42546             // then let's assume it's an array of objects..
42547             Roo.each(v, function(l) {
42548                 this.addItem(l);
42549             }, this);
42550              
42551         }
42552         
42553         
42554     },
42555     setFromData: function(v)
42556     {
42557         // this recieves an object, if setValues is called.
42558         this.reset();
42559         this.el.dom.value = v[this.displayField];
42560         this.hiddenEl.dom.value = v[this.valueField];
42561         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42562             return;
42563         }
42564         var kv = v[this.valueField];
42565         var dv = v[this.displayField];
42566         kv = typeof(kv) != 'string' ? '' : kv;
42567         dv = typeof(dv) != 'string' ? '' : dv;
42568         
42569         
42570         var keys = kv.split(this.seperator);
42571         var display = dv.split(this.seperator);
42572         for (var i = 0 ; i < keys.length; i++) {
42573             add = {};
42574             add[this.valueField] = keys[i];
42575             add[this.displayField] = display[i];
42576             this.addItem(add);
42577         }
42578       
42579         
42580     },
42581     
42582     /**
42583      * Validates the combox array value
42584      * @return {Boolean} True if the value is valid, else false
42585      */
42586     validate : function(){
42587         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42588             this.clearInvalid();
42589             return true;
42590         }
42591         return false;
42592     },
42593     
42594     validateValue : function(value){
42595         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42596         
42597     },
42598     
42599     /*@
42600      * overide
42601      * 
42602      */
42603     isDirty : function() {
42604         if(this.disabled) {
42605             return false;
42606         }
42607         
42608         try {
42609             var d = Roo.decode(String(this.originalValue));
42610         } catch (e) {
42611             return String(this.getValue()) !== String(this.originalValue);
42612         }
42613         
42614         var originalValue = [];
42615         
42616         for (var i = 0; i < d.length; i++){
42617             originalValue.push(d[i][this.valueField]);
42618         }
42619         
42620         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42621         
42622     }
42623     
42624 });
42625
42626
42627
42628 /**
42629  * @class Roo.form.ComboBoxArray.Item
42630  * @extends Roo.BoxComponent
42631  * A selected item in the list
42632  *  Fred [x]  Brian [x]  [Pick another |v]
42633  * 
42634  * @constructor
42635  * Create a new item.
42636  * @param {Object} config Configuration options
42637  */
42638  
42639 Roo.form.ComboBoxArray.Item = function(config) {
42640     config.id = Roo.id();
42641     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42642 }
42643
42644 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42645     data : {},
42646     cb: false,
42647     displayField : false,
42648     tipField : false,
42649     
42650     
42651     defaultAutoCreate : {
42652         tag: 'div',
42653         cls: 'x-cbarray-item',
42654         cn : [ 
42655             { tag: 'div' },
42656             {
42657                 tag: 'img',
42658                 width:16,
42659                 height : 16,
42660                 src : Roo.BLANK_IMAGE_URL ,
42661                 align: 'center'
42662             }
42663         ]
42664         
42665     },
42666     
42667  
42668     onRender : function(ct, position)
42669     {
42670         Roo.form.Field.superclass.onRender.call(this, ct, position);
42671         
42672         if(!this.el){
42673             var cfg = this.getAutoCreate();
42674             this.el = ct.createChild(cfg, position);
42675         }
42676         
42677         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42678         
42679         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42680             this.cb.renderer(this.data) :
42681             String.format('{0}',this.data[this.displayField]);
42682         
42683             
42684         this.el.child('div').dom.setAttribute('qtip',
42685                         String.format('{0}',this.data[this.tipField])
42686         );
42687         
42688         this.el.child('img').on('click', this.remove, this);
42689         
42690     },
42691    
42692     remove : function()
42693     {
42694         if(this.cb.disabled){
42695             return;
42696         }
42697         
42698         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42699             this.cb.items.remove(this);
42700             this.el.child('img').un('click', this.remove, this);
42701             this.el.remove();
42702             this.cb.updateHiddenEl();
42703
42704             this.cb.fireEvent('remove', this.cb, this);
42705         }
42706         
42707     }
42708 });/*
42709  * RooJS Library 1.1.1
42710  * Copyright(c) 2008-2011  Alan Knowles
42711  *
42712  * License - LGPL
42713  */
42714  
42715
42716 /**
42717  * @class Roo.form.ComboNested
42718  * @extends Roo.form.ComboBox
42719  * A combobox for that allows selection of nested items in a list,
42720  * eg.
42721  *
42722  *  Book
42723  *    -> red
42724  *    -> green
42725  *  Table
42726  *    -> square
42727  *      ->red
42728  *      ->green
42729  *    -> rectangle
42730  *      ->green
42731  *      
42732  * 
42733  * @constructor
42734  * Create a new ComboNested
42735  * @param {Object} config Configuration options
42736  */
42737 Roo.form.ComboNested = function(config){
42738     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42739     // should verify some data...
42740     // like
42741     // hiddenName = required..
42742     // displayField = required
42743     // valudField == required
42744     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42745     var _t = this;
42746     Roo.each(req, function(e) {
42747         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42748             throw "Roo.form.ComboNested : missing value for: " + e;
42749         }
42750     });
42751      
42752     
42753 };
42754
42755 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42756    
42757     /*
42758      * @config {Number} max Number of columns to show
42759      */
42760     
42761     maxColumns : 3,
42762    
42763     list : null, // the outermost div..
42764     innerLists : null, // the
42765     views : null,
42766     stores : null,
42767     // private
42768     loadingChildren : false,
42769     
42770     onRender : function(ct, position)
42771     {
42772         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42773         
42774         if(this.hiddenName){
42775             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42776                     'before', true);
42777             this.hiddenField.value =
42778                 this.hiddenValue !== undefined ? this.hiddenValue :
42779                 this.value !== undefined ? this.value : '';
42780
42781             // prevent input submission
42782             this.el.dom.removeAttribute('name');
42783              
42784              
42785         }
42786         
42787         if(Roo.isGecko){
42788             this.el.dom.setAttribute('autocomplete', 'off');
42789         }
42790
42791         var cls = 'x-combo-list';
42792
42793         this.list = new Roo.Layer({
42794             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42795         });
42796
42797         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42798         this.list.setWidth(lw);
42799         this.list.swallowEvent('mousewheel');
42800         this.assetHeight = 0;
42801
42802         if(this.title){
42803             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42804             this.assetHeight += this.header.getHeight();
42805         }
42806         this.innerLists = [];
42807         this.views = [];
42808         this.stores = [];
42809         for (var i =0 ; i < this.maxColumns; i++) {
42810             this.onRenderList( cls, i);
42811         }
42812         
42813         // always needs footer, as we are going to have an 'OK' button.
42814         this.footer = this.list.createChild({cls:cls+'-ft'});
42815         this.pageTb = new Roo.Toolbar(this.footer);  
42816         var _this = this;
42817         this.pageTb.add(  {
42818             
42819             text: 'Done',
42820             handler: function()
42821             {
42822                 _this.collapse();
42823             }
42824         });
42825         
42826         if ( this.allowBlank && !this.disableClear) {
42827             
42828             this.pageTb.add(new Roo.Toolbar.Fill(), {
42829                 cls: 'x-btn-icon x-btn-clear',
42830                 text: '&#160;',
42831                 handler: function()
42832                 {
42833                     _this.collapse();
42834                     _this.clearValue();
42835                     _this.onSelect(false, -1);
42836                 }
42837             });
42838         }
42839         if (this.footer) {
42840             this.assetHeight += this.footer.getHeight();
42841         }
42842         
42843     },
42844     onRenderList : function (  cls, i)
42845     {
42846         
42847         var lw = Math.floor(
42848                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42849         );
42850         
42851         this.list.setWidth(lw); // default to '1'
42852
42853         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42854         //il.on('mouseover', this.onViewOver, this, { list:  i });
42855         //il.on('mousemove', this.onViewMove, this, { list:  i });
42856         il.setWidth(lw);
42857         il.setStyle({ 'overflow-x' : 'hidden'});
42858
42859         if(!this.tpl){
42860             this.tpl = new Roo.Template({
42861                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42862                 isEmpty: function (value, allValues) {
42863                     //Roo.log(value);
42864                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42865                     return dl ? 'has-children' : 'no-children'
42866                 }
42867             });
42868         }
42869         
42870         var store  = this.store;
42871         if (i > 0) {
42872             store  = new Roo.data.SimpleStore({
42873                 //fields : this.store.reader.meta.fields,
42874                 reader : this.store.reader,
42875                 data : [ ]
42876             });
42877         }
42878         this.stores[i]  = store;
42879                   
42880         var view = this.views[i] = new Roo.View(
42881             il,
42882             this.tpl,
42883             {
42884                 singleSelect:true,
42885                 store: store,
42886                 selectedClass: this.selectedClass
42887             }
42888         );
42889         view.getEl().setWidth(lw);
42890         view.getEl().setStyle({
42891             position: i < 1 ? 'relative' : 'absolute',
42892             top: 0,
42893             left: (i * lw ) + 'px',
42894             display : i > 0 ? 'none' : 'block'
42895         });
42896         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42897         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42898         //view.on('click', this.onViewClick, this, { list : i });
42899
42900         store.on('beforeload', this.onBeforeLoad, this);
42901         store.on('load',  this.onLoad, this, { list  : i});
42902         store.on('loadexception', this.onLoadException, this);
42903
42904         // hide the other vies..
42905         
42906         
42907         
42908     },
42909       
42910     restrictHeight : function()
42911     {
42912         var mh = 0;
42913         Roo.each(this.innerLists, function(il,i) {
42914             var el = this.views[i].getEl();
42915             el.dom.style.height = '';
42916             var inner = el.dom;
42917             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42918             // only adjust heights on other ones..
42919             mh = Math.max(h, mh);
42920             if (i < 1) {
42921                 
42922                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42923                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42924                
42925             }
42926             
42927             
42928         }, this);
42929         
42930         this.list.beginUpdate();
42931         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42932         this.list.alignTo(this.el, this.listAlign);
42933         this.list.endUpdate();
42934         
42935     },
42936      
42937     
42938     // -- store handlers..
42939     // private
42940     onBeforeLoad : function()
42941     {
42942         if(!this.hasFocus){
42943             return;
42944         }
42945         this.innerLists[0].update(this.loadingText ?
42946                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42947         this.restrictHeight();
42948         this.selectedIndex = -1;
42949     },
42950     // private
42951     onLoad : function(a,b,c,d)
42952     {
42953         if (!this.loadingChildren) {
42954             // then we are loading the top level. - hide the children
42955             for (var i = 1;i < this.views.length; i++) {
42956                 this.views[i].getEl().setStyle({ display : 'none' });
42957             }
42958             var lw = Math.floor(
42959                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42960             );
42961         
42962              this.list.setWidth(lw); // default to '1'
42963
42964             
42965         }
42966         if(!this.hasFocus){
42967             return;
42968         }
42969         
42970         if(this.store.getCount() > 0) {
42971             this.expand();
42972             this.restrictHeight();   
42973         } else {
42974             this.onEmptyResults();
42975         }
42976         
42977         if (!this.loadingChildren) {
42978             this.selectActive();
42979         }
42980         /*
42981         this.stores[1].loadData([]);
42982         this.stores[2].loadData([]);
42983         this.views
42984         */    
42985     
42986         //this.el.focus();
42987     },
42988     
42989     
42990     // private
42991     onLoadException : function()
42992     {
42993         this.collapse();
42994         Roo.log(this.store.reader.jsonData);
42995         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42996             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42997         }
42998         
42999         
43000     },
43001     // no cleaning of leading spaces on blur here.
43002     cleanLeadingSpace : function(e) { },
43003     
43004
43005     onSelectChange : function (view, sels, opts )
43006     {
43007         var ix = view.getSelectedIndexes();
43008          
43009         if (opts.list > this.maxColumns - 2) {
43010             if (view.store.getCount()<  1) {
43011                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43012
43013             } else  {
43014                 if (ix.length) {
43015                     // used to clear ?? but if we are loading unselected 
43016                     this.setFromData(view.store.getAt(ix[0]).data);
43017                 }
43018                 
43019             }
43020             
43021             return;
43022         }
43023         
43024         if (!ix.length) {
43025             // this get's fired when trigger opens..
43026            // this.setFromData({});
43027             var str = this.stores[opts.list+1];
43028             str.data.clear(); // removeall wihtout the fire events..
43029             return;
43030         }
43031         
43032         var rec = view.store.getAt(ix[0]);
43033          
43034         this.setFromData(rec.data);
43035         this.fireEvent('select', this, rec, ix[0]);
43036         
43037         var lw = Math.floor(
43038              (
43039                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43040              ) / this.maxColumns
43041         );
43042         this.loadingChildren = true;
43043         this.stores[opts.list+1].loadDataFromChildren( rec );
43044         this.loadingChildren = false;
43045         var dl = this.stores[opts.list+1]. getTotalCount();
43046         
43047         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43048         
43049         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43050         for (var i = opts.list+2; i < this.views.length;i++) {
43051             this.views[i].getEl().setStyle({ display : 'none' });
43052         }
43053         
43054         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43055         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43056         
43057         if (this.isLoading) {
43058            // this.selectActive(opts.list);
43059         }
43060          
43061     },
43062     
43063     
43064     
43065     
43066     onDoubleClick : function()
43067     {
43068         this.collapse(); //??
43069     },
43070     
43071      
43072     
43073     
43074     
43075     // private
43076     recordToStack : function(store, prop, value, stack)
43077     {
43078         var cstore = new Roo.data.SimpleStore({
43079             //fields : this.store.reader.meta.fields, // we need array reader.. for
43080             reader : this.store.reader,
43081             data : [ ]
43082         });
43083         var _this = this;
43084         var record  = false;
43085         var srec = false;
43086         if(store.getCount() < 1){
43087             return false;
43088         }
43089         store.each(function(r){
43090             if(r.data[prop] == value){
43091                 record = r;
43092             srec = r;
43093                 return false;
43094             }
43095             if (r.data.cn && r.data.cn.length) {
43096                 cstore.loadDataFromChildren( r);
43097                 var cret = _this.recordToStack(cstore, prop, value, stack);
43098                 if (cret !== false) {
43099                     record = cret;
43100                     srec = r;
43101                     return false;
43102                 }
43103             }
43104              
43105             return true;
43106         });
43107         if (record == false) {
43108             return false
43109         }
43110         stack.unshift(srec);
43111         return record;
43112     },
43113     
43114     /*
43115      * find the stack of stores that match our value.
43116      *
43117      * 
43118      */
43119     
43120     selectActive : function ()
43121     {
43122         // if store is not loaded, then we will need to wait for that to happen first.
43123         var stack = [];
43124         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43125         for (var i = 0; i < stack.length; i++ ) {
43126             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43127         }
43128         
43129     }
43130         
43131          
43132     
43133     
43134     
43135     
43136 });/*
43137  * Based on:
43138  * Ext JS Library 1.1.1
43139  * Copyright(c) 2006-2007, Ext JS, LLC.
43140  *
43141  * Originally Released Under LGPL - original licence link has changed is not relivant.
43142  *
43143  * Fork - LGPL
43144  * <script type="text/javascript">
43145  */
43146 /**
43147  * @class Roo.form.Checkbox
43148  * @extends Roo.form.Field
43149  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43150  * @constructor
43151  * Creates a new Checkbox
43152  * @param {Object} config Configuration options
43153  */
43154 Roo.form.Checkbox = function(config){
43155     Roo.form.Checkbox.superclass.constructor.call(this, config);
43156     this.addEvents({
43157         /**
43158          * @event check
43159          * Fires when the checkbox is checked or unchecked.
43160              * @param {Roo.form.Checkbox} this This checkbox
43161              * @param {Boolean} checked The new checked value
43162              */
43163         check : true
43164     });
43165 };
43166
43167 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43168     /**
43169      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43170      */
43171     focusClass : undefined,
43172     /**
43173      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43174      */
43175     fieldClass: "x-form-field",
43176     /**
43177      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43178      */
43179     checked: false,
43180     /**
43181      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43182      * {tag: "input", type: "checkbox", autocomplete: "off"})
43183      */
43184     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43185     /**
43186      * @cfg {String} boxLabel The text that appears beside the checkbox
43187      */
43188     boxLabel : "",
43189     /**
43190      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43191      */  
43192     inputValue : '1',
43193     /**
43194      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43195      */
43196      valueOff: '0', // value when not checked..
43197
43198     actionMode : 'viewEl', 
43199     //
43200     // private
43201     itemCls : 'x-menu-check-item x-form-item',
43202     groupClass : 'x-menu-group-item',
43203     inputType : 'hidden',
43204     
43205     
43206     inSetChecked: false, // check that we are not calling self...
43207     
43208     inputElement: false, // real input element?
43209     basedOn: false, // ????
43210     
43211     isFormField: true, // not sure where this is needed!!!!
43212
43213     onResize : function(){
43214         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43215         if(!this.boxLabel){
43216             this.el.alignTo(this.wrap, 'c-c');
43217         }
43218     },
43219
43220     initEvents : function(){
43221         Roo.form.Checkbox.superclass.initEvents.call(this);
43222         this.el.on("click", this.onClick,  this);
43223         this.el.on("change", this.onClick,  this);
43224     },
43225
43226
43227     getResizeEl : function(){
43228         return this.wrap;
43229     },
43230
43231     getPositionEl : function(){
43232         return this.wrap;
43233     },
43234
43235     // private
43236     onRender : function(ct, position){
43237         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43238         /*
43239         if(this.inputValue !== undefined){
43240             this.el.dom.value = this.inputValue;
43241         }
43242         */
43243         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43244         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43245         var viewEl = this.wrap.createChild({ 
43246             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43247         this.viewEl = viewEl;   
43248         this.wrap.on('click', this.onClick,  this); 
43249         
43250         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43251         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43252         
43253         
43254         
43255         if(this.boxLabel){
43256             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43257         //    viewEl.on('click', this.onClick,  this); 
43258         }
43259         //if(this.checked){
43260             this.setChecked(this.checked);
43261         //}else{
43262             //this.checked = this.el.dom;
43263         //}
43264
43265     },
43266
43267     // private
43268     initValue : Roo.emptyFn,
43269
43270     /**
43271      * Returns the checked state of the checkbox.
43272      * @return {Boolean} True if checked, else false
43273      */
43274     getValue : function(){
43275         if(this.el){
43276             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43277         }
43278         return this.valueOff;
43279         
43280     },
43281
43282         // private
43283     onClick : function(){ 
43284         if (this.disabled) {
43285             return;
43286         }
43287         this.setChecked(!this.checked);
43288
43289         //if(this.el.dom.checked != this.checked){
43290         //    this.setValue(this.el.dom.checked);
43291        // }
43292     },
43293
43294     /**
43295      * Sets the checked state of the checkbox.
43296      * On is always based on a string comparison between inputValue and the param.
43297      * @param {Boolean/String} value - the value to set 
43298      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43299      */
43300     setValue : function(v,suppressEvent){
43301         
43302         
43303         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43304         //if(this.el && this.el.dom){
43305         //    this.el.dom.checked = this.checked;
43306         //    this.el.dom.defaultChecked = this.checked;
43307         //}
43308         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43309         //this.fireEvent("check", this, this.checked);
43310     },
43311     // private..
43312     setChecked : function(state,suppressEvent)
43313     {
43314         if (this.inSetChecked) {
43315             this.checked = state;
43316             return;
43317         }
43318         
43319     
43320         if(this.wrap){
43321             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43322         }
43323         this.checked = state;
43324         if(suppressEvent !== true){
43325             this.fireEvent('check', this, state);
43326         }
43327         this.inSetChecked = true;
43328         this.el.dom.value = state ? this.inputValue : this.valueOff;
43329         this.inSetChecked = false;
43330         
43331     },
43332     // handle setting of hidden value by some other method!!?!?
43333     setFromHidden: function()
43334     {
43335         if(!this.el){
43336             return;
43337         }
43338         //console.log("SET FROM HIDDEN");
43339         //alert('setFrom hidden');
43340         this.setValue(this.el.dom.value);
43341     },
43342     
43343     onDestroy : function()
43344     {
43345         if(this.viewEl){
43346             Roo.get(this.viewEl).remove();
43347         }
43348          
43349         Roo.form.Checkbox.superclass.onDestroy.call(this);
43350     },
43351     
43352     setBoxLabel : function(str)
43353     {
43354         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43355     }
43356
43357 });/*
43358  * Based on:
43359  * Ext JS Library 1.1.1
43360  * Copyright(c) 2006-2007, Ext JS, LLC.
43361  *
43362  * Originally Released Under LGPL - original licence link has changed is not relivant.
43363  *
43364  * Fork - LGPL
43365  * <script type="text/javascript">
43366  */
43367  
43368 /**
43369  * @class Roo.form.Radio
43370  * @extends Roo.form.Checkbox
43371  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43372  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43373  * @constructor
43374  * Creates a new Radio
43375  * @param {Object} config Configuration options
43376  */
43377 Roo.form.Radio = function(){
43378     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43379 };
43380 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43381     inputType: 'radio',
43382
43383     /**
43384      * If this radio is part of a group, it will return the selected value
43385      * @return {String}
43386      */
43387     getGroupValue : function(){
43388         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43389     },
43390     
43391     
43392     onRender : function(ct, position){
43393         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43394         
43395         if(this.inputValue !== undefined){
43396             this.el.dom.value = this.inputValue;
43397         }
43398          
43399         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43400         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43401         //var viewEl = this.wrap.createChild({ 
43402         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43403         //this.viewEl = viewEl;   
43404         //this.wrap.on('click', this.onClick,  this); 
43405         
43406         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43407         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43408         
43409         
43410         
43411         if(this.boxLabel){
43412             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43413         //    viewEl.on('click', this.onClick,  this); 
43414         }
43415          if(this.checked){
43416             this.el.dom.checked =   'checked' ;
43417         }
43418          
43419     } 
43420     
43421     
43422 });//<script type="text/javascript">
43423
43424 /*
43425  * Based  Ext JS Library 1.1.1
43426  * Copyright(c) 2006-2007, Ext JS, LLC.
43427  * LGPL
43428  *
43429  */
43430  
43431 /**
43432  * @class Roo.HtmlEditorCore
43433  * @extends Roo.Component
43434  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43435  *
43436  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43437  */
43438
43439 Roo.HtmlEditorCore = function(config){
43440     
43441     
43442     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43443     
43444     
43445     this.addEvents({
43446         /**
43447          * @event initialize
43448          * Fires when the editor is fully initialized (including the iframe)
43449          * @param {Roo.HtmlEditorCore} this
43450          */
43451         initialize: true,
43452         /**
43453          * @event activate
43454          * Fires when the editor is first receives the focus. Any insertion must wait
43455          * until after this event.
43456          * @param {Roo.HtmlEditorCore} this
43457          */
43458         activate: true,
43459          /**
43460          * @event beforesync
43461          * Fires before the textarea is updated with content from the editor iframe. Return false
43462          * to cancel the sync.
43463          * @param {Roo.HtmlEditorCore} this
43464          * @param {String} html
43465          */
43466         beforesync: true,
43467          /**
43468          * @event beforepush
43469          * Fires before the iframe editor is updated with content from the textarea. Return false
43470          * to cancel the push.
43471          * @param {Roo.HtmlEditorCore} this
43472          * @param {String} html
43473          */
43474         beforepush: true,
43475          /**
43476          * @event sync
43477          * Fires when the textarea is updated with content from the editor iframe.
43478          * @param {Roo.HtmlEditorCore} this
43479          * @param {String} html
43480          */
43481         sync: true,
43482          /**
43483          * @event push
43484          * Fires when the iframe editor is updated with content from the textarea.
43485          * @param {Roo.HtmlEditorCore} this
43486          * @param {String} html
43487          */
43488         push: true,
43489         
43490         /**
43491          * @event editorevent
43492          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43493          * @param {Roo.HtmlEditorCore} this
43494          */
43495         editorevent: true
43496         
43497     });
43498     
43499     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43500     
43501     // defaults : white / black...
43502     this.applyBlacklists();
43503     
43504     
43505     
43506 };
43507
43508
43509 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43510
43511
43512      /**
43513      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43514      */
43515     
43516     owner : false,
43517     
43518      /**
43519      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43520      *                        Roo.resizable.
43521      */
43522     resizable : false,
43523      /**
43524      * @cfg {Number} height (in pixels)
43525      */   
43526     height: 300,
43527    /**
43528      * @cfg {Number} width (in pixels)
43529      */   
43530     width: 500,
43531     
43532     /**
43533      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43534      * 
43535      */
43536     stylesheets: false,
43537     
43538     // id of frame..
43539     frameId: false,
43540     
43541     // private properties
43542     validationEvent : false,
43543     deferHeight: true,
43544     initialized : false,
43545     activated : false,
43546     sourceEditMode : false,
43547     onFocus : Roo.emptyFn,
43548     iframePad:3,
43549     hideMode:'offsets',
43550     
43551     clearUp: true,
43552     
43553     // blacklist + whitelisted elements..
43554     black: false,
43555     white: false,
43556      
43557     bodyCls : '',
43558
43559     /**
43560      * Protected method that will not generally be called directly. It
43561      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43562      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43563      */
43564     getDocMarkup : function(){
43565         // body styles..
43566         var st = '';
43567         
43568         // inherit styels from page...?? 
43569         if (this.stylesheets === false) {
43570             
43571             Roo.get(document.head).select('style').each(function(node) {
43572                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43573             });
43574             
43575             Roo.get(document.head).select('link').each(function(node) { 
43576                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43577             });
43578             
43579         } else if (!this.stylesheets.length) {
43580                 // simple..
43581                 st = '<style type="text/css">' +
43582                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43583                    '</style>';
43584         } else { 
43585             st = '<style type="text/css">' +
43586                     this.stylesheets +
43587                 '</style>';
43588         }
43589         
43590         st +=  '<style type="text/css">' +
43591             'IMG { cursor: pointer } ' +
43592         '</style>';
43593
43594         var cls = 'roo-htmleditor-body';
43595         
43596         if(this.bodyCls.length){
43597             cls += ' ' + this.bodyCls;
43598         }
43599         
43600         return '<html><head>' + st  +
43601             //<style type="text/css">' +
43602             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43603             //'</style>' +
43604             ' </head><body class="' +  cls + '"></body></html>';
43605     },
43606
43607     // private
43608     onRender : function(ct, position)
43609     {
43610         var _t = this;
43611         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43612         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43613         
43614         
43615         this.el.dom.style.border = '0 none';
43616         this.el.dom.setAttribute('tabIndex', -1);
43617         this.el.addClass('x-hidden hide');
43618         
43619         
43620         
43621         if(Roo.isIE){ // fix IE 1px bogus margin
43622             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43623         }
43624        
43625         
43626         this.frameId = Roo.id();
43627         
43628          
43629         
43630         var iframe = this.owner.wrap.createChild({
43631             tag: 'iframe',
43632             cls: 'form-control', // bootstrap..
43633             id: this.frameId,
43634             name: this.frameId,
43635             frameBorder : 'no',
43636             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43637         }, this.el
43638         );
43639         
43640         
43641         this.iframe = iframe.dom;
43642
43643          this.assignDocWin();
43644         
43645         this.doc.designMode = 'on';
43646        
43647         this.doc.open();
43648         this.doc.write(this.getDocMarkup());
43649         this.doc.close();
43650
43651         
43652         var task = { // must defer to wait for browser to be ready
43653             run : function(){
43654                 //console.log("run task?" + this.doc.readyState);
43655                 this.assignDocWin();
43656                 if(this.doc.body || this.doc.readyState == 'complete'){
43657                     try {
43658                         this.doc.designMode="on";
43659                     } catch (e) {
43660                         return;
43661                     }
43662                     Roo.TaskMgr.stop(task);
43663                     this.initEditor.defer(10, this);
43664                 }
43665             },
43666             interval : 10,
43667             duration: 10000,
43668             scope: this
43669         };
43670         Roo.TaskMgr.start(task);
43671
43672     },
43673
43674     // private
43675     onResize : function(w, h)
43676     {
43677          Roo.log('resize: ' +w + ',' + h );
43678         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43679         if(!this.iframe){
43680             return;
43681         }
43682         if(typeof w == 'number'){
43683             
43684             this.iframe.style.width = w + 'px';
43685         }
43686         if(typeof h == 'number'){
43687             
43688             this.iframe.style.height = h + 'px';
43689             if(this.doc){
43690                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43691             }
43692         }
43693         
43694     },
43695
43696     /**
43697      * Toggles the editor between standard and source edit mode.
43698      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43699      */
43700     toggleSourceEdit : function(sourceEditMode){
43701         
43702         this.sourceEditMode = sourceEditMode === true;
43703         
43704         if(this.sourceEditMode){
43705  
43706             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43707             
43708         }else{
43709             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43710             //this.iframe.className = '';
43711             this.deferFocus();
43712         }
43713         //this.setSize(this.owner.wrap.getSize());
43714         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43715     },
43716
43717     
43718   
43719
43720     /**
43721      * Protected method that will not generally be called directly. If you need/want
43722      * custom HTML cleanup, this is the method you should override.
43723      * @param {String} html The HTML to be cleaned
43724      * return {String} The cleaned HTML
43725      */
43726     cleanHtml : function(html){
43727         html = String(html);
43728         if(html.length > 5){
43729             if(Roo.isSafari){ // strip safari nonsense
43730                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43731             }
43732         }
43733         if(html == '&nbsp;'){
43734             html = '';
43735         }
43736         return html;
43737     },
43738
43739     /**
43740      * HTML Editor -> Textarea
43741      * Protected method that will not generally be called directly. Syncs the contents
43742      * of the editor iframe with the textarea.
43743      */
43744     syncValue : function(){
43745         if(this.initialized){
43746             var bd = (this.doc.body || this.doc.documentElement);
43747             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43748             var html = bd.innerHTML;
43749             if(Roo.isSafari){
43750                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43751                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43752                 if(m && m[1]){
43753                     html = '<div style="'+m[0]+'">' + html + '</div>';
43754                 }
43755             }
43756             html = this.cleanHtml(html);
43757             // fix up the special chars.. normaly like back quotes in word...
43758             // however we do not want to do this with chinese..
43759             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43760                 
43761                 var cc = match.charCodeAt();
43762
43763                 // Get the character value, handling surrogate pairs
43764                 if (match.length == 2) {
43765                     // It's a surrogate pair, calculate the Unicode code point
43766                     var high = match.charCodeAt(0) - 0xD800;
43767                     var low  = match.charCodeAt(1) - 0xDC00;
43768                     cc = (high * 0x400) + low + 0x10000;
43769                 }  else if (
43770                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43771                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43772                     (cc >= 0xf900 && cc < 0xfb00 )
43773                 ) {
43774                         return match;
43775                 }  
43776          
43777                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43778                 return "&#" + cc + ";";
43779                 
43780                 
43781             });
43782             
43783             
43784              
43785             if(this.owner.fireEvent('beforesync', this, html) !== false){
43786                 this.el.dom.value = html;
43787                 this.owner.fireEvent('sync', this, html);
43788             }
43789         }
43790     },
43791
43792     /**
43793      * Protected method that will not generally be called directly. Pushes the value of the textarea
43794      * into the iframe editor.
43795      */
43796     pushValue : function(){
43797         if(this.initialized){
43798             var v = this.el.dom.value.trim();
43799             
43800 //            if(v.length < 1){
43801 //                v = '&#160;';
43802 //            }
43803             
43804             if(this.owner.fireEvent('beforepush', this, v) !== false){
43805                 var d = (this.doc.body || this.doc.documentElement);
43806                 d.innerHTML = v;
43807                 this.cleanUpPaste();
43808                 this.el.dom.value = d.innerHTML;
43809                 this.owner.fireEvent('push', this, v);
43810             }
43811         }
43812     },
43813
43814     // private
43815     deferFocus : function(){
43816         this.focus.defer(10, this);
43817     },
43818
43819     // doc'ed in Field
43820     focus : function(){
43821         if(this.win && !this.sourceEditMode){
43822             this.win.focus();
43823         }else{
43824             this.el.focus();
43825         }
43826     },
43827     
43828     assignDocWin: function()
43829     {
43830         var iframe = this.iframe;
43831         
43832          if(Roo.isIE){
43833             this.doc = iframe.contentWindow.document;
43834             this.win = iframe.contentWindow;
43835         } else {
43836 //            if (!Roo.get(this.frameId)) {
43837 //                return;
43838 //            }
43839 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43840 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43841             
43842             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43843                 return;
43844             }
43845             
43846             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43847             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43848         }
43849     },
43850     
43851     // private
43852     initEditor : function(){
43853         //console.log("INIT EDITOR");
43854         this.assignDocWin();
43855         
43856         
43857         
43858         this.doc.designMode="on";
43859         this.doc.open();
43860         this.doc.write(this.getDocMarkup());
43861         this.doc.close();
43862         
43863         var dbody = (this.doc.body || this.doc.documentElement);
43864         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43865         // this copies styles from the containing element into thsi one..
43866         // not sure why we need all of this..
43867         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43868         
43869         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43870         //ss['background-attachment'] = 'fixed'; // w3c
43871         dbody.bgProperties = 'fixed'; // ie
43872         //Roo.DomHelper.applyStyles(dbody, ss);
43873         Roo.EventManager.on(this.doc, {
43874             //'mousedown': this.onEditorEvent,
43875             'mouseup': this.onEditorEvent,
43876             'dblclick': this.onEditorEvent,
43877             'click': this.onEditorEvent,
43878             'keyup': this.onEditorEvent,
43879             buffer:100,
43880             scope: this
43881         });
43882         if(Roo.isGecko){
43883             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43884         }
43885         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43886             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43887         }
43888         this.initialized = true;
43889
43890         this.owner.fireEvent('initialize', this);
43891         this.pushValue();
43892     },
43893
43894     // private
43895     onDestroy : function(){
43896         
43897         
43898         
43899         if(this.rendered){
43900             
43901             //for (var i =0; i < this.toolbars.length;i++) {
43902             //    // fixme - ask toolbars for heights?
43903             //    this.toolbars[i].onDestroy();
43904            // }
43905             
43906             //this.wrap.dom.innerHTML = '';
43907             //this.wrap.remove();
43908         }
43909     },
43910
43911     // private
43912     onFirstFocus : function(){
43913         
43914         this.assignDocWin();
43915         
43916         
43917         this.activated = true;
43918          
43919     
43920         if(Roo.isGecko){ // prevent silly gecko errors
43921             this.win.focus();
43922             var s = this.win.getSelection();
43923             if(!s.focusNode || s.focusNode.nodeType != 3){
43924                 var r = s.getRangeAt(0);
43925                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43926                 r.collapse(true);
43927                 this.deferFocus();
43928             }
43929             try{
43930                 this.execCmd('useCSS', true);
43931                 this.execCmd('styleWithCSS', false);
43932             }catch(e){}
43933         }
43934         this.owner.fireEvent('activate', this);
43935     },
43936
43937     // private
43938     adjustFont: function(btn){
43939         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43940         //if(Roo.isSafari){ // safari
43941         //    adjust *= 2;
43942        // }
43943         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43944         if(Roo.isSafari){ // safari
43945             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43946             v =  (v < 10) ? 10 : v;
43947             v =  (v > 48) ? 48 : v;
43948             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43949             
43950         }
43951         
43952         
43953         v = Math.max(1, v+adjust);
43954         
43955         this.execCmd('FontSize', v  );
43956     },
43957
43958     onEditorEvent : function(e)
43959     {
43960         this.owner.fireEvent('editorevent', this, e);
43961       //  this.updateToolbar();
43962         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43963     },
43964
43965     insertTag : function(tg)
43966     {
43967         // could be a bit smarter... -> wrap the current selected tRoo..
43968         if (tg.toLowerCase() == 'span' ||
43969             tg.toLowerCase() == 'code' ||
43970             tg.toLowerCase() == 'sup' ||
43971             tg.toLowerCase() == 'sub' 
43972             ) {
43973             
43974             range = this.createRange(this.getSelection());
43975             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43976             wrappingNode.appendChild(range.extractContents());
43977             range.insertNode(wrappingNode);
43978
43979             return;
43980             
43981             
43982             
43983         }
43984         this.execCmd("formatblock",   tg);
43985         
43986     },
43987     
43988     insertText : function(txt)
43989     {
43990         
43991         
43992         var range = this.createRange();
43993         range.deleteContents();
43994                //alert(Sender.getAttribute('label'));
43995                
43996         range.insertNode(this.doc.createTextNode(txt));
43997     } ,
43998     
43999      
44000
44001     /**
44002      * Executes a Midas editor command on the editor document and performs necessary focus and
44003      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44004      * @param {String} cmd The Midas command
44005      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44006      */
44007     relayCmd : function(cmd, value){
44008         this.win.focus();
44009         this.execCmd(cmd, value);
44010         this.owner.fireEvent('editorevent', this);
44011         //this.updateToolbar();
44012         this.owner.deferFocus();
44013     },
44014
44015     /**
44016      * Executes a Midas editor command directly on the editor document.
44017      * For visual commands, you should use {@link #relayCmd} instead.
44018      * <b>This should only be called after the editor is initialized.</b>
44019      * @param {String} cmd The Midas command
44020      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44021      */
44022     execCmd : function(cmd, value){
44023         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44024         this.syncValue();
44025     },
44026  
44027  
44028    
44029     /**
44030      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44031      * to insert tRoo.
44032      * @param {String} text | dom node.. 
44033      */
44034     insertAtCursor : function(text)
44035     {
44036         
44037         if(!this.activated){
44038             return;
44039         }
44040         /*
44041         if(Roo.isIE){
44042             this.win.focus();
44043             var r = this.doc.selection.createRange();
44044             if(r){
44045                 r.collapse(true);
44046                 r.pasteHTML(text);
44047                 this.syncValue();
44048                 this.deferFocus();
44049             
44050             }
44051             return;
44052         }
44053         */
44054         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44055             this.win.focus();
44056             
44057             
44058             // from jquery ui (MIT licenced)
44059             var range, node;
44060             var win = this.win;
44061             
44062             if (win.getSelection && win.getSelection().getRangeAt) {
44063                 range = win.getSelection().getRangeAt(0);
44064                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44065                 range.insertNode(node);
44066             } else if (win.document.selection && win.document.selection.createRange) {
44067                 // no firefox support
44068                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44069                 win.document.selection.createRange().pasteHTML(txt);
44070             } else {
44071                 // no firefox support
44072                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44073                 this.execCmd('InsertHTML', txt);
44074             } 
44075             
44076             this.syncValue();
44077             
44078             this.deferFocus();
44079         }
44080     },
44081  // private
44082     mozKeyPress : function(e){
44083         if(e.ctrlKey){
44084             var c = e.getCharCode(), cmd;
44085           
44086             if(c > 0){
44087                 c = String.fromCharCode(c).toLowerCase();
44088                 switch(c){
44089                     case 'b':
44090                         cmd = 'bold';
44091                         break;
44092                     case 'i':
44093                         cmd = 'italic';
44094                         break;
44095                     
44096                     case 'u':
44097                         cmd = 'underline';
44098                         break;
44099                     
44100                     case 'v':
44101                         this.cleanUpPaste.defer(100, this);
44102                         return;
44103                         
44104                 }
44105                 if(cmd){
44106                     this.win.focus();
44107                     this.execCmd(cmd);
44108                     this.deferFocus();
44109                     e.preventDefault();
44110                 }
44111                 
44112             }
44113         }
44114     },
44115
44116     // private
44117     fixKeys : function(){ // load time branching for fastest keydown performance
44118         if(Roo.isIE){
44119             return function(e){
44120                 var k = e.getKey(), r;
44121                 if(k == e.TAB){
44122                     e.stopEvent();
44123                     r = this.doc.selection.createRange();
44124                     if(r){
44125                         r.collapse(true);
44126                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44127                         this.deferFocus();
44128                     }
44129                     return;
44130                 }
44131                 
44132                 if(k == e.ENTER){
44133                     r = this.doc.selection.createRange();
44134                     if(r){
44135                         var target = r.parentElement();
44136                         if(!target || target.tagName.toLowerCase() != 'li'){
44137                             e.stopEvent();
44138                             r.pasteHTML('<br />');
44139                             r.collapse(false);
44140                             r.select();
44141                         }
44142                     }
44143                 }
44144                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44145                     this.cleanUpPaste.defer(100, this);
44146                     return;
44147                 }
44148                 
44149                 
44150             };
44151         }else if(Roo.isOpera){
44152             return function(e){
44153                 var k = e.getKey();
44154                 if(k == e.TAB){
44155                     e.stopEvent();
44156                     this.win.focus();
44157                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44158                     this.deferFocus();
44159                 }
44160                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44161                     this.cleanUpPaste.defer(100, this);
44162                     return;
44163                 }
44164                 
44165             };
44166         }else if(Roo.isSafari){
44167             return function(e){
44168                 var k = e.getKey();
44169                 
44170                 if(k == e.TAB){
44171                     e.stopEvent();
44172                     this.execCmd('InsertText','\t');
44173                     this.deferFocus();
44174                     return;
44175                 }
44176                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44177                     this.cleanUpPaste.defer(100, this);
44178                     return;
44179                 }
44180                 
44181              };
44182         }
44183     }(),
44184     
44185     getAllAncestors: function()
44186     {
44187         var p = this.getSelectedNode();
44188         var a = [];
44189         if (!p) {
44190             a.push(p); // push blank onto stack..
44191             p = this.getParentElement();
44192         }
44193         
44194         
44195         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44196             a.push(p);
44197             p = p.parentNode;
44198         }
44199         a.push(this.doc.body);
44200         return a;
44201     },
44202     lastSel : false,
44203     lastSelNode : false,
44204     
44205     
44206     getSelection : function() 
44207     {
44208         this.assignDocWin();
44209         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44210     },
44211     
44212     getSelectedNode: function() 
44213     {
44214         // this may only work on Gecko!!!
44215         
44216         // should we cache this!!!!
44217         
44218         
44219         
44220          
44221         var range = this.createRange(this.getSelection()).cloneRange();
44222         
44223         if (Roo.isIE) {
44224             var parent = range.parentElement();
44225             while (true) {
44226                 var testRange = range.duplicate();
44227                 testRange.moveToElementText(parent);
44228                 if (testRange.inRange(range)) {
44229                     break;
44230                 }
44231                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44232                     break;
44233                 }
44234                 parent = parent.parentElement;
44235             }
44236             return parent;
44237         }
44238         
44239         // is ancestor a text element.
44240         var ac =  range.commonAncestorContainer;
44241         if (ac.nodeType == 3) {
44242             ac = ac.parentNode;
44243         }
44244         
44245         var ar = ac.childNodes;
44246          
44247         var nodes = [];
44248         var other_nodes = [];
44249         var has_other_nodes = false;
44250         for (var i=0;i<ar.length;i++) {
44251             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44252                 continue;
44253             }
44254             // fullly contained node.
44255             
44256             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44257                 nodes.push(ar[i]);
44258                 continue;
44259             }
44260             
44261             // probably selected..
44262             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44263                 other_nodes.push(ar[i]);
44264                 continue;
44265             }
44266             // outer..
44267             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44268                 continue;
44269             }
44270             
44271             
44272             has_other_nodes = true;
44273         }
44274         if (!nodes.length && other_nodes.length) {
44275             nodes= other_nodes;
44276         }
44277         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44278             return false;
44279         }
44280         
44281         return nodes[0];
44282     },
44283     createRange: function(sel)
44284     {
44285         // this has strange effects when using with 
44286         // top toolbar - not sure if it's a great idea.
44287         //this.editor.contentWindow.focus();
44288         if (typeof sel != "undefined") {
44289             try {
44290                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44291             } catch(e) {
44292                 return this.doc.createRange();
44293             }
44294         } else {
44295             return this.doc.createRange();
44296         }
44297     },
44298     getParentElement: function()
44299     {
44300         
44301         this.assignDocWin();
44302         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44303         
44304         var range = this.createRange(sel);
44305          
44306         try {
44307             var p = range.commonAncestorContainer;
44308             while (p.nodeType == 3) { // text node
44309                 p = p.parentNode;
44310             }
44311             return p;
44312         } catch (e) {
44313             return null;
44314         }
44315     
44316     },
44317     /***
44318      *
44319      * Range intersection.. the hard stuff...
44320      *  '-1' = before
44321      *  '0' = hits..
44322      *  '1' = after.
44323      *         [ -- selected range --- ]
44324      *   [fail]                        [fail]
44325      *
44326      *    basically..
44327      *      if end is before start or  hits it. fail.
44328      *      if start is after end or hits it fail.
44329      *
44330      *   if either hits (but other is outside. - then it's not 
44331      *   
44332      *    
44333      **/
44334     
44335     
44336     // @see http://www.thismuchiknow.co.uk/?p=64.
44337     rangeIntersectsNode : function(range, node)
44338     {
44339         var nodeRange = node.ownerDocument.createRange();
44340         try {
44341             nodeRange.selectNode(node);
44342         } catch (e) {
44343             nodeRange.selectNodeContents(node);
44344         }
44345     
44346         var rangeStartRange = range.cloneRange();
44347         rangeStartRange.collapse(true);
44348     
44349         var rangeEndRange = range.cloneRange();
44350         rangeEndRange.collapse(false);
44351     
44352         var nodeStartRange = nodeRange.cloneRange();
44353         nodeStartRange.collapse(true);
44354     
44355         var nodeEndRange = nodeRange.cloneRange();
44356         nodeEndRange.collapse(false);
44357     
44358         return rangeStartRange.compareBoundaryPoints(
44359                  Range.START_TO_START, nodeEndRange) == -1 &&
44360                rangeEndRange.compareBoundaryPoints(
44361                  Range.START_TO_START, nodeStartRange) == 1;
44362         
44363          
44364     },
44365     rangeCompareNode : function(range, node)
44366     {
44367         var nodeRange = node.ownerDocument.createRange();
44368         try {
44369             nodeRange.selectNode(node);
44370         } catch (e) {
44371             nodeRange.selectNodeContents(node);
44372         }
44373         
44374         
44375         range.collapse(true);
44376     
44377         nodeRange.collapse(true);
44378      
44379         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44380         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44381          
44382         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44383         
44384         var nodeIsBefore   =  ss == 1;
44385         var nodeIsAfter    = ee == -1;
44386         
44387         if (nodeIsBefore && nodeIsAfter) {
44388             return 0; // outer
44389         }
44390         if (!nodeIsBefore && nodeIsAfter) {
44391             return 1; //right trailed.
44392         }
44393         
44394         if (nodeIsBefore && !nodeIsAfter) {
44395             return 2;  // left trailed.
44396         }
44397         // fully contined.
44398         return 3;
44399     },
44400
44401     // private? - in a new class?
44402     cleanUpPaste :  function()
44403     {
44404         // cleans up the whole document..
44405         Roo.log('cleanuppaste');
44406         
44407         this.cleanUpChildren(this.doc.body);
44408         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44409         if (clean != this.doc.body.innerHTML) {
44410             this.doc.body.innerHTML = clean;
44411         }
44412         
44413     },
44414     
44415     cleanWordChars : function(input) {// change the chars to hex code
44416         var he = Roo.HtmlEditorCore;
44417         
44418         var output = input;
44419         Roo.each(he.swapCodes, function(sw) { 
44420             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44421             
44422             output = output.replace(swapper, sw[1]);
44423         });
44424         
44425         return output;
44426     },
44427     
44428     
44429     cleanUpChildren : function (n)
44430     {
44431         if (!n.childNodes.length) {
44432             return;
44433         }
44434         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44435            this.cleanUpChild(n.childNodes[i]);
44436         }
44437     },
44438     
44439     
44440         
44441     
44442     cleanUpChild : function (node)
44443     {
44444         var ed = this;
44445         //console.log(node);
44446         if (node.nodeName == "#text") {
44447             // clean up silly Windows -- stuff?
44448             return; 
44449         }
44450         if (node.nodeName == "#comment") {
44451             node.parentNode.removeChild(node);
44452             // clean up silly Windows -- stuff?
44453             return; 
44454         }
44455         var lcname = node.tagName.toLowerCase();
44456         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44457         // whitelist of tags..
44458         
44459         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44460             // remove node.
44461             node.parentNode.removeChild(node);
44462             return;
44463             
44464         }
44465         
44466         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44467         
44468         // spans with no attributes - just remove them..
44469         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44470             remove_keep_children = true;
44471         }
44472         
44473         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44474         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44475         
44476         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44477         //    remove_keep_children = true;
44478         //}
44479         
44480         if (remove_keep_children) {
44481             this.cleanUpChildren(node);
44482             // inserts everything just before this node...
44483             while (node.childNodes.length) {
44484                 var cn = node.childNodes[0];
44485                 node.removeChild(cn);
44486                 node.parentNode.insertBefore(cn, node);
44487             }
44488             node.parentNode.removeChild(node);
44489             return;
44490         }
44491         
44492         if (!node.attributes || !node.attributes.length) {
44493             
44494           
44495             
44496             
44497             this.cleanUpChildren(node);
44498             return;
44499         }
44500         
44501         function cleanAttr(n,v)
44502         {
44503             
44504             if (v.match(/^\./) || v.match(/^\//)) {
44505                 return;
44506             }
44507             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44508                 return;
44509             }
44510             if (v.match(/^#/)) {
44511                 return;
44512             }
44513 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44514             node.removeAttribute(n);
44515             
44516         }
44517         
44518         var cwhite = this.cwhite;
44519         var cblack = this.cblack;
44520             
44521         function cleanStyle(n,v)
44522         {
44523             if (v.match(/expression/)) { //XSS?? should we even bother..
44524                 node.removeAttribute(n);
44525                 return;
44526             }
44527             
44528             var parts = v.split(/;/);
44529             var clean = [];
44530             
44531             Roo.each(parts, function(p) {
44532                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44533                 if (!p.length) {
44534                     return true;
44535                 }
44536                 var l = p.split(':').shift().replace(/\s+/g,'');
44537                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44538                 
44539                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44540 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44541                     //node.removeAttribute(n);
44542                     return true;
44543                 }
44544                 //Roo.log()
44545                 // only allow 'c whitelisted system attributes'
44546                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44547 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44548                     //node.removeAttribute(n);
44549                     return true;
44550                 }
44551                 
44552                 
44553                  
44554                 
44555                 clean.push(p);
44556                 return true;
44557             });
44558             if (clean.length) { 
44559                 node.setAttribute(n, clean.join(';'));
44560             } else {
44561                 node.removeAttribute(n);
44562             }
44563             
44564         }
44565         
44566         
44567         for (var i = node.attributes.length-1; i > -1 ; i--) {
44568             var a = node.attributes[i];
44569             //console.log(a);
44570             
44571             if (a.name.toLowerCase().substr(0,2)=='on')  {
44572                 node.removeAttribute(a.name);
44573                 continue;
44574             }
44575             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44576                 node.removeAttribute(a.name);
44577                 continue;
44578             }
44579             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44580                 cleanAttr(a.name,a.value); // fixme..
44581                 continue;
44582             }
44583             if (a.name == 'style') {
44584                 cleanStyle(a.name,a.value);
44585                 continue;
44586             }
44587             /// clean up MS crap..
44588             // tecnically this should be a list of valid class'es..
44589             
44590             
44591             if (a.name == 'class') {
44592                 if (a.value.match(/^Mso/)) {
44593                     node.removeAttribute('class');
44594                 }
44595                 
44596                 if (a.value.match(/^body$/)) {
44597                     node.removeAttribute('class');
44598                 }
44599                 continue;
44600             }
44601             
44602             // style cleanup!?
44603             // class cleanup?
44604             
44605         }
44606         
44607         
44608         this.cleanUpChildren(node);
44609         
44610         
44611     },
44612     
44613     /**
44614      * Clean up MS wordisms...
44615      */
44616     cleanWord : function(node)
44617     {
44618         if (!node) {
44619             this.cleanWord(this.doc.body);
44620             return;
44621         }
44622         
44623         if(
44624                 node.nodeName == 'SPAN' &&
44625                 !node.hasAttributes() &&
44626                 node.childNodes.length == 1 &&
44627                 node.firstChild.nodeName == "#text"  
44628         ) {
44629             var textNode = node.firstChild;
44630             node.removeChild(textNode);
44631             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44632                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44633             }
44634             node.parentNode.insertBefore(textNode, node);
44635             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44636                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44637             }
44638             node.parentNode.removeChild(node);
44639         }
44640         
44641         if (node.nodeName == "#text") {
44642             // clean up silly Windows -- stuff?
44643             return; 
44644         }
44645         if (node.nodeName == "#comment") {
44646             node.parentNode.removeChild(node);
44647             // clean up silly Windows -- stuff?
44648             return; 
44649         }
44650         
44651         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44652             node.parentNode.removeChild(node);
44653             return;
44654         }
44655         //Roo.log(node.tagName);
44656         // remove - but keep children..
44657         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44658             //Roo.log('-- removed');
44659             while (node.childNodes.length) {
44660                 var cn = node.childNodes[0];
44661                 node.removeChild(cn);
44662                 node.parentNode.insertBefore(cn, node);
44663                 // move node to parent - and clean it..
44664                 this.cleanWord(cn);
44665             }
44666             node.parentNode.removeChild(node);
44667             /// no need to iterate chidlren = it's got none..
44668             //this.iterateChildren(node, this.cleanWord);
44669             return;
44670         }
44671         // clean styles
44672         if (node.className.length) {
44673             
44674             var cn = node.className.split(/\W+/);
44675             var cna = [];
44676             Roo.each(cn, function(cls) {
44677                 if (cls.match(/Mso[a-zA-Z]+/)) {
44678                     return;
44679                 }
44680                 cna.push(cls);
44681             });
44682             node.className = cna.length ? cna.join(' ') : '';
44683             if (!cna.length) {
44684                 node.removeAttribute("class");
44685             }
44686         }
44687         
44688         if (node.hasAttribute("lang")) {
44689             node.removeAttribute("lang");
44690         }
44691         
44692         if (node.hasAttribute("style")) {
44693             
44694             var styles = node.getAttribute("style").split(";");
44695             var nstyle = [];
44696             Roo.each(styles, function(s) {
44697                 if (!s.match(/:/)) {
44698                     return;
44699                 }
44700                 var kv = s.split(":");
44701                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44702                     return;
44703                 }
44704                 // what ever is left... we allow.
44705                 nstyle.push(s);
44706             });
44707             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44708             if (!nstyle.length) {
44709                 node.removeAttribute('style');
44710             }
44711         }
44712         this.iterateChildren(node, this.cleanWord);
44713         
44714         
44715         
44716     },
44717     /**
44718      * iterateChildren of a Node, calling fn each time, using this as the scole..
44719      * @param {DomNode} node node to iterate children of.
44720      * @param {Function} fn method of this class to call on each item.
44721      */
44722     iterateChildren : function(node, fn)
44723     {
44724         if (!node.childNodes.length) {
44725                 return;
44726         }
44727         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44728            fn.call(this, node.childNodes[i])
44729         }
44730     },
44731     
44732     
44733     /**
44734      * cleanTableWidths.
44735      *
44736      * Quite often pasting from word etc.. results in tables with column and widths.
44737      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44738      *
44739      */
44740     cleanTableWidths : function(node)
44741     {
44742          
44743          
44744         if (!node) {
44745             this.cleanTableWidths(this.doc.body);
44746             return;
44747         }
44748         
44749         // ignore list...
44750         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44751             return; 
44752         }
44753         Roo.log(node.tagName);
44754         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44755             this.iterateChildren(node, this.cleanTableWidths);
44756             return;
44757         }
44758         if (node.hasAttribute('width')) {
44759             node.removeAttribute('width');
44760         }
44761         
44762          
44763         if (node.hasAttribute("style")) {
44764             // pretty basic...
44765             
44766             var styles = node.getAttribute("style").split(";");
44767             var nstyle = [];
44768             Roo.each(styles, function(s) {
44769                 if (!s.match(/:/)) {
44770                     return;
44771                 }
44772                 var kv = s.split(":");
44773                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44774                     return;
44775                 }
44776                 // what ever is left... we allow.
44777                 nstyle.push(s);
44778             });
44779             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44780             if (!nstyle.length) {
44781                 node.removeAttribute('style');
44782             }
44783         }
44784         
44785         this.iterateChildren(node, this.cleanTableWidths);
44786         
44787         
44788     },
44789     
44790     
44791     
44792     
44793     domToHTML : function(currentElement, depth, nopadtext) {
44794         
44795         depth = depth || 0;
44796         nopadtext = nopadtext || false;
44797     
44798         if (!currentElement) {
44799             return this.domToHTML(this.doc.body);
44800         }
44801         
44802         //Roo.log(currentElement);
44803         var j;
44804         var allText = false;
44805         var nodeName = currentElement.nodeName;
44806         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44807         
44808         if  (nodeName == '#text') {
44809             
44810             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44811         }
44812         
44813         
44814         var ret = '';
44815         if (nodeName != 'BODY') {
44816              
44817             var i = 0;
44818             // Prints the node tagName, such as <A>, <IMG>, etc
44819             if (tagName) {
44820                 var attr = [];
44821                 for(i = 0; i < currentElement.attributes.length;i++) {
44822                     // quoting?
44823                     var aname = currentElement.attributes.item(i).name;
44824                     if (!currentElement.attributes.item(i).value.length) {
44825                         continue;
44826                     }
44827                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44828                 }
44829                 
44830                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44831             } 
44832             else {
44833                 
44834                 // eack
44835             }
44836         } else {
44837             tagName = false;
44838         }
44839         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44840             return ret;
44841         }
44842         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44843             nopadtext = true;
44844         }
44845         
44846         
44847         // Traverse the tree
44848         i = 0;
44849         var currentElementChild = currentElement.childNodes.item(i);
44850         var allText = true;
44851         var innerHTML  = '';
44852         lastnode = '';
44853         while (currentElementChild) {
44854             // Formatting code (indent the tree so it looks nice on the screen)
44855             var nopad = nopadtext;
44856             if (lastnode == 'SPAN') {
44857                 nopad  = true;
44858             }
44859             // text
44860             if  (currentElementChild.nodeName == '#text') {
44861                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44862                 toadd = nopadtext ? toadd : toadd.trim();
44863                 if (!nopad && toadd.length > 80) {
44864                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44865                 }
44866                 innerHTML  += toadd;
44867                 
44868                 i++;
44869                 currentElementChild = currentElement.childNodes.item(i);
44870                 lastNode = '';
44871                 continue;
44872             }
44873             allText = false;
44874             
44875             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44876                 
44877             // Recursively traverse the tree structure of the child node
44878             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44879             lastnode = currentElementChild.nodeName;
44880             i++;
44881             currentElementChild=currentElement.childNodes.item(i);
44882         }
44883         
44884         ret += innerHTML;
44885         
44886         if (!allText) {
44887                 // The remaining code is mostly for formatting the tree
44888             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44889         }
44890         
44891         
44892         if (tagName) {
44893             ret+= "</"+tagName+">";
44894         }
44895         return ret;
44896         
44897     },
44898         
44899     applyBlacklists : function()
44900     {
44901         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44902         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44903         
44904         this.white = [];
44905         this.black = [];
44906         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44907             if (b.indexOf(tag) > -1) {
44908                 return;
44909             }
44910             this.white.push(tag);
44911             
44912         }, this);
44913         
44914         Roo.each(w, function(tag) {
44915             if (b.indexOf(tag) > -1) {
44916                 return;
44917             }
44918             if (this.white.indexOf(tag) > -1) {
44919                 return;
44920             }
44921             this.white.push(tag);
44922             
44923         }, this);
44924         
44925         
44926         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44927             if (w.indexOf(tag) > -1) {
44928                 return;
44929             }
44930             this.black.push(tag);
44931             
44932         }, this);
44933         
44934         Roo.each(b, function(tag) {
44935             if (w.indexOf(tag) > -1) {
44936                 return;
44937             }
44938             if (this.black.indexOf(tag) > -1) {
44939                 return;
44940             }
44941             this.black.push(tag);
44942             
44943         }, this);
44944         
44945         
44946         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44947         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44948         
44949         this.cwhite = [];
44950         this.cblack = [];
44951         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44952             if (b.indexOf(tag) > -1) {
44953                 return;
44954             }
44955             this.cwhite.push(tag);
44956             
44957         }, this);
44958         
44959         Roo.each(w, function(tag) {
44960             if (b.indexOf(tag) > -1) {
44961                 return;
44962             }
44963             if (this.cwhite.indexOf(tag) > -1) {
44964                 return;
44965             }
44966             this.cwhite.push(tag);
44967             
44968         }, this);
44969         
44970         
44971         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44972             if (w.indexOf(tag) > -1) {
44973                 return;
44974             }
44975             this.cblack.push(tag);
44976             
44977         }, this);
44978         
44979         Roo.each(b, function(tag) {
44980             if (w.indexOf(tag) > -1) {
44981                 return;
44982             }
44983             if (this.cblack.indexOf(tag) > -1) {
44984                 return;
44985             }
44986             this.cblack.push(tag);
44987             
44988         }, this);
44989     },
44990     
44991     setStylesheets : function(stylesheets)
44992     {
44993         if(typeof(stylesheets) == 'string'){
44994             Roo.get(this.iframe.contentDocument.head).createChild({
44995                 tag : 'link',
44996                 rel : 'stylesheet',
44997                 type : 'text/css',
44998                 href : stylesheets
44999             });
45000             
45001             return;
45002         }
45003         var _this = this;
45004      
45005         Roo.each(stylesheets, function(s) {
45006             if(!s.length){
45007                 return;
45008             }
45009             
45010             Roo.get(_this.iframe.contentDocument.head).createChild({
45011                 tag : 'link',
45012                 rel : 'stylesheet',
45013                 type : 'text/css',
45014                 href : s
45015             });
45016         });
45017
45018         
45019     },
45020     
45021     removeStylesheets : function()
45022     {
45023         var _this = this;
45024         
45025         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45026             s.remove();
45027         });
45028     },
45029     
45030     setStyle : function(style)
45031     {
45032         Roo.get(this.iframe.contentDocument.head).createChild({
45033             tag : 'style',
45034             type : 'text/css',
45035             html : style
45036         });
45037
45038         return;
45039     }
45040     
45041     // hide stuff that is not compatible
45042     /**
45043      * @event blur
45044      * @hide
45045      */
45046     /**
45047      * @event change
45048      * @hide
45049      */
45050     /**
45051      * @event focus
45052      * @hide
45053      */
45054     /**
45055      * @event specialkey
45056      * @hide
45057      */
45058     /**
45059      * @cfg {String} fieldClass @hide
45060      */
45061     /**
45062      * @cfg {String} focusClass @hide
45063      */
45064     /**
45065      * @cfg {String} autoCreate @hide
45066      */
45067     /**
45068      * @cfg {String} inputType @hide
45069      */
45070     /**
45071      * @cfg {String} invalidClass @hide
45072      */
45073     /**
45074      * @cfg {String} invalidText @hide
45075      */
45076     /**
45077      * @cfg {String} msgFx @hide
45078      */
45079     /**
45080      * @cfg {String} validateOnBlur @hide
45081      */
45082 });
45083
45084 Roo.HtmlEditorCore.white = [
45085         'area', 'br', 'img', 'input', 'hr', 'wbr',
45086         
45087        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45088        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45089        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45090        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45091        'table',   'ul',         'xmp', 
45092        
45093        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45094       'thead',   'tr', 
45095      
45096       'dir', 'menu', 'ol', 'ul', 'dl',
45097        
45098       'embed',  'object'
45099 ];
45100
45101
45102 Roo.HtmlEditorCore.black = [
45103     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45104         'applet', // 
45105         'base',   'basefont', 'bgsound', 'blink',  'body', 
45106         'frame',  'frameset', 'head',    'html',   'ilayer', 
45107         'iframe', 'layer',  'link',     'meta',    'object',   
45108         'script', 'style' ,'title',  'xml' // clean later..
45109 ];
45110 Roo.HtmlEditorCore.clean = [
45111     'script', 'style', 'title', 'xml'
45112 ];
45113 Roo.HtmlEditorCore.remove = [
45114     'font'
45115 ];
45116 // attributes..
45117
45118 Roo.HtmlEditorCore.ablack = [
45119     'on'
45120 ];
45121     
45122 Roo.HtmlEditorCore.aclean = [ 
45123     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45124 ];
45125
45126 // protocols..
45127 Roo.HtmlEditorCore.pwhite= [
45128         'http',  'https',  'mailto'
45129 ];
45130
45131 // white listed style attributes.
45132 Roo.HtmlEditorCore.cwhite= [
45133       //  'text-align', /// default is to allow most things..
45134       
45135          
45136 //        'font-size'//??
45137 ];
45138
45139 // black listed style attributes.
45140 Roo.HtmlEditorCore.cblack= [
45141       //  'font-size' -- this can be set by the project 
45142 ];
45143
45144
45145 Roo.HtmlEditorCore.swapCodes   =[ 
45146     [    8211, "--" ], 
45147     [    8212, "--" ], 
45148     [    8216,  "'" ],  
45149     [    8217, "'" ],  
45150     [    8220, '"' ],  
45151     [    8221, '"' ],  
45152     [    8226, "*" ],  
45153     [    8230, "..." ]
45154 ]; 
45155
45156     //<script type="text/javascript">
45157
45158 /*
45159  * Ext JS Library 1.1.1
45160  * Copyright(c) 2006-2007, Ext JS, LLC.
45161  * Licence LGPL
45162  * 
45163  */
45164  
45165  
45166 Roo.form.HtmlEditor = function(config){
45167     
45168     
45169     
45170     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45171     
45172     if (!this.toolbars) {
45173         this.toolbars = [];
45174     }
45175     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45176     
45177     
45178 };
45179
45180 /**
45181  * @class Roo.form.HtmlEditor
45182  * @extends Roo.form.Field
45183  * Provides a lightweight HTML Editor component.
45184  *
45185  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45186  * 
45187  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45188  * supported by this editor.</b><br/><br/>
45189  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45190  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45191  */
45192 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45193     /**
45194      * @cfg {Boolean} clearUp
45195      */
45196     clearUp : true,
45197       /**
45198      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45199      */
45200     toolbars : false,
45201    
45202      /**
45203      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45204      *                        Roo.resizable.
45205      */
45206     resizable : false,
45207      /**
45208      * @cfg {Number} height (in pixels)
45209      */   
45210     height: 300,
45211    /**
45212      * @cfg {Number} width (in pixels)
45213      */   
45214     width: 500,
45215     
45216     /**
45217      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45218      * 
45219      */
45220     stylesheets: false,
45221     
45222     
45223      /**
45224      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45225      * 
45226      */
45227     cblack: false,
45228     /**
45229      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45230      * 
45231      */
45232     cwhite: false,
45233     
45234      /**
45235      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45236      * 
45237      */
45238     black: false,
45239     /**
45240      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45241      * 
45242      */
45243     white: false,
45244     
45245     // id of frame..
45246     frameId: false,
45247     
45248     // private properties
45249     validationEvent : false,
45250     deferHeight: true,
45251     initialized : false,
45252     activated : false,
45253     
45254     onFocus : Roo.emptyFn,
45255     iframePad:3,
45256     hideMode:'offsets',
45257     
45258     actionMode : 'container', // defaults to hiding it...
45259     
45260     defaultAutoCreate : { // modified by initCompnoent..
45261         tag: "textarea",
45262         style:"width:500px;height:300px;",
45263         autocomplete: "new-password"
45264     },
45265
45266     // private
45267     initComponent : function(){
45268         this.addEvents({
45269             /**
45270              * @event initialize
45271              * Fires when the editor is fully initialized (including the iframe)
45272              * @param {HtmlEditor} this
45273              */
45274             initialize: true,
45275             /**
45276              * @event activate
45277              * Fires when the editor is first receives the focus. Any insertion must wait
45278              * until after this event.
45279              * @param {HtmlEditor} this
45280              */
45281             activate: true,
45282              /**
45283              * @event beforesync
45284              * Fires before the textarea is updated with content from the editor iframe. Return false
45285              * to cancel the sync.
45286              * @param {HtmlEditor} this
45287              * @param {String} html
45288              */
45289             beforesync: true,
45290              /**
45291              * @event beforepush
45292              * Fires before the iframe editor is updated with content from the textarea. Return false
45293              * to cancel the push.
45294              * @param {HtmlEditor} this
45295              * @param {String} html
45296              */
45297             beforepush: true,
45298              /**
45299              * @event sync
45300              * Fires when the textarea is updated with content from the editor iframe.
45301              * @param {HtmlEditor} this
45302              * @param {String} html
45303              */
45304             sync: true,
45305              /**
45306              * @event push
45307              * Fires when the iframe editor is updated with content from the textarea.
45308              * @param {HtmlEditor} this
45309              * @param {String} html
45310              */
45311             push: true,
45312              /**
45313              * @event editmodechange
45314              * Fires when the editor switches edit modes
45315              * @param {HtmlEditor} this
45316              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45317              */
45318             editmodechange: true,
45319             /**
45320              * @event editorevent
45321              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45322              * @param {HtmlEditor} this
45323              */
45324             editorevent: true,
45325             /**
45326              * @event firstfocus
45327              * Fires when on first focus - needed by toolbars..
45328              * @param {HtmlEditor} this
45329              */
45330             firstfocus: true,
45331             /**
45332              * @event autosave
45333              * Auto save the htmlEditor value as a file into Events
45334              * @param {HtmlEditor} this
45335              */
45336             autosave: true,
45337             /**
45338              * @event savedpreview
45339              * preview the saved version of htmlEditor
45340              * @param {HtmlEditor} this
45341              */
45342             savedpreview: true,
45343             
45344             /**
45345             * @event stylesheetsclick
45346             * Fires when press the Sytlesheets button
45347             * @param {Roo.HtmlEditorCore} this
45348             */
45349             stylesheetsclick: true
45350         });
45351         this.defaultAutoCreate =  {
45352             tag: "textarea",
45353             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45354             autocomplete: "new-password"
45355         };
45356     },
45357
45358     /**
45359      * Protected method that will not generally be called directly. It
45360      * is called when the editor creates its toolbar. Override this method if you need to
45361      * add custom toolbar buttons.
45362      * @param {HtmlEditor} editor
45363      */
45364     createToolbar : function(editor){
45365         Roo.log("create toolbars");
45366         if (!editor.toolbars || !editor.toolbars.length) {
45367             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45368         }
45369         
45370         for (var i =0 ; i < editor.toolbars.length;i++) {
45371             editor.toolbars[i] = Roo.factory(
45372                     typeof(editor.toolbars[i]) == 'string' ?
45373                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45374                 Roo.form.HtmlEditor);
45375             editor.toolbars[i].init(editor);
45376         }
45377          
45378         
45379     },
45380
45381      
45382     // private
45383     onRender : function(ct, position)
45384     {
45385         var _t = this;
45386         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45387         
45388         this.wrap = this.el.wrap({
45389             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45390         });
45391         
45392         this.editorcore.onRender(ct, position);
45393          
45394         if (this.resizable) {
45395             this.resizeEl = new Roo.Resizable(this.wrap, {
45396                 pinned : true,
45397                 wrap: true,
45398                 dynamic : true,
45399                 minHeight : this.height,
45400                 height: this.height,
45401                 handles : this.resizable,
45402                 width: this.width,
45403                 listeners : {
45404                     resize : function(r, w, h) {
45405                         _t.onResize(w,h); // -something
45406                     }
45407                 }
45408             });
45409             
45410         }
45411         this.createToolbar(this);
45412        
45413         
45414         if(!this.width){
45415             this.setSize(this.wrap.getSize());
45416         }
45417         if (this.resizeEl) {
45418             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45419             // should trigger onReize..
45420         }
45421         
45422         this.keyNav = new Roo.KeyNav(this.el, {
45423             
45424             "tab" : function(e){
45425                 e.preventDefault();
45426                 
45427                 var value = this.getValue();
45428                 
45429                 var start = this.el.dom.selectionStart;
45430                 var end = this.el.dom.selectionEnd;
45431                 
45432                 if(!e.shiftKey){
45433                     
45434                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45435                     this.el.dom.setSelectionRange(end + 1, end + 1);
45436                     return;
45437                 }
45438                 
45439                 var f = value.substring(0, start).split("\t");
45440                 
45441                 if(f.pop().length != 0){
45442                     return;
45443                 }
45444                 
45445                 this.setValue(f.join("\t") + value.substring(end));
45446                 this.el.dom.setSelectionRange(start - 1, start - 1);
45447                 
45448             },
45449             
45450             "home" : function(e){
45451                 e.preventDefault();
45452                 
45453                 var curr = this.el.dom.selectionStart;
45454                 var lines = this.getValue().split("\n");
45455                 
45456                 if(!lines.length){
45457                     return;
45458                 }
45459                 
45460                 if(e.ctrlKey){
45461                     this.el.dom.setSelectionRange(0, 0);
45462                     return;
45463                 }
45464                 
45465                 var pos = 0;
45466                 
45467                 for (var i = 0; i < lines.length;i++) {
45468                     pos += lines[i].length;
45469                     
45470                     if(i != 0){
45471                         pos += 1;
45472                     }
45473                     
45474                     if(pos < curr){
45475                         continue;
45476                     }
45477                     
45478                     pos -= lines[i].length;
45479                     
45480                     break;
45481                 }
45482                 
45483                 if(!e.shiftKey){
45484                     this.el.dom.setSelectionRange(pos, pos);
45485                     return;
45486                 }
45487                 
45488                 this.el.dom.selectionStart = pos;
45489                 this.el.dom.selectionEnd = curr;
45490             },
45491             
45492             "end" : function(e){
45493                 e.preventDefault();
45494                 
45495                 var curr = this.el.dom.selectionStart;
45496                 var lines = this.getValue().split("\n");
45497                 
45498                 if(!lines.length){
45499                     return;
45500                 }
45501                 
45502                 if(e.ctrlKey){
45503                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45504                     return;
45505                 }
45506                 
45507                 var pos = 0;
45508                 
45509                 for (var i = 0; i < lines.length;i++) {
45510                     
45511                     pos += lines[i].length;
45512                     
45513                     if(i != 0){
45514                         pos += 1;
45515                     }
45516                     
45517                     if(pos < curr){
45518                         continue;
45519                     }
45520                     
45521                     break;
45522                 }
45523                 
45524                 if(!e.shiftKey){
45525                     this.el.dom.setSelectionRange(pos, pos);
45526                     return;
45527                 }
45528                 
45529                 this.el.dom.selectionStart = curr;
45530                 this.el.dom.selectionEnd = pos;
45531             },
45532
45533             scope : this,
45534
45535             doRelay : function(foo, bar, hname){
45536                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45537             },
45538
45539             forceKeyDown: true
45540         });
45541         
45542 //        if(this.autosave && this.w){
45543 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45544 //        }
45545     },
45546
45547     // private
45548     onResize : function(w, h)
45549     {
45550         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45551         var ew = false;
45552         var eh = false;
45553         
45554         if(this.el ){
45555             if(typeof w == 'number'){
45556                 var aw = w - this.wrap.getFrameWidth('lr');
45557                 this.el.setWidth(this.adjustWidth('textarea', aw));
45558                 ew = aw;
45559             }
45560             if(typeof h == 'number'){
45561                 var tbh = 0;
45562                 for (var i =0; i < this.toolbars.length;i++) {
45563                     // fixme - ask toolbars for heights?
45564                     tbh += this.toolbars[i].tb.el.getHeight();
45565                     if (this.toolbars[i].footer) {
45566                         tbh += this.toolbars[i].footer.el.getHeight();
45567                     }
45568                 }
45569                 
45570                 
45571                 
45572                 
45573                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45574                 ah -= 5; // knock a few pixes off for look..
45575 //                Roo.log(ah);
45576                 this.el.setHeight(this.adjustWidth('textarea', ah));
45577                 var eh = ah;
45578             }
45579         }
45580         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45581         this.editorcore.onResize(ew,eh);
45582         
45583     },
45584
45585     /**
45586      * Toggles the editor between standard and source edit mode.
45587      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45588      */
45589     toggleSourceEdit : function(sourceEditMode)
45590     {
45591         this.editorcore.toggleSourceEdit(sourceEditMode);
45592         
45593         if(this.editorcore.sourceEditMode){
45594             Roo.log('editor - showing textarea');
45595             
45596 //            Roo.log('in');
45597 //            Roo.log(this.syncValue());
45598             this.editorcore.syncValue();
45599             this.el.removeClass('x-hidden');
45600             this.el.dom.removeAttribute('tabIndex');
45601             this.el.focus();
45602             
45603             for (var i = 0; i < this.toolbars.length; i++) {
45604                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45605                     this.toolbars[i].tb.hide();
45606                     this.toolbars[i].footer.hide();
45607                 }
45608             }
45609             
45610         }else{
45611             Roo.log('editor - hiding textarea');
45612 //            Roo.log('out')
45613 //            Roo.log(this.pushValue()); 
45614             this.editorcore.pushValue();
45615             
45616             this.el.addClass('x-hidden');
45617             this.el.dom.setAttribute('tabIndex', -1);
45618             
45619             for (var i = 0; i < this.toolbars.length; i++) {
45620                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45621                     this.toolbars[i].tb.show();
45622                     this.toolbars[i].footer.show();
45623                 }
45624             }
45625             
45626             //this.deferFocus();
45627         }
45628         
45629         this.setSize(this.wrap.getSize());
45630         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45631         
45632         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45633     },
45634  
45635     // private (for BoxComponent)
45636     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45637
45638     // private (for BoxComponent)
45639     getResizeEl : function(){
45640         return this.wrap;
45641     },
45642
45643     // private (for BoxComponent)
45644     getPositionEl : function(){
45645         return this.wrap;
45646     },
45647
45648     // private
45649     initEvents : function(){
45650         this.originalValue = this.getValue();
45651     },
45652
45653     /**
45654      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45655      * @method
45656      */
45657     markInvalid : Roo.emptyFn,
45658     /**
45659      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45660      * @method
45661      */
45662     clearInvalid : Roo.emptyFn,
45663
45664     setValue : function(v){
45665         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45666         this.editorcore.pushValue();
45667     },
45668
45669      
45670     // private
45671     deferFocus : function(){
45672         this.focus.defer(10, this);
45673     },
45674
45675     // doc'ed in Field
45676     focus : function(){
45677         this.editorcore.focus();
45678         
45679     },
45680       
45681
45682     // private
45683     onDestroy : function(){
45684         
45685         
45686         
45687         if(this.rendered){
45688             
45689             for (var i =0; i < this.toolbars.length;i++) {
45690                 // fixme - ask toolbars for heights?
45691                 this.toolbars[i].onDestroy();
45692             }
45693             
45694             this.wrap.dom.innerHTML = '';
45695             this.wrap.remove();
45696         }
45697     },
45698
45699     // private
45700     onFirstFocus : function(){
45701         //Roo.log("onFirstFocus");
45702         this.editorcore.onFirstFocus();
45703          for (var i =0; i < this.toolbars.length;i++) {
45704             this.toolbars[i].onFirstFocus();
45705         }
45706         
45707     },
45708     
45709     // private
45710     syncValue : function()
45711     {
45712         this.editorcore.syncValue();
45713     },
45714     
45715     pushValue : function()
45716     {
45717         this.editorcore.pushValue();
45718     },
45719     
45720     setStylesheets : function(stylesheets)
45721     {
45722         this.editorcore.setStylesheets(stylesheets);
45723     },
45724     
45725     removeStylesheets : function()
45726     {
45727         this.editorcore.removeStylesheets();
45728     }
45729      
45730     
45731     // hide stuff that is not compatible
45732     /**
45733      * @event blur
45734      * @hide
45735      */
45736     /**
45737      * @event change
45738      * @hide
45739      */
45740     /**
45741      * @event focus
45742      * @hide
45743      */
45744     /**
45745      * @event specialkey
45746      * @hide
45747      */
45748     /**
45749      * @cfg {String} fieldClass @hide
45750      */
45751     /**
45752      * @cfg {String} focusClass @hide
45753      */
45754     /**
45755      * @cfg {String} autoCreate @hide
45756      */
45757     /**
45758      * @cfg {String} inputType @hide
45759      */
45760     /**
45761      * @cfg {String} invalidClass @hide
45762      */
45763     /**
45764      * @cfg {String} invalidText @hide
45765      */
45766     /**
45767      * @cfg {String} msgFx @hide
45768      */
45769     /**
45770      * @cfg {String} validateOnBlur @hide
45771      */
45772 });
45773  
45774     // <script type="text/javascript">
45775 /*
45776  * Based on
45777  * Ext JS Library 1.1.1
45778  * Copyright(c) 2006-2007, Ext JS, LLC.
45779  *  
45780  
45781  */
45782
45783 /**
45784  * @class Roo.form.HtmlEditorToolbar1
45785  * Basic Toolbar
45786  * 
45787  * Usage:
45788  *
45789  new Roo.form.HtmlEditor({
45790     ....
45791     toolbars : [
45792         new Roo.form.HtmlEditorToolbar1({
45793             disable : { fonts: 1 , format: 1, ..., ... , ...],
45794             btns : [ .... ]
45795         })
45796     }
45797      
45798  * 
45799  * @cfg {Object} disable List of elements to disable..
45800  * @cfg {Array} btns List of additional buttons.
45801  * 
45802  * 
45803  * NEEDS Extra CSS? 
45804  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45805  */
45806  
45807 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45808 {
45809     
45810     Roo.apply(this, config);
45811     
45812     // default disabled, based on 'good practice'..
45813     this.disable = this.disable || {};
45814     Roo.applyIf(this.disable, {
45815         fontSize : true,
45816         colors : true,
45817         specialElements : true
45818     });
45819     
45820     
45821     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45822     // dont call parent... till later.
45823 }
45824
45825 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45826     
45827     tb: false,
45828     
45829     rendered: false,
45830     
45831     editor : false,
45832     editorcore : false,
45833     /**
45834      * @cfg {Object} disable  List of toolbar elements to disable
45835          
45836      */
45837     disable : false,
45838     
45839     
45840      /**
45841      * @cfg {String} createLinkText The default text for the create link prompt
45842      */
45843     createLinkText : 'Please enter the URL for the link:',
45844     /**
45845      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45846      */
45847     defaultLinkValue : 'http:/'+'/',
45848    
45849     
45850       /**
45851      * @cfg {Array} fontFamilies An array of available font families
45852      */
45853     fontFamilies : [
45854         'Arial',
45855         'Courier New',
45856         'Tahoma',
45857         'Times New Roman',
45858         'Verdana'
45859     ],
45860     
45861     specialChars : [
45862            "&#169;",
45863           "&#174;",     
45864           "&#8482;",    
45865           "&#163;" ,    
45866          // "&#8212;",    
45867           "&#8230;",    
45868           "&#247;" ,    
45869         //  "&#225;" ,     ?? a acute?
45870            "&#8364;"    , //Euro
45871        //   "&#8220;"    ,
45872         //  "&#8221;"    ,
45873         //  "&#8226;"    ,
45874           "&#176;"  //   , // degrees
45875
45876          // "&#233;"     , // e ecute
45877          // "&#250;"     , // u ecute?
45878     ],
45879     
45880     specialElements : [
45881         {
45882             text: "Insert Table",
45883             xtype: 'MenuItem',
45884             xns : Roo.Menu,
45885             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45886                 
45887         },
45888         {    
45889             text: "Insert Image",
45890             xtype: 'MenuItem',
45891             xns : Roo.Menu,
45892             ihtml : '<img src="about:blank"/>'
45893             
45894         }
45895         
45896          
45897     ],
45898     
45899     
45900     inputElements : [ 
45901             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45902             "input:submit", "input:button", "select", "textarea", "label" ],
45903     formats : [
45904         ["p"] ,  
45905         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45906         ["pre"],[ "code"], 
45907         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45908         ['div'],['span'],
45909         ['sup'],['sub']
45910     ],
45911     
45912     cleanStyles : [
45913         "font-size"
45914     ],
45915      /**
45916      * @cfg {String} defaultFont default font to use.
45917      */
45918     defaultFont: 'tahoma',
45919    
45920     fontSelect : false,
45921     
45922     
45923     formatCombo : false,
45924     
45925     init : function(editor)
45926     {
45927         this.editor = editor;
45928         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45929         var editorcore = this.editorcore;
45930         
45931         var _t = this;
45932         
45933         var fid = editorcore.frameId;
45934         var etb = this;
45935         function btn(id, toggle, handler){
45936             var xid = fid + '-'+ id ;
45937             return {
45938                 id : xid,
45939                 cmd : id,
45940                 cls : 'x-btn-icon x-edit-'+id,
45941                 enableToggle:toggle !== false,
45942                 scope: _t, // was editor...
45943                 handler:handler||_t.relayBtnCmd,
45944                 clickEvent:'mousedown',
45945                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45946                 tabIndex:-1
45947             };
45948         }
45949         
45950         
45951         
45952         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45953         this.tb = tb;
45954          // stop form submits
45955         tb.el.on('click', function(e){
45956             e.preventDefault(); // what does this do?
45957         });
45958
45959         if(!this.disable.font) { // && !Roo.isSafari){
45960             /* why no safari for fonts 
45961             editor.fontSelect = tb.el.createChild({
45962                 tag:'select',
45963                 tabIndex: -1,
45964                 cls:'x-font-select',
45965                 html: this.createFontOptions()
45966             });
45967             
45968             editor.fontSelect.on('change', function(){
45969                 var font = editor.fontSelect.dom.value;
45970                 editor.relayCmd('fontname', font);
45971                 editor.deferFocus();
45972             }, editor);
45973             
45974             tb.add(
45975                 editor.fontSelect.dom,
45976                 '-'
45977             );
45978             */
45979             
45980         };
45981         if(!this.disable.formats){
45982             this.formatCombo = new Roo.form.ComboBox({
45983                 store: new Roo.data.SimpleStore({
45984                     id : 'tag',
45985                     fields: ['tag'],
45986                     data : this.formats // from states.js
45987                 }),
45988                 blockFocus : true,
45989                 name : '',
45990                 //autoCreate : {tag: "div",  size: "20"},
45991                 displayField:'tag',
45992                 typeAhead: false,
45993                 mode: 'local',
45994                 editable : false,
45995                 triggerAction: 'all',
45996                 emptyText:'Add tag',
45997                 selectOnFocus:true,
45998                 width:135,
45999                 listeners : {
46000                     'select': function(c, r, i) {
46001                         editorcore.insertTag(r.get('tag'));
46002                         editor.focus();
46003                     }
46004                 }
46005
46006             });
46007             tb.addField(this.formatCombo);
46008             
46009         }
46010         
46011         if(!this.disable.format){
46012             tb.add(
46013                 btn('bold'),
46014                 btn('italic'),
46015                 btn('underline'),
46016                 btn('strikethrough')
46017             );
46018         };
46019         if(!this.disable.fontSize){
46020             tb.add(
46021                 '-',
46022                 
46023                 
46024                 btn('increasefontsize', false, editorcore.adjustFont),
46025                 btn('decreasefontsize', false, editorcore.adjustFont)
46026             );
46027         };
46028         
46029         
46030         if(!this.disable.colors){
46031             tb.add(
46032                 '-', {
46033                     id:editorcore.frameId +'-forecolor',
46034                     cls:'x-btn-icon x-edit-forecolor',
46035                     clickEvent:'mousedown',
46036                     tooltip: this.buttonTips['forecolor'] || undefined,
46037                     tabIndex:-1,
46038                     menu : new Roo.menu.ColorMenu({
46039                         allowReselect: true,
46040                         focus: Roo.emptyFn,
46041                         value:'000000',
46042                         plain:true,
46043                         selectHandler: function(cp, color){
46044                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46045                             editor.deferFocus();
46046                         },
46047                         scope: editorcore,
46048                         clickEvent:'mousedown'
46049                     })
46050                 }, {
46051                     id:editorcore.frameId +'backcolor',
46052                     cls:'x-btn-icon x-edit-backcolor',
46053                     clickEvent:'mousedown',
46054                     tooltip: this.buttonTips['backcolor'] || undefined,
46055                     tabIndex:-1,
46056                     menu : new Roo.menu.ColorMenu({
46057                         focus: Roo.emptyFn,
46058                         value:'FFFFFF',
46059                         plain:true,
46060                         allowReselect: true,
46061                         selectHandler: function(cp, color){
46062                             if(Roo.isGecko){
46063                                 editorcore.execCmd('useCSS', false);
46064                                 editorcore.execCmd('hilitecolor', color);
46065                                 editorcore.execCmd('useCSS', true);
46066                                 editor.deferFocus();
46067                             }else{
46068                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46069                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46070                                 editor.deferFocus();
46071                             }
46072                         },
46073                         scope:editorcore,
46074                         clickEvent:'mousedown'
46075                     })
46076                 }
46077             );
46078         };
46079         // now add all the items...
46080         
46081
46082         if(!this.disable.alignments){
46083             tb.add(
46084                 '-',
46085                 btn('justifyleft'),
46086                 btn('justifycenter'),
46087                 btn('justifyright')
46088             );
46089         };
46090
46091         //if(!Roo.isSafari){
46092             if(!this.disable.links){
46093                 tb.add(
46094                     '-',
46095                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46096                 );
46097             };
46098
46099             if(!this.disable.lists){
46100                 tb.add(
46101                     '-',
46102                     btn('insertorderedlist'),
46103                     btn('insertunorderedlist')
46104                 );
46105             }
46106             if(!this.disable.sourceEdit){
46107                 tb.add(
46108                     '-',
46109                     btn('sourceedit', true, function(btn){
46110                         this.toggleSourceEdit(btn.pressed);
46111                     })
46112                 );
46113             }
46114         //}
46115         
46116         var smenu = { };
46117         // special menu.. - needs to be tidied up..
46118         if (!this.disable.special) {
46119             smenu = {
46120                 text: "&#169;",
46121                 cls: 'x-edit-none',
46122                 
46123                 menu : {
46124                     items : []
46125                 }
46126             };
46127             for (var i =0; i < this.specialChars.length; i++) {
46128                 smenu.menu.items.push({
46129                     
46130                     html: this.specialChars[i],
46131                     handler: function(a,b) {
46132                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46133                         //editor.insertAtCursor(a.html);
46134                         
46135                     },
46136                     tabIndex:-1
46137                 });
46138             }
46139             
46140             
46141             tb.add(smenu);
46142             
46143             
46144         }
46145         
46146         var cmenu = { };
46147         if (!this.disable.cleanStyles) {
46148             cmenu = {
46149                 cls: 'x-btn-icon x-btn-clear',
46150                 
46151                 menu : {
46152                     items : []
46153                 }
46154             };
46155             for (var i =0; i < this.cleanStyles.length; i++) {
46156                 cmenu.menu.items.push({
46157                     actiontype : this.cleanStyles[i],
46158                     html: 'Remove ' + this.cleanStyles[i],
46159                     handler: function(a,b) {
46160 //                        Roo.log(a);
46161 //                        Roo.log(b);
46162                         var c = Roo.get(editorcore.doc.body);
46163                         c.select('[style]').each(function(s) {
46164                             s.dom.style.removeProperty(a.actiontype);
46165                         });
46166                         editorcore.syncValue();
46167                     },
46168                     tabIndex:-1
46169                 });
46170             }
46171              cmenu.menu.items.push({
46172                 actiontype : 'tablewidths',
46173                 html: 'Remove Table Widths',
46174                 handler: function(a,b) {
46175                     editorcore.cleanTableWidths();
46176                     editorcore.syncValue();
46177                 },
46178                 tabIndex:-1
46179             });
46180             cmenu.menu.items.push({
46181                 actiontype : 'word',
46182                 html: 'Remove MS Word Formating',
46183                 handler: function(a,b) {
46184                     editorcore.cleanWord();
46185                     editorcore.syncValue();
46186                 },
46187                 tabIndex:-1
46188             });
46189             
46190             cmenu.menu.items.push({
46191                 actiontype : 'all',
46192                 html: 'Remove All Styles',
46193                 handler: function(a,b) {
46194                     
46195                     var c = Roo.get(editorcore.doc.body);
46196                     c.select('[style]').each(function(s) {
46197                         s.dom.removeAttribute('style');
46198                     });
46199                     editorcore.syncValue();
46200                 },
46201                 tabIndex:-1
46202             });
46203             
46204             cmenu.menu.items.push({
46205                 actiontype : 'all',
46206                 html: 'Remove All CSS Classes',
46207                 handler: function(a,b) {
46208                     
46209                     var c = Roo.get(editorcore.doc.body);
46210                     c.select('[class]').each(function(s) {
46211                         s.dom.removeAttribute('class');
46212                     });
46213                     editorcore.cleanWord();
46214                     editorcore.syncValue();
46215                 },
46216                 tabIndex:-1
46217             });
46218             
46219              cmenu.menu.items.push({
46220                 actiontype : 'tidy',
46221                 html: 'Tidy HTML Source',
46222                 handler: function(a,b) {
46223                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46224                     editorcore.syncValue();
46225                 },
46226                 tabIndex:-1
46227             });
46228             
46229             
46230             tb.add(cmenu);
46231         }
46232          
46233         if (!this.disable.specialElements) {
46234             var semenu = {
46235                 text: "Other;",
46236                 cls: 'x-edit-none',
46237                 menu : {
46238                     items : []
46239                 }
46240             };
46241             for (var i =0; i < this.specialElements.length; i++) {
46242                 semenu.menu.items.push(
46243                     Roo.apply({ 
46244                         handler: function(a,b) {
46245                             editor.insertAtCursor(this.ihtml);
46246                         }
46247                     }, this.specialElements[i])
46248                 );
46249                     
46250             }
46251             
46252             tb.add(semenu);
46253             
46254             
46255         }
46256          
46257         
46258         if (this.btns) {
46259             for(var i =0; i< this.btns.length;i++) {
46260                 var b = Roo.factory(this.btns[i],Roo.form);
46261                 b.cls =  'x-edit-none';
46262                 
46263                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46264                     b.cls += ' x-init-enable';
46265                 }
46266                 
46267                 b.scope = editorcore;
46268                 tb.add(b);
46269             }
46270         
46271         }
46272         
46273         
46274         
46275         // disable everything...
46276         
46277         this.tb.items.each(function(item){
46278             
46279            if(
46280                 item.id != editorcore.frameId+ '-sourceedit' && 
46281                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46282             ){
46283                 
46284                 item.disable();
46285             }
46286         });
46287         this.rendered = true;
46288         
46289         // the all the btns;
46290         editor.on('editorevent', this.updateToolbar, this);
46291         // other toolbars need to implement this..
46292         //editor.on('editmodechange', this.updateToolbar, this);
46293     },
46294     
46295     
46296     relayBtnCmd : function(btn) {
46297         this.editorcore.relayCmd(btn.cmd);
46298     },
46299     // private used internally
46300     createLink : function(){
46301         Roo.log("create link?");
46302         var url = prompt(this.createLinkText, this.defaultLinkValue);
46303         if(url && url != 'http:/'+'/'){
46304             this.editorcore.relayCmd('createlink', url);
46305         }
46306     },
46307
46308     
46309     /**
46310      * Protected method that will not generally be called directly. It triggers
46311      * a toolbar update by reading the markup state of the current selection in the editor.
46312      */
46313     updateToolbar: function(){
46314
46315         if(!this.editorcore.activated){
46316             this.editor.onFirstFocus();
46317             return;
46318         }
46319
46320         var btns = this.tb.items.map, 
46321             doc = this.editorcore.doc,
46322             frameId = this.editorcore.frameId;
46323
46324         if(!this.disable.font && !Roo.isSafari){
46325             /*
46326             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46327             if(name != this.fontSelect.dom.value){
46328                 this.fontSelect.dom.value = name;
46329             }
46330             */
46331         }
46332         if(!this.disable.format){
46333             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46334             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46335             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46336             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46337         }
46338         if(!this.disable.alignments){
46339             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46340             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46341             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46342         }
46343         if(!Roo.isSafari && !this.disable.lists){
46344             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46345             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46346         }
46347         
46348         var ans = this.editorcore.getAllAncestors();
46349         if (this.formatCombo) {
46350             
46351             
46352             var store = this.formatCombo.store;
46353             this.formatCombo.setValue("");
46354             for (var i =0; i < ans.length;i++) {
46355                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46356                     // select it..
46357                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46358                     break;
46359                 }
46360             }
46361         }
46362         
46363         
46364         
46365         // hides menus... - so this cant be on a menu...
46366         Roo.menu.MenuMgr.hideAll();
46367
46368         //this.editorsyncValue();
46369     },
46370    
46371     
46372     createFontOptions : function(){
46373         var buf = [], fs = this.fontFamilies, ff, lc;
46374         
46375         
46376         
46377         for(var i = 0, len = fs.length; i< len; i++){
46378             ff = fs[i];
46379             lc = ff.toLowerCase();
46380             buf.push(
46381                 '<option value="',lc,'" style="font-family:',ff,';"',
46382                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46383                     ff,
46384                 '</option>'
46385             );
46386         }
46387         return buf.join('');
46388     },
46389     
46390     toggleSourceEdit : function(sourceEditMode){
46391         
46392         Roo.log("toolbar toogle");
46393         if(sourceEditMode === undefined){
46394             sourceEditMode = !this.sourceEditMode;
46395         }
46396         this.sourceEditMode = sourceEditMode === true;
46397         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46398         // just toggle the button?
46399         if(btn.pressed !== this.sourceEditMode){
46400             btn.toggle(this.sourceEditMode);
46401             return;
46402         }
46403         
46404         if(sourceEditMode){
46405             Roo.log("disabling buttons");
46406             this.tb.items.each(function(item){
46407                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46408                     item.disable();
46409                 }
46410             });
46411           
46412         }else{
46413             Roo.log("enabling buttons");
46414             if(this.editorcore.initialized){
46415                 this.tb.items.each(function(item){
46416                     item.enable();
46417                 });
46418             }
46419             
46420         }
46421         Roo.log("calling toggole on editor");
46422         // tell the editor that it's been pressed..
46423         this.editor.toggleSourceEdit(sourceEditMode);
46424        
46425     },
46426      /**
46427      * Object collection of toolbar tooltips for the buttons in the editor. The key
46428      * is the command id associated with that button and the value is a valid QuickTips object.
46429      * For example:
46430 <pre><code>
46431 {
46432     bold : {
46433         title: 'Bold (Ctrl+B)',
46434         text: 'Make the selected text bold.',
46435         cls: 'x-html-editor-tip'
46436     },
46437     italic : {
46438         title: 'Italic (Ctrl+I)',
46439         text: 'Make the selected text italic.',
46440         cls: 'x-html-editor-tip'
46441     },
46442     ...
46443 </code></pre>
46444     * @type Object
46445      */
46446     buttonTips : {
46447         bold : {
46448             title: 'Bold (Ctrl+B)',
46449             text: 'Make the selected text bold.',
46450             cls: 'x-html-editor-tip'
46451         },
46452         italic : {
46453             title: 'Italic (Ctrl+I)',
46454             text: 'Make the selected text italic.',
46455             cls: 'x-html-editor-tip'
46456         },
46457         underline : {
46458             title: 'Underline (Ctrl+U)',
46459             text: 'Underline the selected text.',
46460             cls: 'x-html-editor-tip'
46461         },
46462         strikethrough : {
46463             title: 'Strikethrough',
46464             text: 'Strikethrough the selected text.',
46465             cls: 'x-html-editor-tip'
46466         },
46467         increasefontsize : {
46468             title: 'Grow Text',
46469             text: 'Increase the font size.',
46470             cls: 'x-html-editor-tip'
46471         },
46472         decreasefontsize : {
46473             title: 'Shrink Text',
46474             text: 'Decrease the font size.',
46475             cls: 'x-html-editor-tip'
46476         },
46477         backcolor : {
46478             title: 'Text Highlight Color',
46479             text: 'Change the background color of the selected text.',
46480             cls: 'x-html-editor-tip'
46481         },
46482         forecolor : {
46483             title: 'Font Color',
46484             text: 'Change the color of the selected text.',
46485             cls: 'x-html-editor-tip'
46486         },
46487         justifyleft : {
46488             title: 'Align Text Left',
46489             text: 'Align text to the left.',
46490             cls: 'x-html-editor-tip'
46491         },
46492         justifycenter : {
46493             title: 'Center Text',
46494             text: 'Center text in the editor.',
46495             cls: 'x-html-editor-tip'
46496         },
46497         justifyright : {
46498             title: 'Align Text Right',
46499             text: 'Align text to the right.',
46500             cls: 'x-html-editor-tip'
46501         },
46502         insertunorderedlist : {
46503             title: 'Bullet List',
46504             text: 'Start a bulleted list.',
46505             cls: 'x-html-editor-tip'
46506         },
46507         insertorderedlist : {
46508             title: 'Numbered List',
46509             text: 'Start a numbered list.',
46510             cls: 'x-html-editor-tip'
46511         },
46512         createlink : {
46513             title: 'Hyperlink',
46514             text: 'Make the selected text a hyperlink.',
46515             cls: 'x-html-editor-tip'
46516         },
46517         sourceedit : {
46518             title: 'Source Edit',
46519             text: 'Switch to source editing mode.',
46520             cls: 'x-html-editor-tip'
46521         }
46522     },
46523     // private
46524     onDestroy : function(){
46525         if(this.rendered){
46526             
46527             this.tb.items.each(function(item){
46528                 if(item.menu){
46529                     item.menu.removeAll();
46530                     if(item.menu.el){
46531                         item.menu.el.destroy();
46532                     }
46533                 }
46534                 item.destroy();
46535             });
46536              
46537         }
46538     },
46539     onFirstFocus: function() {
46540         this.tb.items.each(function(item){
46541            item.enable();
46542         });
46543     }
46544 });
46545
46546
46547
46548
46549 // <script type="text/javascript">
46550 /*
46551  * Based on
46552  * Ext JS Library 1.1.1
46553  * Copyright(c) 2006-2007, Ext JS, LLC.
46554  *  
46555  
46556  */
46557
46558  
46559 /**
46560  * @class Roo.form.HtmlEditor.ToolbarContext
46561  * Context Toolbar
46562  * 
46563  * Usage:
46564  *
46565  new Roo.form.HtmlEditor({
46566     ....
46567     toolbars : [
46568         { xtype: 'ToolbarStandard', styles : {} }
46569         { xtype: 'ToolbarContext', disable : {} }
46570     ]
46571 })
46572
46573      
46574  * 
46575  * @config : {Object} disable List of elements to disable.. (not done yet.)
46576  * @config : {Object} styles  Map of styles available.
46577  * 
46578  */
46579
46580 Roo.form.HtmlEditor.ToolbarContext = function(config)
46581 {
46582     
46583     Roo.apply(this, config);
46584     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46585     // dont call parent... till later.
46586     this.styles = this.styles || {};
46587 }
46588
46589  
46590
46591 Roo.form.HtmlEditor.ToolbarContext.types = {
46592     'IMG' : {
46593         width : {
46594             title: "Width",
46595             width: 40
46596         },
46597         height:  {
46598             title: "Height",
46599             width: 40
46600         },
46601         align: {
46602             title: "Align",
46603             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46604             width : 80
46605             
46606         },
46607         border: {
46608             title: "Border",
46609             width: 40
46610         },
46611         alt: {
46612             title: "Alt",
46613             width: 120
46614         },
46615         src : {
46616             title: "Src",
46617             width: 220
46618         }
46619         
46620     },
46621     'A' : {
46622         name : {
46623             title: "Name",
46624             width: 50
46625         },
46626         target:  {
46627             title: "Target",
46628             width: 120
46629         },
46630         href:  {
46631             title: "Href",
46632             width: 220
46633         } // border?
46634         
46635     },
46636     'TABLE' : {
46637         rows : {
46638             title: "Rows",
46639             width: 20
46640         },
46641         cols : {
46642             title: "Cols",
46643             width: 20
46644         },
46645         width : {
46646             title: "Width",
46647             width: 40
46648         },
46649         height : {
46650             title: "Height",
46651             width: 40
46652         },
46653         border : {
46654             title: "Border",
46655             width: 20
46656         }
46657     },
46658     'TD' : {
46659         width : {
46660             title: "Width",
46661             width: 40
46662         },
46663         height : {
46664             title: "Height",
46665             width: 40
46666         },   
46667         align: {
46668             title: "Align",
46669             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46670             width: 80
46671         },
46672         valign: {
46673             title: "Valign",
46674             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46675             width: 80
46676         },
46677         colspan: {
46678             title: "Colspan",
46679             width: 20
46680             
46681         },
46682          'font-family'  : {
46683             title : "Font",
46684             style : 'fontFamily',
46685             displayField: 'display',
46686             optname : 'font-family',
46687             width: 140
46688         }
46689     },
46690     'INPUT' : {
46691         name : {
46692             title: "name",
46693             width: 120
46694         },
46695         value : {
46696             title: "Value",
46697             width: 120
46698         },
46699         width : {
46700             title: "Width",
46701             width: 40
46702         }
46703     },
46704     'LABEL' : {
46705         'for' : {
46706             title: "For",
46707             width: 120
46708         }
46709     },
46710     'TEXTAREA' : {
46711           name : {
46712             title: "name",
46713             width: 120
46714         },
46715         rows : {
46716             title: "Rows",
46717             width: 20
46718         },
46719         cols : {
46720             title: "Cols",
46721             width: 20
46722         }
46723     },
46724     'SELECT' : {
46725         name : {
46726             title: "name",
46727             width: 120
46728         },
46729         selectoptions : {
46730             title: "Options",
46731             width: 200
46732         }
46733     },
46734     
46735     // should we really allow this??
46736     // should this just be 
46737     'BODY' : {
46738         title : {
46739             title: "Title",
46740             width: 200,
46741             disabled : true
46742         }
46743     },
46744     'SPAN' : {
46745         'font-family'  : {
46746             title : "Font",
46747             style : 'fontFamily',
46748             displayField: 'display',
46749             optname : 'font-family',
46750             width: 140
46751         }
46752     },
46753     'DIV' : {
46754         'font-family'  : {
46755             title : "Font",
46756             style : 'fontFamily',
46757             displayField: 'display',
46758             optname : 'font-family',
46759             width: 140
46760         }
46761     },
46762      'P' : {
46763         'font-family'  : {
46764             title : "Font",
46765             style : 'fontFamily',
46766             displayField: 'display',
46767             optname : 'font-family',
46768             width: 140
46769         }
46770     },
46771     
46772     '*' : {
46773         // empty..
46774     }
46775
46776 };
46777
46778 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46779 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46780
46781 Roo.form.HtmlEditor.ToolbarContext.options = {
46782         'font-family'  : [ 
46783                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46784                 [ 'Courier New', 'Courier New'],
46785                 [ 'Tahoma', 'Tahoma'],
46786                 [ 'Times New Roman,serif', 'Times'],
46787                 [ 'Verdana','Verdana' ]
46788         ]
46789 };
46790
46791 // fixme - these need to be configurable..
46792  
46793
46794 //Roo.form.HtmlEditor.ToolbarContext.types
46795
46796
46797 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46798     
46799     tb: false,
46800     
46801     rendered: false,
46802     
46803     editor : false,
46804     editorcore : false,
46805     /**
46806      * @cfg {Object} disable  List of toolbar elements to disable
46807          
46808      */
46809     disable : false,
46810     /**
46811      * @cfg {Object} styles List of styles 
46812      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46813      *
46814      * These must be defined in the page, so they get rendered correctly..
46815      * .headline { }
46816      * TD.underline { }
46817      * 
46818      */
46819     styles : false,
46820     
46821     options: false,
46822     
46823     toolbars : false,
46824     
46825     init : function(editor)
46826     {
46827         this.editor = editor;
46828         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46829         var editorcore = this.editorcore;
46830         
46831         var fid = editorcore.frameId;
46832         var etb = this;
46833         function btn(id, toggle, handler){
46834             var xid = fid + '-'+ id ;
46835             return {
46836                 id : xid,
46837                 cmd : id,
46838                 cls : 'x-btn-icon x-edit-'+id,
46839                 enableToggle:toggle !== false,
46840                 scope: editorcore, // was editor...
46841                 handler:handler||editorcore.relayBtnCmd,
46842                 clickEvent:'mousedown',
46843                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46844                 tabIndex:-1
46845             };
46846         }
46847         // create a new element.
46848         var wdiv = editor.wrap.createChild({
46849                 tag: 'div'
46850             }, editor.wrap.dom.firstChild.nextSibling, true);
46851         
46852         // can we do this more than once??
46853         
46854          // stop form submits
46855       
46856  
46857         // disable everything...
46858         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46859         this.toolbars = {};
46860            
46861         for (var i in  ty) {
46862           
46863             this.toolbars[i] = this.buildToolbar(ty[i],i);
46864         }
46865         this.tb = this.toolbars.BODY;
46866         this.tb.el.show();
46867         this.buildFooter();
46868         this.footer.show();
46869         editor.on('hide', function( ) { this.footer.hide() }, this);
46870         editor.on('show', function( ) { this.footer.show() }, this);
46871         
46872          
46873         this.rendered = true;
46874         
46875         // the all the btns;
46876         editor.on('editorevent', this.updateToolbar, this);
46877         // other toolbars need to implement this..
46878         //editor.on('editmodechange', this.updateToolbar, this);
46879     },
46880     
46881     
46882     
46883     /**
46884      * Protected method that will not generally be called directly. It triggers
46885      * a toolbar update by reading the markup state of the current selection in the editor.
46886      *
46887      * Note you can force an update by calling on('editorevent', scope, false)
46888      */
46889     updateToolbar: function(editor,ev,sel){
46890
46891         //Roo.log(ev);
46892         // capture mouse up - this is handy for selecting images..
46893         // perhaps should go somewhere else...
46894         if(!this.editorcore.activated){
46895              this.editor.onFirstFocus();
46896             return;
46897         }
46898         
46899         
46900         
46901         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46902         // selectNode - might want to handle IE?
46903         if (ev &&
46904             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46905             ev.target && ev.target.tagName == 'IMG') {
46906             // they have click on an image...
46907             // let's see if we can change the selection...
46908             sel = ev.target;
46909          
46910               var nodeRange = sel.ownerDocument.createRange();
46911             try {
46912                 nodeRange.selectNode(sel);
46913             } catch (e) {
46914                 nodeRange.selectNodeContents(sel);
46915             }
46916             //nodeRange.collapse(true);
46917             var s = this.editorcore.win.getSelection();
46918             s.removeAllRanges();
46919             s.addRange(nodeRange);
46920         }  
46921         
46922       
46923         var updateFooter = sel ? false : true;
46924         
46925         
46926         var ans = this.editorcore.getAllAncestors();
46927         
46928         // pick
46929         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46930         
46931         if (!sel) { 
46932             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46933             sel = sel ? sel : this.editorcore.doc.body;
46934             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46935             
46936         }
46937         // pick a menu that exists..
46938         var tn = sel.tagName.toUpperCase();
46939         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46940         
46941         tn = sel.tagName.toUpperCase();
46942         
46943         var lastSel = this.tb.selectedNode;
46944         
46945         this.tb.selectedNode = sel;
46946         
46947         // if current menu does not match..
46948         
46949         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46950                 
46951             this.tb.el.hide();
46952             ///console.log("show: " + tn);
46953             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46954             this.tb.el.show();
46955             // update name
46956             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46957             
46958             
46959             // update attributes
46960             if (this.tb.fields) {
46961                 this.tb.fields.each(function(e) {
46962                     if (e.stylename) {
46963                         e.setValue(sel.style[e.stylename]);
46964                         return;
46965                     } 
46966                    e.setValue(sel.getAttribute(e.attrname));
46967                 });
46968             }
46969             
46970             var hasStyles = false;
46971             for(var i in this.styles) {
46972                 hasStyles = true;
46973                 break;
46974             }
46975             
46976             // update styles
46977             if (hasStyles) { 
46978                 var st = this.tb.fields.item(0);
46979                 
46980                 st.store.removeAll();
46981                
46982                 
46983                 var cn = sel.className.split(/\s+/);
46984                 
46985                 var avs = [];
46986                 if (this.styles['*']) {
46987                     
46988                     Roo.each(this.styles['*'], function(v) {
46989                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46990                     });
46991                 }
46992                 if (this.styles[tn]) { 
46993                     Roo.each(this.styles[tn], function(v) {
46994                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46995                     });
46996                 }
46997                 
46998                 st.store.loadData(avs);
46999                 st.collapse();
47000                 st.setValue(cn);
47001             }
47002             // flag our selected Node.
47003             this.tb.selectedNode = sel;
47004            
47005            
47006             Roo.menu.MenuMgr.hideAll();
47007
47008         }
47009         
47010         if (!updateFooter) {
47011             //this.footDisp.dom.innerHTML = ''; 
47012             return;
47013         }
47014         // update the footer
47015         //
47016         var html = '';
47017         
47018         this.footerEls = ans.reverse();
47019         Roo.each(this.footerEls, function(a,i) {
47020             if (!a) { return; }
47021             html += html.length ? ' &gt; '  :  '';
47022             
47023             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47024             
47025         });
47026        
47027         // 
47028         var sz = this.footDisp.up('td').getSize();
47029         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47030         this.footDisp.dom.style.marginLeft = '5px';
47031         
47032         this.footDisp.dom.style.overflow = 'hidden';
47033         
47034         this.footDisp.dom.innerHTML = html;
47035             
47036         //this.editorsyncValue();
47037     },
47038      
47039     
47040    
47041        
47042     // private
47043     onDestroy : function(){
47044         if(this.rendered){
47045             
47046             this.tb.items.each(function(item){
47047                 if(item.menu){
47048                     item.menu.removeAll();
47049                     if(item.menu.el){
47050                         item.menu.el.destroy();
47051                     }
47052                 }
47053                 item.destroy();
47054             });
47055              
47056         }
47057     },
47058     onFirstFocus: function() {
47059         // need to do this for all the toolbars..
47060         this.tb.items.each(function(item){
47061            item.enable();
47062         });
47063     },
47064     buildToolbar: function(tlist, nm)
47065     {
47066         var editor = this.editor;
47067         var editorcore = this.editorcore;
47068          // create a new element.
47069         var wdiv = editor.wrap.createChild({
47070                 tag: 'div'
47071             }, editor.wrap.dom.firstChild.nextSibling, true);
47072         
47073        
47074         var tb = new Roo.Toolbar(wdiv);
47075         // add the name..
47076         
47077         tb.add(nm+ ":&nbsp;");
47078         
47079         var styles = [];
47080         for(var i in this.styles) {
47081             styles.push(i);
47082         }
47083         
47084         // styles...
47085         if (styles && styles.length) {
47086             
47087             // this needs a multi-select checkbox...
47088             tb.addField( new Roo.form.ComboBox({
47089                 store: new Roo.data.SimpleStore({
47090                     id : 'val',
47091                     fields: ['val', 'selected'],
47092                     data : [] 
47093                 }),
47094                 name : '-roo-edit-className',
47095                 attrname : 'className',
47096                 displayField: 'val',
47097                 typeAhead: false,
47098                 mode: 'local',
47099                 editable : false,
47100                 triggerAction: 'all',
47101                 emptyText:'Select Style',
47102                 selectOnFocus:true,
47103                 width: 130,
47104                 listeners : {
47105                     'select': function(c, r, i) {
47106                         // initial support only for on class per el..
47107                         tb.selectedNode.className =  r ? r.get('val') : '';
47108                         editorcore.syncValue();
47109                     }
47110                 }
47111     
47112             }));
47113         }
47114         
47115         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47116         var tbops = tbc.options;
47117         
47118         for (var i in tlist) {
47119             
47120             var item = tlist[i];
47121             tb.add(item.title + ":&nbsp;");
47122             
47123             
47124             //optname == used so you can configure the options available..
47125             var opts = item.opts ? item.opts : false;
47126             if (item.optname) {
47127                 opts = tbops[item.optname];
47128            
47129             }
47130             
47131             if (opts) {
47132                 // opts == pulldown..
47133                 tb.addField( new Roo.form.ComboBox({
47134                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47135                         id : 'val',
47136                         fields: ['val', 'display'],
47137                         data : opts  
47138                     }),
47139                     name : '-roo-edit-' + i,
47140                     attrname : i,
47141                     stylename : item.style ? item.style : false,
47142                     displayField: item.displayField ? item.displayField : 'val',
47143                     valueField :  'val',
47144                     typeAhead: false,
47145                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47146                     editable : false,
47147                     triggerAction: 'all',
47148                     emptyText:'Select',
47149                     selectOnFocus:true,
47150                     width: item.width ? item.width  : 130,
47151                     listeners : {
47152                         'select': function(c, r, i) {
47153                             if (c.stylename) {
47154                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47155                                 return;
47156                             }
47157                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47158                         }
47159                     }
47160
47161                 }));
47162                 continue;
47163                     
47164                  
47165                 
47166                 tb.addField( new Roo.form.TextField({
47167                     name: i,
47168                     width: 100,
47169                     //allowBlank:false,
47170                     value: ''
47171                 }));
47172                 continue;
47173             }
47174             tb.addField( new Roo.form.TextField({
47175                 name: '-roo-edit-' + i,
47176                 attrname : i,
47177                 
47178                 width: item.width,
47179                 //allowBlank:true,
47180                 value: '',
47181                 listeners: {
47182                     'change' : function(f, nv, ov) {
47183                         tb.selectedNode.setAttribute(f.attrname, nv);
47184                         editorcore.syncValue();
47185                     }
47186                 }
47187             }));
47188              
47189         }
47190         
47191         var _this = this;
47192         
47193         if(nm == 'BODY'){
47194             tb.addSeparator();
47195         
47196             tb.addButton( {
47197                 text: 'Stylesheets',
47198
47199                 listeners : {
47200                     click : function ()
47201                     {
47202                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47203                     }
47204                 }
47205             });
47206         }
47207         
47208         tb.addFill();
47209         tb.addButton( {
47210             text: 'Remove Tag',
47211     
47212             listeners : {
47213                 click : function ()
47214                 {
47215                     // remove
47216                     // undo does not work.
47217                      
47218                     var sn = tb.selectedNode;
47219                     
47220                     var pn = sn.parentNode;
47221                     
47222                     var stn =  sn.childNodes[0];
47223                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47224                     while (sn.childNodes.length) {
47225                         var node = sn.childNodes[0];
47226                         sn.removeChild(node);
47227                         //Roo.log(node);
47228                         pn.insertBefore(node, sn);
47229                         
47230                     }
47231                     pn.removeChild(sn);
47232                     var range = editorcore.createRange();
47233         
47234                     range.setStart(stn,0);
47235                     range.setEnd(en,0); //????
47236                     //range.selectNode(sel);
47237                     
47238                     
47239                     var selection = editorcore.getSelection();
47240                     selection.removeAllRanges();
47241                     selection.addRange(range);
47242                     
47243                     
47244                     
47245                     //_this.updateToolbar(null, null, pn);
47246                     _this.updateToolbar(null, null, null);
47247                     _this.footDisp.dom.innerHTML = ''; 
47248                 }
47249             }
47250             
47251                     
47252                 
47253             
47254         });
47255         
47256         
47257         tb.el.on('click', function(e){
47258             e.preventDefault(); // what does this do?
47259         });
47260         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47261         tb.el.hide();
47262         tb.name = nm;
47263         // dont need to disable them... as they will get hidden
47264         return tb;
47265          
47266         
47267     },
47268     buildFooter : function()
47269     {
47270         
47271         var fel = this.editor.wrap.createChild();
47272         this.footer = new Roo.Toolbar(fel);
47273         // toolbar has scrolly on left / right?
47274         var footDisp= new Roo.Toolbar.Fill();
47275         var _t = this;
47276         this.footer.add(
47277             {
47278                 text : '&lt;',
47279                 xtype: 'Button',
47280                 handler : function() {
47281                     _t.footDisp.scrollTo('left',0,true)
47282                 }
47283             }
47284         );
47285         this.footer.add( footDisp );
47286         this.footer.add( 
47287             {
47288                 text : '&gt;',
47289                 xtype: 'Button',
47290                 handler : function() {
47291                     // no animation..
47292                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47293                 }
47294             }
47295         );
47296         var fel = Roo.get(footDisp.el);
47297         fel.addClass('x-editor-context');
47298         this.footDispWrap = fel; 
47299         this.footDispWrap.overflow  = 'hidden';
47300         
47301         this.footDisp = fel.createChild();
47302         this.footDispWrap.on('click', this.onContextClick, this)
47303         
47304         
47305     },
47306     onContextClick : function (ev,dom)
47307     {
47308         ev.preventDefault();
47309         var  cn = dom.className;
47310         //Roo.log(cn);
47311         if (!cn.match(/x-ed-loc-/)) {
47312             return;
47313         }
47314         var n = cn.split('-').pop();
47315         var ans = this.footerEls;
47316         var sel = ans[n];
47317         
47318          // pick
47319         var range = this.editorcore.createRange();
47320         
47321         range.selectNodeContents(sel);
47322         //range.selectNode(sel);
47323         
47324         
47325         var selection = this.editorcore.getSelection();
47326         selection.removeAllRanges();
47327         selection.addRange(range);
47328         
47329         
47330         
47331         this.updateToolbar(null, null, sel);
47332         
47333         
47334     }
47335     
47336     
47337     
47338     
47339     
47340 });
47341
47342
47343
47344
47345
47346 /*
47347  * Based on:
47348  * Ext JS Library 1.1.1
47349  * Copyright(c) 2006-2007, Ext JS, LLC.
47350  *
47351  * Originally Released Under LGPL - original licence link has changed is not relivant.
47352  *
47353  * Fork - LGPL
47354  * <script type="text/javascript">
47355  */
47356  
47357 /**
47358  * @class Roo.form.BasicForm
47359  * @extends Roo.util.Observable
47360  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47361  * @constructor
47362  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47363  * @param {Object} config Configuration options
47364  */
47365 Roo.form.BasicForm = function(el, config){
47366     this.allItems = [];
47367     this.childForms = [];
47368     Roo.apply(this, config);
47369     /*
47370      * The Roo.form.Field items in this form.
47371      * @type MixedCollection
47372      */
47373      
47374      
47375     this.items = new Roo.util.MixedCollection(false, function(o){
47376         return o.id || (o.id = Roo.id());
47377     });
47378     this.addEvents({
47379         /**
47380          * @event beforeaction
47381          * Fires before any action is performed. Return false to cancel the action.
47382          * @param {Form} this
47383          * @param {Action} action The action to be performed
47384          */
47385         beforeaction: true,
47386         /**
47387          * @event actionfailed
47388          * Fires when an action fails.
47389          * @param {Form} this
47390          * @param {Action} action The action that failed
47391          */
47392         actionfailed : true,
47393         /**
47394          * @event actioncomplete
47395          * Fires when an action is completed.
47396          * @param {Form} this
47397          * @param {Action} action The action that completed
47398          */
47399         actioncomplete : true
47400     });
47401     if(el){
47402         this.initEl(el);
47403     }
47404     Roo.form.BasicForm.superclass.constructor.call(this);
47405     
47406     Roo.form.BasicForm.popover.apply();
47407 };
47408
47409 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47410     /**
47411      * @cfg {String} method
47412      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47413      */
47414     /**
47415      * @cfg {DataReader} reader
47416      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47417      * This is optional as there is built-in support for processing JSON.
47418      */
47419     /**
47420      * @cfg {DataReader} errorReader
47421      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47422      * This is completely optional as there is built-in support for processing JSON.
47423      */
47424     /**
47425      * @cfg {String} url
47426      * The URL to use for form actions if one isn't supplied in the action options.
47427      */
47428     /**
47429      * @cfg {Boolean} fileUpload
47430      * Set to true if this form is a file upload.
47431      */
47432      
47433     /**
47434      * @cfg {Object} baseParams
47435      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47436      */
47437      /**
47438      
47439     /**
47440      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47441      */
47442     timeout: 30,
47443
47444     // private
47445     activeAction : null,
47446
47447     /**
47448      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47449      * or setValues() data instead of when the form was first created.
47450      */
47451     trackResetOnLoad : false,
47452     
47453     
47454     /**
47455      * childForms - used for multi-tab forms
47456      * @type {Array}
47457      */
47458     childForms : false,
47459     
47460     /**
47461      * allItems - full list of fields.
47462      * @type {Array}
47463      */
47464     allItems : false,
47465     
47466     /**
47467      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47468      * element by passing it or its id or mask the form itself by passing in true.
47469      * @type Mixed
47470      */
47471     waitMsgTarget : false,
47472     
47473     /**
47474      * @type Boolean
47475      */
47476     disableMask : false,
47477     
47478     /**
47479      * @cfg {Boolean} errorMask (true|false) default false
47480      */
47481     errorMask : false,
47482     
47483     /**
47484      * @cfg {Number} maskOffset Default 100
47485      */
47486     maskOffset : 100,
47487
47488     // private
47489     initEl : function(el){
47490         this.el = Roo.get(el);
47491         this.id = this.el.id || Roo.id();
47492         this.el.on('submit', this.onSubmit, this);
47493         this.el.addClass('x-form');
47494     },
47495
47496     // private
47497     onSubmit : function(e){
47498         e.stopEvent();
47499     },
47500
47501     /**
47502      * Returns true if client-side validation on the form is successful.
47503      * @return Boolean
47504      */
47505     isValid : function(){
47506         var valid = true;
47507         var target = false;
47508         this.items.each(function(f){
47509             if(f.validate()){
47510                 return;
47511             }
47512             
47513             valid = false;
47514                 
47515             if(!target && f.el.isVisible(true)){
47516                 target = f;
47517             }
47518         });
47519         
47520         if(this.errorMask && !valid){
47521             Roo.form.BasicForm.popover.mask(this, target);
47522         }
47523         
47524         return valid;
47525     },
47526
47527     /**
47528      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47529      * @return Boolean
47530      */
47531     isDirty : function(){
47532         var dirty = false;
47533         this.items.each(function(f){
47534            if(f.isDirty()){
47535                dirty = true;
47536                return false;
47537            }
47538         });
47539         return dirty;
47540     },
47541     
47542     /**
47543      * Returns true if any fields in this form have changed since their original load. (New version)
47544      * @return Boolean
47545      */
47546     
47547     hasChanged : function()
47548     {
47549         var dirty = false;
47550         this.items.each(function(f){
47551            if(f.hasChanged()){
47552                dirty = true;
47553                return false;
47554            }
47555         });
47556         return dirty;
47557         
47558     },
47559     /**
47560      * Resets all hasChanged to 'false' -
47561      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47562      * So hasChanged storage is only to be used for this purpose
47563      * @return Boolean
47564      */
47565     resetHasChanged : function()
47566     {
47567         this.items.each(function(f){
47568            f.resetHasChanged();
47569         });
47570         
47571     },
47572     
47573     
47574     /**
47575      * Performs a predefined action (submit or load) or custom actions you define on this form.
47576      * @param {String} actionName The name of the action type
47577      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47578      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47579      * accept other config options):
47580      * <pre>
47581 Property          Type             Description
47582 ----------------  ---------------  ----------------------------------------------------------------------------------
47583 url               String           The url for the action (defaults to the form's url)
47584 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47585 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47586 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47587                                    validate the form on the client (defaults to false)
47588      * </pre>
47589      * @return {BasicForm} this
47590      */
47591     doAction : function(action, options){
47592         if(typeof action == 'string'){
47593             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47594         }
47595         if(this.fireEvent('beforeaction', this, action) !== false){
47596             this.beforeAction(action);
47597             action.run.defer(100, action);
47598         }
47599         return this;
47600     },
47601
47602     /**
47603      * Shortcut to do a submit action.
47604      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47605      * @return {BasicForm} this
47606      */
47607     submit : function(options){
47608         this.doAction('submit', options);
47609         return this;
47610     },
47611
47612     /**
47613      * Shortcut to do a load action.
47614      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47615      * @return {BasicForm} this
47616      */
47617     load : function(options){
47618         this.doAction('load', options);
47619         return this;
47620     },
47621
47622     /**
47623      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47624      * @param {Record} record The record to edit
47625      * @return {BasicForm} this
47626      */
47627     updateRecord : function(record){
47628         record.beginEdit();
47629         var fs = record.fields;
47630         fs.each(function(f){
47631             var field = this.findField(f.name);
47632             if(field){
47633                 record.set(f.name, field.getValue());
47634             }
47635         }, this);
47636         record.endEdit();
47637         return this;
47638     },
47639
47640     /**
47641      * Loads an Roo.data.Record into this form.
47642      * @param {Record} record The record to load
47643      * @return {BasicForm} this
47644      */
47645     loadRecord : function(record){
47646         this.setValues(record.data);
47647         return this;
47648     },
47649
47650     // private
47651     beforeAction : function(action){
47652         var o = action.options;
47653         
47654         if(!this.disableMask) {
47655             if(this.waitMsgTarget === true){
47656                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47657             }else if(this.waitMsgTarget){
47658                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47659                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47660             }else {
47661                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47662             }
47663         }
47664         
47665          
47666     },
47667
47668     // private
47669     afterAction : function(action, success){
47670         this.activeAction = null;
47671         var o = action.options;
47672         
47673         if(!this.disableMask) {
47674             if(this.waitMsgTarget === true){
47675                 this.el.unmask();
47676             }else if(this.waitMsgTarget){
47677                 this.waitMsgTarget.unmask();
47678             }else{
47679                 Roo.MessageBox.updateProgress(1);
47680                 Roo.MessageBox.hide();
47681             }
47682         }
47683         
47684         if(success){
47685             if(o.reset){
47686                 this.reset();
47687             }
47688             Roo.callback(o.success, o.scope, [this, action]);
47689             this.fireEvent('actioncomplete', this, action);
47690             
47691         }else{
47692             
47693             // failure condition..
47694             // we have a scenario where updates need confirming.
47695             // eg. if a locking scenario exists..
47696             // we look for { errors : { needs_confirm : true }} in the response.
47697             if (
47698                 (typeof(action.result) != 'undefined')  &&
47699                 (typeof(action.result.errors) != 'undefined')  &&
47700                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47701            ){
47702                 var _t = this;
47703                 Roo.MessageBox.confirm(
47704                     "Change requires confirmation",
47705                     action.result.errorMsg,
47706                     function(r) {
47707                         if (r != 'yes') {
47708                             return;
47709                         }
47710                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47711                     }
47712                     
47713                 );
47714                 
47715                 
47716                 
47717                 return;
47718             }
47719             
47720             Roo.callback(o.failure, o.scope, [this, action]);
47721             // show an error message if no failed handler is set..
47722             if (!this.hasListener('actionfailed')) {
47723                 Roo.MessageBox.alert("Error",
47724                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47725                         action.result.errorMsg :
47726                         "Saving Failed, please check your entries or try again"
47727                 );
47728             }
47729             
47730             this.fireEvent('actionfailed', this, action);
47731         }
47732         
47733     },
47734
47735     /**
47736      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47737      * @param {String} id The value to search for
47738      * @return Field
47739      */
47740     findField : function(id){
47741         var field = this.items.get(id);
47742         if(!field){
47743             this.items.each(function(f){
47744                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47745                     field = f;
47746                     return false;
47747                 }
47748             });
47749         }
47750         return field || null;
47751     },
47752
47753     /**
47754      * Add a secondary form to this one, 
47755      * Used to provide tabbed forms. One form is primary, with hidden values 
47756      * which mirror the elements from the other forms.
47757      * 
47758      * @param {Roo.form.Form} form to add.
47759      * 
47760      */
47761     addForm : function(form)
47762     {
47763        
47764         if (this.childForms.indexOf(form) > -1) {
47765             // already added..
47766             return;
47767         }
47768         this.childForms.push(form);
47769         var n = '';
47770         Roo.each(form.allItems, function (fe) {
47771             
47772             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47773             if (this.findField(n)) { // already added..
47774                 return;
47775             }
47776             var add = new Roo.form.Hidden({
47777                 name : n
47778             });
47779             add.render(this.el);
47780             
47781             this.add( add );
47782         }, this);
47783         
47784     },
47785     /**
47786      * Mark fields in this form invalid in bulk.
47787      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47788      * @return {BasicForm} this
47789      */
47790     markInvalid : function(errors){
47791         if(errors instanceof Array){
47792             for(var i = 0, len = errors.length; i < len; i++){
47793                 var fieldError = errors[i];
47794                 var f = this.findField(fieldError.id);
47795                 if(f){
47796                     f.markInvalid(fieldError.msg);
47797                 }
47798             }
47799         }else{
47800             var field, id;
47801             for(id in errors){
47802                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47803                     field.markInvalid(errors[id]);
47804                 }
47805             }
47806         }
47807         Roo.each(this.childForms || [], function (f) {
47808             f.markInvalid(errors);
47809         });
47810         
47811         return this;
47812     },
47813
47814     /**
47815      * Set values for fields in this form in bulk.
47816      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47817      * @return {BasicForm} this
47818      */
47819     setValues : function(values){
47820         if(values instanceof Array){ // array of objects
47821             for(var i = 0, len = values.length; i < len; i++){
47822                 var v = values[i];
47823                 var f = this.findField(v.id);
47824                 if(f){
47825                     f.setValue(v.value);
47826                     if(this.trackResetOnLoad){
47827                         f.originalValue = f.getValue();
47828                     }
47829                 }
47830             }
47831         }else{ // object hash
47832             var field, id;
47833             for(id in values){
47834                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47835                     
47836                     if (field.setFromData && 
47837                         field.valueField && 
47838                         field.displayField &&
47839                         // combos' with local stores can 
47840                         // be queried via setValue()
47841                         // to set their value..
47842                         (field.store && !field.store.isLocal)
47843                         ) {
47844                         // it's a combo
47845                         var sd = { };
47846                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47847                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47848                         field.setFromData(sd);
47849                         
47850                     } else {
47851                         field.setValue(values[id]);
47852                     }
47853                     
47854                     
47855                     if(this.trackResetOnLoad){
47856                         field.originalValue = field.getValue();
47857                     }
47858                 }
47859             }
47860         }
47861         this.resetHasChanged();
47862         
47863         
47864         Roo.each(this.childForms || [], function (f) {
47865             f.setValues(values);
47866             f.resetHasChanged();
47867         });
47868                 
47869         return this;
47870     },
47871  
47872     /**
47873      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47874      * they are returned as an array.
47875      * @param {Boolean} asString
47876      * @return {Object}
47877      */
47878     getValues : function(asString){
47879         if (this.childForms) {
47880             // copy values from the child forms
47881             Roo.each(this.childForms, function (f) {
47882                 this.setValues(f.getValues());
47883             }, this);
47884         }
47885         
47886         // use formdata
47887         if (typeof(FormData) != 'undefined' && asString !== true) {
47888             // this relies on a 'recent' version of chrome apparently...
47889             try {
47890                 var fd = (new FormData(this.el.dom)).entries();
47891                 var ret = {};
47892                 var ent = fd.next();
47893                 while (!ent.done) {
47894                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47895                     ent = fd.next();
47896                 };
47897                 return ret;
47898             } catch(e) {
47899                 
47900             }
47901             
47902         }
47903         
47904         
47905         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47906         if(asString === true){
47907             return fs;
47908         }
47909         return Roo.urlDecode(fs);
47910     },
47911     
47912     /**
47913      * Returns the fields in this form as an object with key/value pairs. 
47914      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47915      * @return {Object}
47916      */
47917     getFieldValues : function(with_hidden)
47918     {
47919         if (this.childForms) {
47920             // copy values from the child forms
47921             // should this call getFieldValues - probably not as we do not currently copy
47922             // hidden fields when we generate..
47923             Roo.each(this.childForms, function (f) {
47924                 this.setValues(f.getValues());
47925             }, this);
47926         }
47927         
47928         var ret = {};
47929         this.items.each(function(f){
47930             if (!f.getName()) {
47931                 return;
47932             }
47933             var v = f.getValue();
47934             if (f.inputType =='radio') {
47935                 if (typeof(ret[f.getName()]) == 'undefined') {
47936                     ret[f.getName()] = ''; // empty..
47937                 }
47938                 
47939                 if (!f.el.dom.checked) {
47940                     return;
47941                     
47942                 }
47943                 v = f.el.dom.value;
47944                 
47945             }
47946             
47947             // not sure if this supported any more..
47948             if ((typeof(v) == 'object') && f.getRawValue) {
47949                 v = f.getRawValue() ; // dates..
47950             }
47951             // combo boxes where name != hiddenName...
47952             if (f.name != f.getName()) {
47953                 ret[f.name] = f.getRawValue();
47954             }
47955             ret[f.getName()] = v;
47956         });
47957         
47958         return ret;
47959     },
47960
47961     /**
47962      * Clears all invalid messages in this form.
47963      * @return {BasicForm} this
47964      */
47965     clearInvalid : function(){
47966         this.items.each(function(f){
47967            f.clearInvalid();
47968         });
47969         
47970         Roo.each(this.childForms || [], function (f) {
47971             f.clearInvalid();
47972         });
47973         
47974         
47975         return this;
47976     },
47977
47978     /**
47979      * Resets this form.
47980      * @return {BasicForm} this
47981      */
47982     reset : function(){
47983         this.items.each(function(f){
47984             f.reset();
47985         });
47986         
47987         Roo.each(this.childForms || [], function (f) {
47988             f.reset();
47989         });
47990         this.resetHasChanged();
47991         
47992         return this;
47993     },
47994
47995     /**
47996      * Add Roo.form components to this form.
47997      * @param {Field} field1
47998      * @param {Field} field2 (optional)
47999      * @param {Field} etc (optional)
48000      * @return {BasicForm} this
48001      */
48002     add : function(){
48003         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48004         return this;
48005     },
48006
48007
48008     /**
48009      * Removes a field from the items collection (does NOT remove its markup).
48010      * @param {Field} field
48011      * @return {BasicForm} this
48012      */
48013     remove : function(field){
48014         this.items.remove(field);
48015         return this;
48016     },
48017
48018     /**
48019      * Looks at the fields in this form, checks them for an id attribute,
48020      * and calls applyTo on the existing dom element with that id.
48021      * @return {BasicForm} this
48022      */
48023     render : function(){
48024         this.items.each(function(f){
48025             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48026                 f.applyTo(f.id);
48027             }
48028         });
48029         return this;
48030     },
48031
48032     /**
48033      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48034      * @param {Object} values
48035      * @return {BasicForm} this
48036      */
48037     applyToFields : function(o){
48038         this.items.each(function(f){
48039            Roo.apply(f, o);
48040         });
48041         return this;
48042     },
48043
48044     /**
48045      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48046      * @param {Object} values
48047      * @return {BasicForm} this
48048      */
48049     applyIfToFields : function(o){
48050         this.items.each(function(f){
48051            Roo.applyIf(f, o);
48052         });
48053         return this;
48054     }
48055 });
48056
48057 // back compat
48058 Roo.BasicForm = Roo.form.BasicForm;
48059
48060 Roo.apply(Roo.form.BasicForm, {
48061     
48062     popover : {
48063         
48064         padding : 5,
48065         
48066         isApplied : false,
48067         
48068         isMasked : false,
48069         
48070         form : false,
48071         
48072         target : false,
48073         
48074         intervalID : false,
48075         
48076         maskEl : false,
48077         
48078         apply : function()
48079         {
48080             if(this.isApplied){
48081                 return;
48082             }
48083             
48084             this.maskEl = {
48085                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48086                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48087                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48088                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48089             };
48090             
48091             this.maskEl.top.enableDisplayMode("block");
48092             this.maskEl.left.enableDisplayMode("block");
48093             this.maskEl.bottom.enableDisplayMode("block");
48094             this.maskEl.right.enableDisplayMode("block");
48095             
48096             Roo.get(document.body).on('click', function(){
48097                 this.unmask();
48098             }, this);
48099             
48100             Roo.get(document.body).on('touchstart', function(){
48101                 this.unmask();
48102             }, this);
48103             
48104             this.isApplied = true
48105         },
48106         
48107         mask : function(form, target)
48108         {
48109             this.form = form;
48110             
48111             this.target = target;
48112             
48113             if(!this.form.errorMask || !target.el){
48114                 return;
48115             }
48116             
48117             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48118             
48119             var ot = this.target.el.calcOffsetsTo(scrollable);
48120             
48121             var scrollTo = ot[1] - this.form.maskOffset;
48122             
48123             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48124             
48125             scrollable.scrollTo('top', scrollTo);
48126             
48127             var el = this.target.wrap || this.target.el;
48128             
48129             var box = el.getBox();
48130             
48131             this.maskEl.top.setStyle('position', 'absolute');
48132             this.maskEl.top.setStyle('z-index', 10000);
48133             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48134             this.maskEl.top.setLeft(0);
48135             this.maskEl.top.setTop(0);
48136             this.maskEl.top.show();
48137             
48138             this.maskEl.left.setStyle('position', 'absolute');
48139             this.maskEl.left.setStyle('z-index', 10000);
48140             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48141             this.maskEl.left.setLeft(0);
48142             this.maskEl.left.setTop(box.y - this.padding);
48143             this.maskEl.left.show();
48144
48145             this.maskEl.bottom.setStyle('position', 'absolute');
48146             this.maskEl.bottom.setStyle('z-index', 10000);
48147             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48148             this.maskEl.bottom.setLeft(0);
48149             this.maskEl.bottom.setTop(box.bottom + this.padding);
48150             this.maskEl.bottom.show();
48151
48152             this.maskEl.right.setStyle('position', 'absolute');
48153             this.maskEl.right.setStyle('z-index', 10000);
48154             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48155             this.maskEl.right.setLeft(box.right + this.padding);
48156             this.maskEl.right.setTop(box.y - this.padding);
48157             this.maskEl.right.show();
48158
48159             this.intervalID = window.setInterval(function() {
48160                 Roo.form.BasicForm.popover.unmask();
48161             }, 10000);
48162
48163             window.onwheel = function(){ return false;};
48164             
48165             (function(){ this.isMasked = true; }).defer(500, this);
48166             
48167         },
48168         
48169         unmask : function()
48170         {
48171             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48172                 return;
48173             }
48174             
48175             this.maskEl.top.setStyle('position', 'absolute');
48176             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48177             this.maskEl.top.hide();
48178
48179             this.maskEl.left.setStyle('position', 'absolute');
48180             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48181             this.maskEl.left.hide();
48182
48183             this.maskEl.bottom.setStyle('position', 'absolute');
48184             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48185             this.maskEl.bottom.hide();
48186
48187             this.maskEl.right.setStyle('position', 'absolute');
48188             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48189             this.maskEl.right.hide();
48190             
48191             window.onwheel = function(){ return true;};
48192             
48193             if(this.intervalID){
48194                 window.clearInterval(this.intervalID);
48195                 this.intervalID = false;
48196             }
48197             
48198             this.isMasked = false;
48199             
48200         }
48201         
48202     }
48203     
48204 });/*
48205  * Based on:
48206  * Ext JS Library 1.1.1
48207  * Copyright(c) 2006-2007, Ext JS, LLC.
48208  *
48209  * Originally Released Under LGPL - original licence link has changed is not relivant.
48210  *
48211  * Fork - LGPL
48212  * <script type="text/javascript">
48213  */
48214
48215 /**
48216  * @class Roo.form.Form
48217  * @extends Roo.form.BasicForm
48218  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48219  * @constructor
48220  * @param {Object} config Configuration options
48221  */
48222 Roo.form.Form = function(config){
48223     var xitems =  [];
48224     if (config.items) {
48225         xitems = config.items;
48226         delete config.items;
48227     }
48228    
48229     
48230     Roo.form.Form.superclass.constructor.call(this, null, config);
48231     this.url = this.url || this.action;
48232     if(!this.root){
48233         this.root = new Roo.form.Layout(Roo.applyIf({
48234             id: Roo.id()
48235         }, config));
48236     }
48237     this.active = this.root;
48238     /**
48239      * Array of all the buttons that have been added to this form via {@link addButton}
48240      * @type Array
48241      */
48242     this.buttons = [];
48243     this.allItems = [];
48244     this.addEvents({
48245         /**
48246          * @event clientvalidation
48247          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48248          * @param {Form} this
48249          * @param {Boolean} valid true if the form has passed client-side validation
48250          */
48251         clientvalidation: true,
48252         /**
48253          * @event rendered
48254          * Fires when the form is rendered
48255          * @param {Roo.form.Form} form
48256          */
48257         rendered : true
48258     });
48259     
48260     if (this.progressUrl) {
48261             // push a hidden field onto the list of fields..
48262             this.addxtype( {
48263                     xns: Roo.form, 
48264                     xtype : 'Hidden', 
48265                     name : 'UPLOAD_IDENTIFIER' 
48266             });
48267         }
48268         
48269     
48270     Roo.each(xitems, this.addxtype, this);
48271     
48272 };
48273
48274 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48275     /**
48276      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48277      */
48278     /**
48279      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48280      */
48281     /**
48282      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48283      */
48284     buttonAlign:'center',
48285
48286     /**
48287      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48288      */
48289     minButtonWidth:75,
48290
48291     /**
48292      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48293      * This property cascades to child containers if not set.
48294      */
48295     labelAlign:'left',
48296
48297     /**
48298      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48299      * fires a looping event with that state. This is required to bind buttons to the valid
48300      * state using the config value formBind:true on the button.
48301      */
48302     monitorValid : false,
48303
48304     /**
48305      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48306      */
48307     monitorPoll : 200,
48308     
48309     /**
48310      * @cfg {String} progressUrl - Url to return progress data 
48311      */
48312     
48313     progressUrl : false,
48314     /**
48315      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48316      * sending a formdata with extra parameters - eg uploaded elements.
48317      */
48318     
48319     formData : false,
48320     
48321     /**
48322      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48323      * fields are added and the column is closed. If no fields are passed the column remains open
48324      * until end() is called.
48325      * @param {Object} config The config to pass to the column
48326      * @param {Field} field1 (optional)
48327      * @param {Field} field2 (optional)
48328      * @param {Field} etc (optional)
48329      * @return Column The column container object
48330      */
48331     column : function(c){
48332         var col = new Roo.form.Column(c);
48333         this.start(col);
48334         if(arguments.length > 1){ // duplicate code required because of Opera
48335             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48336             this.end();
48337         }
48338         return col;
48339     },
48340
48341     /**
48342      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48343      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48344      * until end() is called.
48345      * @param {Object} config The config to pass to the fieldset
48346      * @param {Field} field1 (optional)
48347      * @param {Field} field2 (optional)
48348      * @param {Field} etc (optional)
48349      * @return FieldSet The fieldset container object
48350      */
48351     fieldset : function(c){
48352         var fs = new Roo.form.FieldSet(c);
48353         this.start(fs);
48354         if(arguments.length > 1){ // duplicate code required because of Opera
48355             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48356             this.end();
48357         }
48358         return fs;
48359     },
48360
48361     /**
48362      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48363      * fields are added and the container is closed. If no fields are passed the container remains open
48364      * until end() is called.
48365      * @param {Object} config The config to pass to the Layout
48366      * @param {Field} field1 (optional)
48367      * @param {Field} field2 (optional)
48368      * @param {Field} etc (optional)
48369      * @return Layout The container object
48370      */
48371     container : function(c){
48372         var l = new Roo.form.Layout(c);
48373         this.start(l);
48374         if(arguments.length > 1){ // duplicate code required because of Opera
48375             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48376             this.end();
48377         }
48378         return l;
48379     },
48380
48381     /**
48382      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48383      * @param {Object} container A Roo.form.Layout or subclass of Layout
48384      * @return {Form} this
48385      */
48386     start : function(c){
48387         // cascade label info
48388         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48389         this.active.stack.push(c);
48390         c.ownerCt = this.active;
48391         this.active = c;
48392         return this;
48393     },
48394
48395     /**
48396      * Closes the current open container
48397      * @return {Form} this
48398      */
48399     end : function(){
48400         if(this.active == this.root){
48401             return this;
48402         }
48403         this.active = this.active.ownerCt;
48404         return this;
48405     },
48406
48407     /**
48408      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48409      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48410      * as the label of the field.
48411      * @param {Field} field1
48412      * @param {Field} field2 (optional)
48413      * @param {Field} etc. (optional)
48414      * @return {Form} this
48415      */
48416     add : function(){
48417         this.active.stack.push.apply(this.active.stack, arguments);
48418         this.allItems.push.apply(this.allItems,arguments);
48419         var r = [];
48420         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48421             if(a[i].isFormField){
48422                 r.push(a[i]);
48423             }
48424         }
48425         if(r.length > 0){
48426             Roo.form.Form.superclass.add.apply(this, r);
48427         }
48428         return this;
48429     },
48430     
48431
48432     
48433     
48434     
48435      /**
48436      * Find any element that has been added to a form, using it's ID or name
48437      * This can include framesets, columns etc. along with regular fields..
48438      * @param {String} id - id or name to find.
48439      
48440      * @return {Element} e - or false if nothing found.
48441      */
48442     findbyId : function(id)
48443     {
48444         var ret = false;
48445         if (!id) {
48446             return ret;
48447         }
48448         Roo.each(this.allItems, function(f){
48449             if (f.id == id || f.name == id ){
48450                 ret = f;
48451                 return false;
48452             }
48453         });
48454         return ret;
48455     },
48456
48457     
48458     
48459     /**
48460      * Render this form into the passed container. This should only be called once!
48461      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48462      * @return {Form} this
48463      */
48464     render : function(ct)
48465     {
48466         
48467         
48468         
48469         ct = Roo.get(ct);
48470         var o = this.autoCreate || {
48471             tag: 'form',
48472             method : this.method || 'POST',
48473             id : this.id || Roo.id()
48474         };
48475         this.initEl(ct.createChild(o));
48476
48477         this.root.render(this.el);
48478         
48479        
48480              
48481         this.items.each(function(f){
48482             f.render('x-form-el-'+f.id);
48483         });
48484
48485         if(this.buttons.length > 0){
48486             // tables are required to maintain order and for correct IE layout
48487             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48488                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48489                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48490             }}, null, true);
48491             var tr = tb.getElementsByTagName('tr')[0];
48492             for(var i = 0, len = this.buttons.length; i < len; i++) {
48493                 var b = this.buttons[i];
48494                 var td = document.createElement('td');
48495                 td.className = 'x-form-btn-td';
48496                 b.render(tr.appendChild(td));
48497             }
48498         }
48499         if(this.monitorValid){ // initialize after render
48500             this.startMonitoring();
48501         }
48502         this.fireEvent('rendered', this);
48503         return this;
48504     },
48505
48506     /**
48507      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48508      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48509      * object or a valid Roo.DomHelper element config
48510      * @param {Function} handler The function called when the button is clicked
48511      * @param {Object} scope (optional) The scope of the handler function
48512      * @return {Roo.Button}
48513      */
48514     addButton : function(config, handler, scope){
48515         var bc = {
48516             handler: handler,
48517             scope: scope,
48518             minWidth: this.minButtonWidth,
48519             hideParent:true
48520         };
48521         if(typeof config == "string"){
48522             bc.text = config;
48523         }else{
48524             Roo.apply(bc, config);
48525         }
48526         var btn = new Roo.Button(null, bc);
48527         this.buttons.push(btn);
48528         return btn;
48529     },
48530
48531      /**
48532      * Adds a series of form elements (using the xtype property as the factory method.
48533      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48534      * @param {Object} config 
48535      */
48536     
48537     addxtype : function()
48538     {
48539         var ar = Array.prototype.slice.call(arguments, 0);
48540         var ret = false;
48541         for(var i = 0; i < ar.length; i++) {
48542             if (!ar[i]) {
48543                 continue; // skip -- if this happends something invalid got sent, we 
48544                 // should ignore it, as basically that interface element will not show up
48545                 // and that should be pretty obvious!!
48546             }
48547             
48548             if (Roo.form[ar[i].xtype]) {
48549                 ar[i].form = this;
48550                 var fe = Roo.factory(ar[i], Roo.form);
48551                 if (!ret) {
48552                     ret = fe;
48553                 }
48554                 fe.form = this;
48555                 if (fe.store) {
48556                     fe.store.form = this;
48557                 }
48558                 if (fe.isLayout) {  
48559                          
48560                     this.start(fe);
48561                     this.allItems.push(fe);
48562                     if (fe.items && fe.addxtype) {
48563                         fe.addxtype.apply(fe, fe.items);
48564                         delete fe.items;
48565                     }
48566                      this.end();
48567                     continue;
48568                 }
48569                 
48570                 
48571                  
48572                 this.add(fe);
48573               //  console.log('adding ' + ar[i].xtype);
48574             }
48575             if (ar[i].xtype == 'Button') {  
48576                 //console.log('adding button');
48577                 //console.log(ar[i]);
48578                 this.addButton(ar[i]);
48579                 this.allItems.push(fe);
48580                 continue;
48581             }
48582             
48583             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48584                 alert('end is not supported on xtype any more, use items');
48585             //    this.end();
48586             //    //console.log('adding end');
48587             }
48588             
48589         }
48590         return ret;
48591     },
48592     
48593     /**
48594      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48595      * option "monitorValid"
48596      */
48597     startMonitoring : function(){
48598         if(!this.bound){
48599             this.bound = true;
48600             Roo.TaskMgr.start({
48601                 run : this.bindHandler,
48602                 interval : this.monitorPoll || 200,
48603                 scope: this
48604             });
48605         }
48606     },
48607
48608     /**
48609      * Stops monitoring of the valid state of this form
48610      */
48611     stopMonitoring : function(){
48612         this.bound = false;
48613     },
48614
48615     // private
48616     bindHandler : function(){
48617         if(!this.bound){
48618             return false; // stops binding
48619         }
48620         var valid = true;
48621         this.items.each(function(f){
48622             if(!f.isValid(true)){
48623                 valid = false;
48624                 return false;
48625             }
48626         });
48627         for(var i = 0, len = this.buttons.length; i < len; i++){
48628             var btn = this.buttons[i];
48629             if(btn.formBind === true && btn.disabled === valid){
48630                 btn.setDisabled(!valid);
48631             }
48632         }
48633         this.fireEvent('clientvalidation', this, valid);
48634     }
48635     
48636     
48637     
48638     
48639     
48640     
48641     
48642     
48643 });
48644
48645
48646 // back compat
48647 Roo.Form = Roo.form.Form;
48648 /*
48649  * Based on:
48650  * Ext JS Library 1.1.1
48651  * Copyright(c) 2006-2007, Ext JS, LLC.
48652  *
48653  * Originally Released Under LGPL - original licence link has changed is not relivant.
48654  *
48655  * Fork - LGPL
48656  * <script type="text/javascript">
48657  */
48658
48659 // as we use this in bootstrap.
48660 Roo.namespace('Roo.form');
48661  /**
48662  * @class Roo.form.Action
48663  * Internal Class used to handle form actions
48664  * @constructor
48665  * @param {Roo.form.BasicForm} el The form element or its id
48666  * @param {Object} config Configuration options
48667  */
48668
48669  
48670  
48671 // define the action interface
48672 Roo.form.Action = function(form, options){
48673     this.form = form;
48674     this.options = options || {};
48675 };
48676 /**
48677  * Client Validation Failed
48678  * @const 
48679  */
48680 Roo.form.Action.CLIENT_INVALID = 'client';
48681 /**
48682  * Server Validation Failed
48683  * @const 
48684  */
48685 Roo.form.Action.SERVER_INVALID = 'server';
48686  /**
48687  * Connect to Server Failed
48688  * @const 
48689  */
48690 Roo.form.Action.CONNECT_FAILURE = 'connect';
48691 /**
48692  * Reading Data from Server Failed
48693  * @const 
48694  */
48695 Roo.form.Action.LOAD_FAILURE = 'load';
48696
48697 Roo.form.Action.prototype = {
48698     type : 'default',
48699     failureType : undefined,
48700     response : undefined,
48701     result : undefined,
48702
48703     // interface method
48704     run : function(options){
48705
48706     },
48707
48708     // interface method
48709     success : function(response){
48710
48711     },
48712
48713     // interface method
48714     handleResponse : function(response){
48715
48716     },
48717
48718     // default connection failure
48719     failure : function(response){
48720         
48721         this.response = response;
48722         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48723         this.form.afterAction(this, false);
48724     },
48725
48726     processResponse : function(response){
48727         this.response = response;
48728         if(!response.responseText){
48729             return true;
48730         }
48731         this.result = this.handleResponse(response);
48732         return this.result;
48733     },
48734
48735     // utility functions used internally
48736     getUrl : function(appendParams){
48737         var url = this.options.url || this.form.url || this.form.el.dom.action;
48738         if(appendParams){
48739             var p = this.getParams();
48740             if(p){
48741                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48742             }
48743         }
48744         return url;
48745     },
48746
48747     getMethod : function(){
48748         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48749     },
48750
48751     getParams : function(){
48752         var bp = this.form.baseParams;
48753         var p = this.options.params;
48754         if(p){
48755             if(typeof p == "object"){
48756                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48757             }else if(typeof p == 'string' && bp){
48758                 p += '&' + Roo.urlEncode(bp);
48759             }
48760         }else if(bp){
48761             p = Roo.urlEncode(bp);
48762         }
48763         return p;
48764     },
48765
48766     createCallback : function(){
48767         return {
48768             success: this.success,
48769             failure: this.failure,
48770             scope: this,
48771             timeout: (this.form.timeout*1000),
48772             upload: this.form.fileUpload ? this.success : undefined
48773         };
48774     }
48775 };
48776
48777 Roo.form.Action.Submit = function(form, options){
48778     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48779 };
48780
48781 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48782     type : 'submit',
48783
48784     haveProgress : false,
48785     uploadComplete : false,
48786     
48787     // uploadProgress indicator.
48788     uploadProgress : function()
48789     {
48790         if (!this.form.progressUrl) {
48791             return;
48792         }
48793         
48794         if (!this.haveProgress) {
48795             Roo.MessageBox.progress("Uploading", "Uploading");
48796         }
48797         if (this.uploadComplete) {
48798            Roo.MessageBox.hide();
48799            return;
48800         }
48801         
48802         this.haveProgress = true;
48803    
48804         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48805         
48806         var c = new Roo.data.Connection();
48807         c.request({
48808             url : this.form.progressUrl,
48809             params: {
48810                 id : uid
48811             },
48812             method: 'GET',
48813             success : function(req){
48814                //console.log(data);
48815                 var rdata = false;
48816                 var edata;
48817                 try  {
48818                    rdata = Roo.decode(req.responseText)
48819                 } catch (e) {
48820                     Roo.log("Invalid data from server..");
48821                     Roo.log(edata);
48822                     return;
48823                 }
48824                 if (!rdata || !rdata.success) {
48825                     Roo.log(rdata);
48826                     Roo.MessageBox.alert(Roo.encode(rdata));
48827                     return;
48828                 }
48829                 var data = rdata.data;
48830                 
48831                 if (this.uploadComplete) {
48832                    Roo.MessageBox.hide();
48833                    return;
48834                 }
48835                    
48836                 if (data){
48837                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48838                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48839                     );
48840                 }
48841                 this.uploadProgress.defer(2000,this);
48842             },
48843        
48844             failure: function(data) {
48845                 Roo.log('progress url failed ');
48846                 Roo.log(data);
48847             },
48848             scope : this
48849         });
48850            
48851     },
48852     
48853     
48854     run : function()
48855     {
48856         // run get Values on the form, so it syncs any secondary forms.
48857         this.form.getValues();
48858         
48859         var o = this.options;
48860         var method = this.getMethod();
48861         var isPost = method == 'POST';
48862         if(o.clientValidation === false || this.form.isValid()){
48863             
48864             if (this.form.progressUrl) {
48865                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48866                     (new Date() * 1) + '' + Math.random());
48867                     
48868             } 
48869             
48870             
48871             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48872                 form:this.form.el.dom,
48873                 url:this.getUrl(!isPost),
48874                 method: method,
48875                 params:isPost ? this.getParams() : null,
48876                 isUpload: this.form.fileUpload,
48877                 formData : this.form.formData
48878             }));
48879             
48880             this.uploadProgress();
48881
48882         }else if (o.clientValidation !== false){ // client validation failed
48883             this.failureType = Roo.form.Action.CLIENT_INVALID;
48884             this.form.afterAction(this, false);
48885         }
48886     },
48887
48888     success : function(response)
48889     {
48890         this.uploadComplete= true;
48891         if (this.haveProgress) {
48892             Roo.MessageBox.hide();
48893         }
48894         
48895         
48896         var result = this.processResponse(response);
48897         if(result === true || result.success){
48898             this.form.afterAction(this, true);
48899             return;
48900         }
48901         if(result.errors){
48902             this.form.markInvalid(result.errors);
48903             this.failureType = Roo.form.Action.SERVER_INVALID;
48904         }
48905         this.form.afterAction(this, false);
48906     },
48907     failure : function(response)
48908     {
48909         this.uploadComplete= true;
48910         if (this.haveProgress) {
48911             Roo.MessageBox.hide();
48912         }
48913         
48914         this.response = response;
48915         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48916         this.form.afterAction(this, false);
48917     },
48918     
48919     handleResponse : function(response){
48920         if(this.form.errorReader){
48921             var rs = this.form.errorReader.read(response);
48922             var errors = [];
48923             if(rs.records){
48924                 for(var i = 0, len = rs.records.length; i < len; i++) {
48925                     var r = rs.records[i];
48926                     errors[i] = r.data;
48927                 }
48928             }
48929             if(errors.length < 1){
48930                 errors = null;
48931             }
48932             return {
48933                 success : rs.success,
48934                 errors : errors
48935             };
48936         }
48937         var ret = false;
48938         try {
48939             ret = Roo.decode(response.responseText);
48940         } catch (e) {
48941             ret = {
48942                 success: false,
48943                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48944                 errors : []
48945             };
48946         }
48947         return ret;
48948         
48949     }
48950 });
48951
48952
48953 Roo.form.Action.Load = function(form, options){
48954     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48955     this.reader = this.form.reader;
48956 };
48957
48958 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48959     type : 'load',
48960
48961     run : function(){
48962         
48963         Roo.Ajax.request(Roo.apply(
48964                 this.createCallback(), {
48965                     method:this.getMethod(),
48966                     url:this.getUrl(false),
48967                     params:this.getParams()
48968         }));
48969     },
48970
48971     success : function(response){
48972         
48973         var result = this.processResponse(response);
48974         if(result === true || !result.success || !result.data){
48975             this.failureType = Roo.form.Action.LOAD_FAILURE;
48976             this.form.afterAction(this, false);
48977             return;
48978         }
48979         this.form.clearInvalid();
48980         this.form.setValues(result.data);
48981         this.form.afterAction(this, true);
48982     },
48983
48984     handleResponse : function(response){
48985         if(this.form.reader){
48986             var rs = this.form.reader.read(response);
48987             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48988             return {
48989                 success : rs.success,
48990                 data : data
48991             };
48992         }
48993         return Roo.decode(response.responseText);
48994     }
48995 });
48996
48997 Roo.form.Action.ACTION_TYPES = {
48998     'load' : Roo.form.Action.Load,
48999     'submit' : Roo.form.Action.Submit
49000 };/*
49001  * Based on:
49002  * Ext JS Library 1.1.1
49003  * Copyright(c) 2006-2007, Ext JS, LLC.
49004  *
49005  * Originally Released Under LGPL - original licence link has changed is not relivant.
49006  *
49007  * Fork - LGPL
49008  * <script type="text/javascript">
49009  */
49010  
49011 /**
49012  * @class Roo.form.Layout
49013  * @extends Roo.Component
49014  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49015  * @constructor
49016  * @param {Object} config Configuration options
49017  */
49018 Roo.form.Layout = function(config){
49019     var xitems = [];
49020     if (config.items) {
49021         xitems = config.items;
49022         delete config.items;
49023     }
49024     Roo.form.Layout.superclass.constructor.call(this, config);
49025     this.stack = [];
49026     Roo.each(xitems, this.addxtype, this);
49027      
49028 };
49029
49030 Roo.extend(Roo.form.Layout, Roo.Component, {
49031     /**
49032      * @cfg {String/Object} autoCreate
49033      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49034      */
49035     /**
49036      * @cfg {String/Object/Function} style
49037      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49038      * a function which returns such a specification.
49039      */
49040     /**
49041      * @cfg {String} labelAlign
49042      * Valid values are "left," "top" and "right" (defaults to "left")
49043      */
49044     /**
49045      * @cfg {Number} labelWidth
49046      * Fixed width in pixels of all field labels (defaults to undefined)
49047      */
49048     /**
49049      * @cfg {Boolean} clear
49050      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49051      */
49052     clear : true,
49053     /**
49054      * @cfg {String} labelSeparator
49055      * The separator to use after field labels (defaults to ':')
49056      */
49057     labelSeparator : ':',
49058     /**
49059      * @cfg {Boolean} hideLabels
49060      * True to suppress the display of field labels in this layout (defaults to false)
49061      */
49062     hideLabels : false,
49063
49064     // private
49065     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49066     
49067     isLayout : true,
49068     
49069     // private
49070     onRender : function(ct, position){
49071         if(this.el){ // from markup
49072             this.el = Roo.get(this.el);
49073         }else {  // generate
49074             var cfg = this.getAutoCreate();
49075             this.el = ct.createChild(cfg, position);
49076         }
49077         if(this.style){
49078             this.el.applyStyles(this.style);
49079         }
49080         if(this.labelAlign){
49081             this.el.addClass('x-form-label-'+this.labelAlign);
49082         }
49083         if(this.hideLabels){
49084             this.labelStyle = "display:none";
49085             this.elementStyle = "padding-left:0;";
49086         }else{
49087             if(typeof this.labelWidth == 'number'){
49088                 this.labelStyle = "width:"+this.labelWidth+"px;";
49089                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49090             }
49091             if(this.labelAlign == 'top'){
49092                 this.labelStyle = "width:auto;";
49093                 this.elementStyle = "padding-left:0;";
49094             }
49095         }
49096         var stack = this.stack;
49097         var slen = stack.length;
49098         if(slen > 0){
49099             if(!this.fieldTpl){
49100                 var t = new Roo.Template(
49101                     '<div class="x-form-item {5}">',
49102                         '<label for="{0}" style="{2}">{1}{4}</label>',
49103                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49104                         '</div>',
49105                     '</div><div class="x-form-clear-left"></div>'
49106                 );
49107                 t.disableFormats = true;
49108                 t.compile();
49109                 Roo.form.Layout.prototype.fieldTpl = t;
49110             }
49111             for(var i = 0; i < slen; i++) {
49112                 if(stack[i].isFormField){
49113                     this.renderField(stack[i]);
49114                 }else{
49115                     this.renderComponent(stack[i]);
49116                 }
49117             }
49118         }
49119         if(this.clear){
49120             this.el.createChild({cls:'x-form-clear'});
49121         }
49122     },
49123
49124     // private
49125     renderField : function(f){
49126         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49127                f.id, //0
49128                f.fieldLabel, //1
49129                f.labelStyle||this.labelStyle||'', //2
49130                this.elementStyle||'', //3
49131                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49132                f.itemCls||this.itemCls||''  //5
49133        ], true).getPrevSibling());
49134     },
49135
49136     // private
49137     renderComponent : function(c){
49138         c.render(c.isLayout ? this.el : this.el.createChild());    
49139     },
49140     /**
49141      * Adds a object form elements (using the xtype property as the factory method.)
49142      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49143      * @param {Object} config 
49144      */
49145     addxtype : function(o)
49146     {
49147         // create the lement.
49148         o.form = this.form;
49149         var fe = Roo.factory(o, Roo.form);
49150         this.form.allItems.push(fe);
49151         this.stack.push(fe);
49152         
49153         if (fe.isFormField) {
49154             this.form.items.add(fe);
49155         }
49156          
49157         return fe;
49158     }
49159 });
49160
49161 /**
49162  * @class Roo.form.Column
49163  * @extends Roo.form.Layout
49164  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49165  * @constructor
49166  * @param {Object} config Configuration options
49167  */
49168 Roo.form.Column = function(config){
49169     Roo.form.Column.superclass.constructor.call(this, config);
49170 };
49171
49172 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49173     /**
49174      * @cfg {Number/String} width
49175      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49176      */
49177     /**
49178      * @cfg {String/Object} autoCreate
49179      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49180      */
49181
49182     // private
49183     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49184
49185     // private
49186     onRender : function(ct, position){
49187         Roo.form.Column.superclass.onRender.call(this, ct, position);
49188         if(this.width){
49189             this.el.setWidth(this.width);
49190         }
49191     }
49192 });
49193
49194
49195 /**
49196  * @class Roo.form.Row
49197  * @extends Roo.form.Layout
49198  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49199  * @constructor
49200  * @param {Object} config Configuration options
49201  */
49202
49203  
49204 Roo.form.Row = function(config){
49205     Roo.form.Row.superclass.constructor.call(this, config);
49206 };
49207  
49208 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49209       /**
49210      * @cfg {Number/String} width
49211      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49212      */
49213     /**
49214      * @cfg {Number/String} height
49215      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49216      */
49217     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49218     
49219     padWidth : 20,
49220     // private
49221     onRender : function(ct, position){
49222         //console.log('row render');
49223         if(!this.rowTpl){
49224             var t = new Roo.Template(
49225                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49226                     '<label for="{0}" style="{2}">{1}{4}</label>',
49227                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49228                     '</div>',
49229                 '</div>'
49230             );
49231             t.disableFormats = true;
49232             t.compile();
49233             Roo.form.Layout.prototype.rowTpl = t;
49234         }
49235         this.fieldTpl = this.rowTpl;
49236         
49237         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49238         var labelWidth = 100;
49239         
49240         if ((this.labelAlign != 'top')) {
49241             if (typeof this.labelWidth == 'number') {
49242                 labelWidth = this.labelWidth
49243             }
49244             this.padWidth =  20 + labelWidth;
49245             
49246         }
49247         
49248         Roo.form.Column.superclass.onRender.call(this, ct, position);
49249         if(this.width){
49250             this.el.setWidth(this.width);
49251         }
49252         if(this.height){
49253             this.el.setHeight(this.height);
49254         }
49255     },
49256     
49257     // private
49258     renderField : function(f){
49259         f.fieldEl = this.fieldTpl.append(this.el, [
49260                f.id, f.fieldLabel,
49261                f.labelStyle||this.labelStyle||'',
49262                this.elementStyle||'',
49263                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49264                f.itemCls||this.itemCls||'',
49265                f.width ? f.width + this.padWidth : 160 + this.padWidth
49266        ],true);
49267     }
49268 });
49269  
49270
49271 /**
49272  * @class Roo.form.FieldSet
49273  * @extends Roo.form.Layout
49274  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49275  * @constructor
49276  * @param {Object} config Configuration options
49277  */
49278 Roo.form.FieldSet = function(config){
49279     Roo.form.FieldSet.superclass.constructor.call(this, config);
49280 };
49281
49282 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49283     /**
49284      * @cfg {String} legend
49285      * The text to display as the legend for the FieldSet (defaults to '')
49286      */
49287     /**
49288      * @cfg {String/Object} autoCreate
49289      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49290      */
49291
49292     // private
49293     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49294
49295     // private
49296     onRender : function(ct, position){
49297         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49298         if(this.legend){
49299             this.setLegend(this.legend);
49300         }
49301     },
49302
49303     // private
49304     setLegend : function(text){
49305         if(this.rendered){
49306             this.el.child('legend').update(text);
49307         }
49308     }
49309 });/*
49310  * Based on:
49311  * Ext JS Library 1.1.1
49312  * Copyright(c) 2006-2007, Ext JS, LLC.
49313  *
49314  * Originally Released Under LGPL - original licence link has changed is not relivant.
49315  *
49316  * Fork - LGPL
49317  * <script type="text/javascript">
49318  */
49319 /**
49320  * @class Roo.form.VTypes
49321  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49322  * @singleton
49323  */
49324 Roo.form.VTypes = function(){
49325     // closure these in so they are only created once.
49326     var alpha = /^[a-zA-Z_]+$/;
49327     var alphanum = /^[a-zA-Z0-9_]+$/;
49328     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49329     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49330
49331     // All these messages and functions are configurable
49332     return {
49333         /**
49334          * The function used to validate email addresses
49335          * @param {String} value The email address
49336          */
49337         'email' : function(v){
49338             return email.test(v);
49339         },
49340         /**
49341          * The error text to display when the email validation function returns false
49342          * @type String
49343          */
49344         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49345         /**
49346          * The keystroke filter mask to be applied on email input
49347          * @type RegExp
49348          */
49349         'emailMask' : /[a-z0-9_\.\-@]/i,
49350
49351         /**
49352          * The function used to validate URLs
49353          * @param {String} value The URL
49354          */
49355         'url' : function(v){
49356             return url.test(v);
49357         },
49358         /**
49359          * The error text to display when the url validation function returns false
49360          * @type String
49361          */
49362         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49363         
49364         /**
49365          * The function used to validate alpha values
49366          * @param {String} value The value
49367          */
49368         'alpha' : function(v){
49369             return alpha.test(v);
49370         },
49371         /**
49372          * The error text to display when the alpha validation function returns false
49373          * @type String
49374          */
49375         'alphaText' : 'This field should only contain letters and _',
49376         /**
49377          * The keystroke filter mask to be applied on alpha input
49378          * @type RegExp
49379          */
49380         'alphaMask' : /[a-z_]/i,
49381
49382         /**
49383          * The function used to validate alphanumeric values
49384          * @param {String} value The value
49385          */
49386         'alphanum' : function(v){
49387             return alphanum.test(v);
49388         },
49389         /**
49390          * The error text to display when the alphanumeric validation function returns false
49391          * @type String
49392          */
49393         'alphanumText' : 'This field should only contain letters, numbers and _',
49394         /**
49395          * The keystroke filter mask to be applied on alphanumeric input
49396          * @type RegExp
49397          */
49398         'alphanumMask' : /[a-z0-9_]/i
49399     };
49400 }();//<script type="text/javascript">
49401
49402 /**
49403  * @class Roo.form.FCKeditor
49404  * @extends Roo.form.TextArea
49405  * Wrapper around the FCKEditor http://www.fckeditor.net
49406  * @constructor
49407  * Creates a new FCKeditor
49408  * @param {Object} config Configuration options
49409  */
49410 Roo.form.FCKeditor = function(config){
49411     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49412     this.addEvents({
49413          /**
49414          * @event editorinit
49415          * Fired when the editor is initialized - you can add extra handlers here..
49416          * @param {FCKeditor} this
49417          * @param {Object} the FCK object.
49418          */
49419         editorinit : true
49420     });
49421     
49422     
49423 };
49424 Roo.form.FCKeditor.editors = { };
49425 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49426 {
49427     //defaultAutoCreate : {
49428     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49429     //},
49430     // private
49431     /**
49432      * @cfg {Object} fck options - see fck manual for details.
49433      */
49434     fckconfig : false,
49435     
49436     /**
49437      * @cfg {Object} fck toolbar set (Basic or Default)
49438      */
49439     toolbarSet : 'Basic',
49440     /**
49441      * @cfg {Object} fck BasePath
49442      */ 
49443     basePath : '/fckeditor/',
49444     
49445     
49446     frame : false,
49447     
49448     value : '',
49449     
49450    
49451     onRender : function(ct, position)
49452     {
49453         if(!this.el){
49454             this.defaultAutoCreate = {
49455                 tag: "textarea",
49456                 style:"width:300px;height:60px;",
49457                 autocomplete: "new-password"
49458             };
49459         }
49460         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49461         /*
49462         if(this.grow){
49463             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49464             if(this.preventScrollbars){
49465                 this.el.setStyle("overflow", "hidden");
49466             }
49467             this.el.setHeight(this.growMin);
49468         }
49469         */
49470         //console.log('onrender' + this.getId() );
49471         Roo.form.FCKeditor.editors[this.getId()] = this;
49472          
49473
49474         this.replaceTextarea() ;
49475         
49476     },
49477     
49478     getEditor : function() {
49479         return this.fckEditor;
49480     },
49481     /**
49482      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49483      * @param {Mixed} value The value to set
49484      */
49485     
49486     
49487     setValue : function(value)
49488     {
49489         //console.log('setValue: ' + value);
49490         
49491         if(typeof(value) == 'undefined') { // not sure why this is happending...
49492             return;
49493         }
49494         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49495         
49496         //if(!this.el || !this.getEditor()) {
49497         //    this.value = value;
49498             //this.setValue.defer(100,this,[value]);    
49499         //    return;
49500         //} 
49501         
49502         if(!this.getEditor()) {
49503             return;
49504         }
49505         
49506         this.getEditor().SetData(value);
49507         
49508         //
49509
49510     },
49511
49512     /**
49513      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49514      * @return {Mixed} value The field value
49515      */
49516     getValue : function()
49517     {
49518         
49519         if (this.frame && this.frame.dom.style.display == 'none') {
49520             return Roo.form.FCKeditor.superclass.getValue.call(this);
49521         }
49522         
49523         if(!this.el || !this.getEditor()) {
49524            
49525            // this.getValue.defer(100,this); 
49526             return this.value;
49527         }
49528        
49529         
49530         var value=this.getEditor().GetData();
49531         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49532         return Roo.form.FCKeditor.superclass.getValue.call(this);
49533         
49534
49535     },
49536
49537     /**
49538      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49539      * @return {Mixed} value The field value
49540      */
49541     getRawValue : function()
49542     {
49543         if (this.frame && this.frame.dom.style.display == 'none') {
49544             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49545         }
49546         
49547         if(!this.el || !this.getEditor()) {
49548             //this.getRawValue.defer(100,this); 
49549             return this.value;
49550             return;
49551         }
49552         
49553         
49554         
49555         var value=this.getEditor().GetData();
49556         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49557         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49558          
49559     },
49560     
49561     setSize : function(w,h) {
49562         
49563         
49564         
49565         //if (this.frame && this.frame.dom.style.display == 'none') {
49566         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49567         //    return;
49568         //}
49569         //if(!this.el || !this.getEditor()) {
49570         //    this.setSize.defer(100,this, [w,h]); 
49571         //    return;
49572         //}
49573         
49574         
49575         
49576         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49577         
49578         this.frame.dom.setAttribute('width', w);
49579         this.frame.dom.setAttribute('height', h);
49580         this.frame.setSize(w,h);
49581         
49582     },
49583     
49584     toggleSourceEdit : function(value) {
49585         
49586       
49587          
49588         this.el.dom.style.display = value ? '' : 'none';
49589         this.frame.dom.style.display = value ?  'none' : '';
49590         
49591     },
49592     
49593     
49594     focus: function(tag)
49595     {
49596         if (this.frame.dom.style.display == 'none') {
49597             return Roo.form.FCKeditor.superclass.focus.call(this);
49598         }
49599         if(!this.el || !this.getEditor()) {
49600             this.focus.defer(100,this, [tag]); 
49601             return;
49602         }
49603         
49604         
49605         
49606         
49607         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49608         this.getEditor().Focus();
49609         if (tgs.length) {
49610             if (!this.getEditor().Selection.GetSelection()) {
49611                 this.focus.defer(100,this, [tag]); 
49612                 return;
49613             }
49614             
49615             
49616             var r = this.getEditor().EditorDocument.createRange();
49617             r.setStart(tgs[0],0);
49618             r.setEnd(tgs[0],0);
49619             this.getEditor().Selection.GetSelection().removeAllRanges();
49620             this.getEditor().Selection.GetSelection().addRange(r);
49621             this.getEditor().Focus();
49622         }
49623         
49624     },
49625     
49626     
49627     
49628     replaceTextarea : function()
49629     {
49630         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49631             return ;
49632         }
49633         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49634         //{
49635             // We must check the elements firstly using the Id and then the name.
49636         var oTextarea = document.getElementById( this.getId() );
49637         
49638         var colElementsByName = document.getElementsByName( this.getId() ) ;
49639          
49640         oTextarea.style.display = 'none' ;
49641
49642         if ( oTextarea.tabIndex ) {            
49643             this.TabIndex = oTextarea.tabIndex ;
49644         }
49645         
49646         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49647         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49648         this.frame = Roo.get(this.getId() + '___Frame')
49649     },
49650     
49651     _getConfigHtml : function()
49652     {
49653         var sConfig = '' ;
49654
49655         for ( var o in this.fckconfig ) {
49656             sConfig += sConfig.length > 0  ? '&amp;' : '';
49657             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49658         }
49659
49660         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49661     },
49662     
49663     
49664     _getIFrameHtml : function()
49665     {
49666         var sFile = 'fckeditor.html' ;
49667         /* no idea what this is about..
49668         try
49669         {
49670             if ( (/fcksource=true/i).test( window.top.location.search ) )
49671                 sFile = 'fckeditor.original.html' ;
49672         }
49673         catch (e) { 
49674         */
49675
49676         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49677         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49678         
49679         
49680         var html = '<iframe id="' + this.getId() +
49681             '___Frame" src="' + sLink +
49682             '" width="' + this.width +
49683             '" height="' + this.height + '"' +
49684             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49685             ' frameborder="0" scrolling="no"></iframe>' ;
49686
49687         return html ;
49688     },
49689     
49690     _insertHtmlBefore : function( html, element )
49691     {
49692         if ( element.insertAdjacentHTML )       {
49693             // IE
49694             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49695         } else { // Gecko
49696             var oRange = document.createRange() ;
49697             oRange.setStartBefore( element ) ;
49698             var oFragment = oRange.createContextualFragment( html );
49699             element.parentNode.insertBefore( oFragment, element ) ;
49700         }
49701     }
49702     
49703     
49704   
49705     
49706     
49707     
49708     
49709
49710 });
49711
49712 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49713
49714 function FCKeditor_OnComplete(editorInstance){
49715     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49716     f.fckEditor = editorInstance;
49717     //console.log("loaded");
49718     f.fireEvent('editorinit', f, editorInstance);
49719
49720   
49721
49722  
49723
49724
49725
49726
49727
49728
49729
49730
49731
49732
49733
49734
49735
49736
49737
49738 //<script type="text/javascript">
49739 /**
49740  * @class Roo.form.GridField
49741  * @extends Roo.form.Field
49742  * Embed a grid (or editable grid into a form)
49743  * STATUS ALPHA
49744  * 
49745  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49746  * it needs 
49747  * xgrid.store = Roo.data.Store
49748  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49749  * xgrid.store.reader = Roo.data.JsonReader 
49750  * 
49751  * 
49752  * @constructor
49753  * Creates a new GridField
49754  * @param {Object} config Configuration options
49755  */
49756 Roo.form.GridField = function(config){
49757     Roo.form.GridField.superclass.constructor.call(this, config);
49758      
49759 };
49760
49761 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49762     /**
49763      * @cfg {Number} width  - used to restrict width of grid..
49764      */
49765     width : 100,
49766     /**
49767      * @cfg {Number} height - used to restrict height of grid..
49768      */
49769     height : 50,
49770      /**
49771      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49772          * 
49773          *}
49774      */
49775     xgrid : false, 
49776     /**
49777      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49778      * {tag: "input", type: "checkbox", autocomplete: "off"})
49779      */
49780    // defaultAutoCreate : { tag: 'div' },
49781     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49782     /**
49783      * @cfg {String} addTitle Text to include for adding a title.
49784      */
49785     addTitle : false,
49786     //
49787     onResize : function(){
49788         Roo.form.Field.superclass.onResize.apply(this, arguments);
49789     },
49790
49791     initEvents : function(){
49792         // Roo.form.Checkbox.superclass.initEvents.call(this);
49793         // has no events...
49794        
49795     },
49796
49797
49798     getResizeEl : function(){
49799         return this.wrap;
49800     },
49801
49802     getPositionEl : function(){
49803         return this.wrap;
49804     },
49805
49806     // private
49807     onRender : function(ct, position){
49808         
49809         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49810         var style = this.style;
49811         delete this.style;
49812         
49813         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49814         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49815         this.viewEl = this.wrap.createChild({ tag: 'div' });
49816         if (style) {
49817             this.viewEl.applyStyles(style);
49818         }
49819         if (this.width) {
49820             this.viewEl.setWidth(this.width);
49821         }
49822         if (this.height) {
49823             this.viewEl.setHeight(this.height);
49824         }
49825         //if(this.inputValue !== undefined){
49826         //this.setValue(this.value);
49827         
49828         
49829         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49830         
49831         
49832         this.grid.render();
49833         this.grid.getDataSource().on('remove', this.refreshValue, this);
49834         this.grid.getDataSource().on('update', this.refreshValue, this);
49835         this.grid.on('afteredit', this.refreshValue, this);
49836  
49837     },
49838      
49839     
49840     /**
49841      * Sets the value of the item. 
49842      * @param {String} either an object  or a string..
49843      */
49844     setValue : function(v){
49845         //this.value = v;
49846         v = v || []; // empty set..
49847         // this does not seem smart - it really only affects memoryproxy grids..
49848         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49849             var ds = this.grid.getDataSource();
49850             // assumes a json reader..
49851             var data = {}
49852             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49853             ds.loadData( data);
49854         }
49855         // clear selection so it does not get stale.
49856         if (this.grid.sm) { 
49857             this.grid.sm.clearSelections();
49858         }
49859         
49860         Roo.form.GridField.superclass.setValue.call(this, v);
49861         this.refreshValue();
49862         // should load data in the grid really....
49863     },
49864     
49865     // private
49866     refreshValue: function() {
49867          var val = [];
49868         this.grid.getDataSource().each(function(r) {
49869             val.push(r.data);
49870         });
49871         this.el.dom.value = Roo.encode(val);
49872     }
49873     
49874      
49875     
49876     
49877 });/*
49878  * Based on:
49879  * Ext JS Library 1.1.1
49880  * Copyright(c) 2006-2007, Ext JS, LLC.
49881  *
49882  * Originally Released Under LGPL - original licence link has changed is not relivant.
49883  *
49884  * Fork - LGPL
49885  * <script type="text/javascript">
49886  */
49887 /**
49888  * @class Roo.form.DisplayField
49889  * @extends Roo.form.Field
49890  * A generic Field to display non-editable data.
49891  * @cfg {Boolean} closable (true|false) default false
49892  * @constructor
49893  * Creates a new Display Field item.
49894  * @param {Object} config Configuration options
49895  */
49896 Roo.form.DisplayField = function(config){
49897     Roo.form.DisplayField.superclass.constructor.call(this, config);
49898     
49899     this.addEvents({
49900         /**
49901          * @event close
49902          * Fires after the click the close btn
49903              * @param {Roo.form.DisplayField} this
49904              */
49905         close : true
49906     });
49907 };
49908
49909 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49910     inputType:      'hidden',
49911     allowBlank:     true,
49912     readOnly:         true,
49913     
49914  
49915     /**
49916      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49917      */
49918     focusClass : undefined,
49919     /**
49920      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49921      */
49922     fieldClass: 'x-form-field',
49923     
49924      /**
49925      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49926      */
49927     valueRenderer: undefined,
49928     
49929     width: 100,
49930     /**
49931      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49932      * {tag: "input", type: "checkbox", autocomplete: "off"})
49933      */
49934      
49935  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49936  
49937     closable : false,
49938     
49939     onResize : function(){
49940         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49941         
49942     },
49943
49944     initEvents : function(){
49945         // Roo.form.Checkbox.superclass.initEvents.call(this);
49946         // has no events...
49947         
49948         if(this.closable){
49949             this.closeEl.on('click', this.onClose, this);
49950         }
49951        
49952     },
49953
49954
49955     getResizeEl : function(){
49956         return this.wrap;
49957     },
49958
49959     getPositionEl : function(){
49960         return this.wrap;
49961     },
49962
49963     // private
49964     onRender : function(ct, position){
49965         
49966         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49967         //if(this.inputValue !== undefined){
49968         this.wrap = this.el.wrap();
49969         
49970         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49971         
49972         if(this.closable){
49973             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49974         }
49975         
49976         if (this.bodyStyle) {
49977             this.viewEl.applyStyles(this.bodyStyle);
49978         }
49979         //this.viewEl.setStyle('padding', '2px');
49980         
49981         this.setValue(this.value);
49982         
49983     },
49984 /*
49985     // private
49986     initValue : Roo.emptyFn,
49987
49988   */
49989
49990         // private
49991     onClick : function(){
49992         
49993     },
49994
49995     /**
49996      * Sets the checked state of the checkbox.
49997      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49998      */
49999     setValue : function(v){
50000         this.value = v;
50001         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50002         // this might be called before we have a dom element..
50003         if (!this.viewEl) {
50004             return;
50005         }
50006         this.viewEl.dom.innerHTML = html;
50007         Roo.form.DisplayField.superclass.setValue.call(this, v);
50008
50009     },
50010     
50011     onClose : function(e)
50012     {
50013         e.preventDefault();
50014         
50015         this.fireEvent('close', this);
50016     }
50017 });/*
50018  * 
50019  * Licence- LGPL
50020  * 
50021  */
50022
50023 /**
50024  * @class Roo.form.DayPicker
50025  * @extends Roo.form.Field
50026  * A Day picker show [M] [T] [W] ....
50027  * @constructor
50028  * Creates a new Day Picker
50029  * @param {Object} config Configuration options
50030  */
50031 Roo.form.DayPicker= function(config){
50032     Roo.form.DayPicker.superclass.constructor.call(this, config);
50033      
50034 };
50035
50036 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50037     /**
50038      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50039      */
50040     focusClass : undefined,
50041     /**
50042      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50043      */
50044     fieldClass: "x-form-field",
50045    
50046     /**
50047      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50048      * {tag: "input", type: "checkbox", autocomplete: "off"})
50049      */
50050     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50051     
50052    
50053     actionMode : 'viewEl', 
50054     //
50055     // private
50056  
50057     inputType : 'hidden',
50058     
50059      
50060     inputElement: false, // real input element?
50061     basedOn: false, // ????
50062     
50063     isFormField: true, // not sure where this is needed!!!!
50064
50065     onResize : function(){
50066         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50067         if(!this.boxLabel){
50068             this.el.alignTo(this.wrap, 'c-c');
50069         }
50070     },
50071
50072     initEvents : function(){
50073         Roo.form.Checkbox.superclass.initEvents.call(this);
50074         this.el.on("click", this.onClick,  this);
50075         this.el.on("change", this.onClick,  this);
50076     },
50077
50078
50079     getResizeEl : function(){
50080         return this.wrap;
50081     },
50082
50083     getPositionEl : function(){
50084         return this.wrap;
50085     },
50086
50087     
50088     // private
50089     onRender : function(ct, position){
50090         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50091        
50092         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50093         
50094         var r1 = '<table><tr>';
50095         var r2 = '<tr class="x-form-daypick-icons">';
50096         for (var i=0; i < 7; i++) {
50097             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50098             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50099         }
50100         
50101         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50102         viewEl.select('img').on('click', this.onClick, this);
50103         this.viewEl = viewEl;   
50104         
50105         
50106         // this will not work on Chrome!!!
50107         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50108         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50109         
50110         
50111           
50112
50113     },
50114
50115     // private
50116     initValue : Roo.emptyFn,
50117
50118     /**
50119      * Returns the checked state of the checkbox.
50120      * @return {Boolean} True if checked, else false
50121      */
50122     getValue : function(){
50123         return this.el.dom.value;
50124         
50125     },
50126
50127         // private
50128     onClick : function(e){ 
50129         //this.setChecked(!this.checked);
50130         Roo.get(e.target).toggleClass('x-menu-item-checked');
50131         this.refreshValue();
50132         //if(this.el.dom.checked != this.checked){
50133         //    this.setValue(this.el.dom.checked);
50134        // }
50135     },
50136     
50137     // private
50138     refreshValue : function()
50139     {
50140         var val = '';
50141         this.viewEl.select('img',true).each(function(e,i,n)  {
50142             val += e.is(".x-menu-item-checked") ? String(n) : '';
50143         });
50144         this.setValue(val, true);
50145     },
50146
50147     /**
50148      * Sets the checked state of the checkbox.
50149      * On is always based on a string comparison between inputValue and the param.
50150      * @param {Boolean/String} value - the value to set 
50151      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50152      */
50153     setValue : function(v,suppressEvent){
50154         if (!this.el.dom) {
50155             return;
50156         }
50157         var old = this.el.dom.value ;
50158         this.el.dom.value = v;
50159         if (suppressEvent) {
50160             return ;
50161         }
50162          
50163         // update display..
50164         this.viewEl.select('img',true).each(function(e,i,n)  {
50165             
50166             var on = e.is(".x-menu-item-checked");
50167             var newv = v.indexOf(String(n)) > -1;
50168             if (on != newv) {
50169                 e.toggleClass('x-menu-item-checked');
50170             }
50171             
50172         });
50173         
50174         
50175         this.fireEvent('change', this, v, old);
50176         
50177         
50178     },
50179    
50180     // handle setting of hidden value by some other method!!?!?
50181     setFromHidden: function()
50182     {
50183         if(!this.el){
50184             return;
50185         }
50186         //console.log("SET FROM HIDDEN");
50187         //alert('setFrom hidden');
50188         this.setValue(this.el.dom.value);
50189     },
50190     
50191     onDestroy : function()
50192     {
50193         if(this.viewEl){
50194             Roo.get(this.viewEl).remove();
50195         }
50196          
50197         Roo.form.DayPicker.superclass.onDestroy.call(this);
50198     }
50199
50200 });/*
50201  * RooJS Library 1.1.1
50202  * Copyright(c) 2008-2011  Alan Knowles
50203  *
50204  * License - LGPL
50205  */
50206  
50207
50208 /**
50209  * @class Roo.form.ComboCheck
50210  * @extends Roo.form.ComboBox
50211  * A combobox for multiple select items.
50212  *
50213  * FIXME - could do with a reset button..
50214  * 
50215  * @constructor
50216  * Create a new ComboCheck
50217  * @param {Object} config Configuration options
50218  */
50219 Roo.form.ComboCheck = function(config){
50220     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50221     // should verify some data...
50222     // like
50223     // hiddenName = required..
50224     // displayField = required
50225     // valudField == required
50226     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50227     var _t = this;
50228     Roo.each(req, function(e) {
50229         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50230             throw "Roo.form.ComboCheck : missing value for: " + e;
50231         }
50232     });
50233     
50234     
50235 };
50236
50237 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50238      
50239      
50240     editable : false,
50241      
50242     selectedClass: 'x-menu-item-checked', 
50243     
50244     // private
50245     onRender : function(ct, position){
50246         var _t = this;
50247         
50248         
50249         
50250         if(!this.tpl){
50251             var cls = 'x-combo-list';
50252
50253             
50254             this.tpl =  new Roo.Template({
50255                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50256                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50257                    '<span>{' + this.displayField + '}</span>' +
50258                     '</div>' 
50259                 
50260             });
50261         }
50262  
50263         
50264         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50265         this.view.singleSelect = false;
50266         this.view.multiSelect = true;
50267         this.view.toggleSelect = true;
50268         this.pageTb.add(new Roo.Toolbar.Fill(), {
50269             
50270             text: 'Done',
50271             handler: function()
50272             {
50273                 _t.collapse();
50274             }
50275         });
50276     },
50277     
50278     onViewOver : function(e, t){
50279         // do nothing...
50280         return;
50281         
50282     },
50283     
50284     onViewClick : function(doFocus,index){
50285         return;
50286         
50287     },
50288     select: function () {
50289         //Roo.log("SELECT CALLED");
50290     },
50291      
50292     selectByValue : function(xv, scrollIntoView){
50293         var ar = this.getValueArray();
50294         var sels = [];
50295         
50296         Roo.each(ar, function(v) {
50297             if(v === undefined || v === null){
50298                 return;
50299             }
50300             var r = this.findRecord(this.valueField, v);
50301             if(r){
50302                 sels.push(this.store.indexOf(r))
50303                 
50304             }
50305         },this);
50306         this.view.select(sels);
50307         return false;
50308     },
50309     
50310     
50311     
50312     onSelect : function(record, index){
50313        // Roo.log("onselect Called");
50314        // this is only called by the clear button now..
50315         this.view.clearSelections();
50316         this.setValue('[]');
50317         if (this.value != this.valueBefore) {
50318             this.fireEvent('change', this, this.value, this.valueBefore);
50319             this.valueBefore = this.value;
50320         }
50321     },
50322     getValueArray : function()
50323     {
50324         var ar = [] ;
50325         
50326         try {
50327             //Roo.log(this.value);
50328             if (typeof(this.value) == 'undefined') {
50329                 return [];
50330             }
50331             var ar = Roo.decode(this.value);
50332             return  ar instanceof Array ? ar : []; //?? valid?
50333             
50334         } catch(e) {
50335             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50336             return [];
50337         }
50338          
50339     },
50340     expand : function ()
50341     {
50342         
50343         Roo.form.ComboCheck.superclass.expand.call(this);
50344         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50345         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50346         
50347
50348     },
50349     
50350     collapse : function(){
50351         Roo.form.ComboCheck.superclass.collapse.call(this);
50352         var sl = this.view.getSelectedIndexes();
50353         var st = this.store;
50354         var nv = [];
50355         var tv = [];
50356         var r;
50357         Roo.each(sl, function(i) {
50358             r = st.getAt(i);
50359             nv.push(r.get(this.valueField));
50360         },this);
50361         this.setValue(Roo.encode(nv));
50362         if (this.value != this.valueBefore) {
50363
50364             this.fireEvent('change', this, this.value, this.valueBefore);
50365             this.valueBefore = this.value;
50366         }
50367         
50368     },
50369     
50370     setValue : function(v){
50371         // Roo.log(v);
50372         this.value = v;
50373         
50374         var vals = this.getValueArray();
50375         var tv = [];
50376         Roo.each(vals, function(k) {
50377             var r = this.findRecord(this.valueField, k);
50378             if(r){
50379                 tv.push(r.data[this.displayField]);
50380             }else if(this.valueNotFoundText !== undefined){
50381                 tv.push( this.valueNotFoundText );
50382             }
50383         },this);
50384        // Roo.log(tv);
50385         
50386         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50387         this.hiddenField.value = v;
50388         this.value = v;
50389     }
50390     
50391 });/*
50392  * Based on:
50393  * Ext JS Library 1.1.1
50394  * Copyright(c) 2006-2007, Ext JS, LLC.
50395  *
50396  * Originally Released Under LGPL - original licence link has changed is not relivant.
50397  *
50398  * Fork - LGPL
50399  * <script type="text/javascript">
50400  */
50401  
50402 /**
50403  * @class Roo.form.Signature
50404  * @extends Roo.form.Field
50405  * Signature field.  
50406  * @constructor
50407  * 
50408  * @param {Object} config Configuration options
50409  */
50410
50411 Roo.form.Signature = function(config){
50412     Roo.form.Signature.superclass.constructor.call(this, config);
50413     
50414     this.addEvents({// not in used??
50415          /**
50416          * @event confirm
50417          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50418              * @param {Roo.form.Signature} combo This combo box
50419              */
50420         'confirm' : true,
50421         /**
50422          * @event reset
50423          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50424              * @param {Roo.form.ComboBox} combo This combo box
50425              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50426              */
50427         'reset' : true
50428     });
50429 };
50430
50431 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50432     /**
50433      * @cfg {Object} labels Label to use when rendering a form.
50434      * defaults to 
50435      * labels : { 
50436      *      clear : "Clear",
50437      *      confirm : "Confirm"
50438      *  }
50439      */
50440     labels : { 
50441         clear : "Clear",
50442         confirm : "Confirm"
50443     },
50444     /**
50445      * @cfg {Number} width The signature panel width (defaults to 300)
50446      */
50447     width: 300,
50448     /**
50449      * @cfg {Number} height The signature panel height (defaults to 100)
50450      */
50451     height : 100,
50452     /**
50453      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50454      */
50455     allowBlank : false,
50456     
50457     //private
50458     // {Object} signPanel The signature SVG panel element (defaults to {})
50459     signPanel : {},
50460     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50461     isMouseDown : false,
50462     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50463     isConfirmed : false,
50464     // {String} signatureTmp SVG mapping string (defaults to empty string)
50465     signatureTmp : '',
50466     
50467     
50468     defaultAutoCreate : { // modified by initCompnoent..
50469         tag: "input",
50470         type:"hidden"
50471     },
50472
50473     // private
50474     onRender : function(ct, position){
50475         
50476         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50477         
50478         this.wrap = this.el.wrap({
50479             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50480         });
50481         
50482         this.createToolbar(this);
50483         this.signPanel = this.wrap.createChild({
50484                 tag: 'div',
50485                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50486             }, this.el
50487         );
50488             
50489         this.svgID = Roo.id();
50490         this.svgEl = this.signPanel.createChild({
50491               xmlns : 'http://www.w3.org/2000/svg',
50492               tag : 'svg',
50493               id : this.svgID + "-svg",
50494               width: this.width,
50495               height: this.height,
50496               viewBox: '0 0 '+this.width+' '+this.height,
50497               cn : [
50498                 {
50499                     tag: "rect",
50500                     id: this.svgID + "-svg-r",
50501                     width: this.width,
50502                     height: this.height,
50503                     fill: "#ffa"
50504                 },
50505                 {
50506                     tag: "line",
50507                     id: this.svgID + "-svg-l",
50508                     x1: "0", // start
50509                     y1: (this.height*0.8), // start set the line in 80% of height
50510                     x2: this.width, // end
50511                     y2: (this.height*0.8), // end set the line in 80% of height
50512                     'stroke': "#666",
50513                     'stroke-width': "1",
50514                     'stroke-dasharray': "3",
50515                     'shape-rendering': "crispEdges",
50516                     'pointer-events': "none"
50517                 },
50518                 {
50519                     tag: "path",
50520                     id: this.svgID + "-svg-p",
50521                     'stroke': "navy",
50522                     'stroke-width': "3",
50523                     'fill': "none",
50524                     'pointer-events': 'none'
50525                 }
50526               ]
50527         });
50528         this.createSVG();
50529         this.svgBox = this.svgEl.dom.getScreenCTM();
50530     },
50531     createSVG : function(){ 
50532         var svg = this.signPanel;
50533         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50534         var t = this;
50535
50536         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50537         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50538         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50539         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50540         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50541         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50542         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50543         
50544     },
50545     isTouchEvent : function(e){
50546         return e.type.match(/^touch/);
50547     },
50548     getCoords : function (e) {
50549         var pt    = this.svgEl.dom.createSVGPoint();
50550         pt.x = e.clientX; 
50551         pt.y = e.clientY;
50552         if (this.isTouchEvent(e)) {
50553             pt.x =  e.targetTouches[0].clientX;
50554             pt.y = e.targetTouches[0].clientY;
50555         }
50556         var a = this.svgEl.dom.getScreenCTM();
50557         var b = a.inverse();
50558         var mx = pt.matrixTransform(b);
50559         return mx.x + ',' + mx.y;
50560     },
50561     //mouse event headler 
50562     down : function (e) {
50563         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50564         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50565         
50566         this.isMouseDown = true;
50567         
50568         e.preventDefault();
50569     },
50570     move : function (e) {
50571         if (this.isMouseDown) {
50572             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50573             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50574         }
50575         
50576         e.preventDefault();
50577     },
50578     up : function (e) {
50579         this.isMouseDown = false;
50580         var sp = this.signatureTmp.split(' ');
50581         
50582         if(sp.length > 1){
50583             if(!sp[sp.length-2].match(/^L/)){
50584                 sp.pop();
50585                 sp.pop();
50586                 sp.push("");
50587                 this.signatureTmp = sp.join(" ");
50588             }
50589         }
50590         if(this.getValue() != this.signatureTmp){
50591             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50592             this.isConfirmed = false;
50593         }
50594         e.preventDefault();
50595     },
50596     
50597     /**
50598      * Protected method that will not generally be called directly. It
50599      * is called when the editor creates its toolbar. Override this method if you need to
50600      * add custom toolbar buttons.
50601      * @param {HtmlEditor} editor
50602      */
50603     createToolbar : function(editor){
50604          function btn(id, toggle, handler){
50605             var xid = fid + '-'+ id ;
50606             return {
50607                 id : xid,
50608                 cmd : id,
50609                 cls : 'x-btn-icon x-edit-'+id,
50610                 enableToggle:toggle !== false,
50611                 scope: editor, // was editor...
50612                 handler:handler||editor.relayBtnCmd,
50613                 clickEvent:'mousedown',
50614                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50615                 tabIndex:-1
50616             };
50617         }
50618         
50619         
50620         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50621         this.tb = tb;
50622         this.tb.add(
50623            {
50624                 cls : ' x-signature-btn x-signature-'+id,
50625                 scope: editor, // was editor...
50626                 handler: this.reset,
50627                 clickEvent:'mousedown',
50628                 text: this.labels.clear
50629             },
50630             {
50631                  xtype : 'Fill',
50632                  xns: Roo.Toolbar
50633             }, 
50634             {
50635                 cls : '  x-signature-btn x-signature-'+id,
50636                 scope: editor, // was editor...
50637                 handler: this.confirmHandler,
50638                 clickEvent:'mousedown',
50639                 text: this.labels.confirm
50640             }
50641         );
50642     
50643     },
50644     //public
50645     /**
50646      * when user is clicked confirm then show this image.....
50647      * 
50648      * @return {String} Image Data URI
50649      */
50650     getImageDataURI : function(){
50651         var svg = this.svgEl.dom.parentNode.innerHTML;
50652         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50653         return src; 
50654     },
50655     /**
50656      * 
50657      * @return {Boolean} this.isConfirmed
50658      */
50659     getConfirmed : function(){
50660         return this.isConfirmed;
50661     },
50662     /**
50663      * 
50664      * @return {Number} this.width
50665      */
50666     getWidth : function(){
50667         return this.width;
50668     },
50669     /**
50670      * 
50671      * @return {Number} this.height
50672      */
50673     getHeight : function(){
50674         return this.height;
50675     },
50676     // private
50677     getSignature : function(){
50678         return this.signatureTmp;
50679     },
50680     // private
50681     reset : function(){
50682         this.signatureTmp = '';
50683         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50684         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50685         this.isConfirmed = false;
50686         Roo.form.Signature.superclass.reset.call(this);
50687     },
50688     setSignature : function(s){
50689         this.signatureTmp = s;
50690         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50691         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50692         this.setValue(s);
50693         this.isConfirmed = false;
50694         Roo.form.Signature.superclass.reset.call(this);
50695     }, 
50696     test : function(){
50697 //        Roo.log(this.signPanel.dom.contentWindow.up())
50698     },
50699     //private
50700     setConfirmed : function(){
50701         
50702         
50703         
50704 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50705     },
50706     // private
50707     confirmHandler : function(){
50708         if(!this.getSignature()){
50709             return;
50710         }
50711         
50712         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50713         this.setValue(this.getSignature());
50714         this.isConfirmed = true;
50715         
50716         this.fireEvent('confirm', this);
50717     },
50718     // private
50719     // Subclasses should provide the validation implementation by overriding this
50720     validateValue : function(value){
50721         if(this.allowBlank){
50722             return true;
50723         }
50724         
50725         if(this.isConfirmed){
50726             return true;
50727         }
50728         return false;
50729     }
50730 });/*
50731  * Based on:
50732  * Ext JS Library 1.1.1
50733  * Copyright(c) 2006-2007, Ext JS, LLC.
50734  *
50735  * Originally Released Under LGPL - original licence link has changed is not relivant.
50736  *
50737  * Fork - LGPL
50738  * <script type="text/javascript">
50739  */
50740  
50741
50742 /**
50743  * @class Roo.form.ComboBox
50744  * @extends Roo.form.TriggerField
50745  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50746  * @constructor
50747  * Create a new ComboBox.
50748  * @param {Object} config Configuration options
50749  */
50750 Roo.form.Select = function(config){
50751     Roo.form.Select.superclass.constructor.call(this, config);
50752      
50753 };
50754
50755 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50756     /**
50757      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50758      */
50759     /**
50760      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50761      * rendering into an Roo.Editor, defaults to false)
50762      */
50763     /**
50764      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50765      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50766      */
50767     /**
50768      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50769      */
50770     /**
50771      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50772      * the dropdown list (defaults to undefined, with no header element)
50773      */
50774
50775      /**
50776      * @cfg {String/Roo.Template} tpl The template to use to render the output
50777      */
50778      
50779     // private
50780     defaultAutoCreate : {tag: "select"  },
50781     /**
50782      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50783      */
50784     listWidth: undefined,
50785     /**
50786      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50787      * mode = 'remote' or 'text' if mode = 'local')
50788      */
50789     displayField: undefined,
50790     /**
50791      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50792      * mode = 'remote' or 'value' if mode = 'local'). 
50793      * Note: use of a valueField requires the user make a selection
50794      * in order for a value to be mapped.
50795      */
50796     valueField: undefined,
50797     
50798     
50799     /**
50800      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50801      * field's data value (defaults to the underlying DOM element's name)
50802      */
50803     hiddenName: undefined,
50804     /**
50805      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50806      */
50807     listClass: '',
50808     /**
50809      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50810      */
50811     selectedClass: 'x-combo-selected',
50812     /**
50813      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50814      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50815      * which displays a downward arrow icon).
50816      */
50817     triggerClass : 'x-form-arrow-trigger',
50818     /**
50819      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50820      */
50821     shadow:'sides',
50822     /**
50823      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50824      * anchor positions (defaults to 'tl-bl')
50825      */
50826     listAlign: 'tl-bl?',
50827     /**
50828      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50829      */
50830     maxHeight: 300,
50831     /**
50832      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50833      * query specified by the allQuery config option (defaults to 'query')
50834      */
50835     triggerAction: 'query',
50836     /**
50837      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50838      * (defaults to 4, does not apply if editable = false)
50839      */
50840     minChars : 4,
50841     /**
50842      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50843      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50844      */
50845     typeAhead: false,
50846     /**
50847      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50848      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50849      */
50850     queryDelay: 500,
50851     /**
50852      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50853      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50854      */
50855     pageSize: 0,
50856     /**
50857      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50858      * when editable = true (defaults to false)
50859      */
50860     selectOnFocus:false,
50861     /**
50862      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50863      */
50864     queryParam: 'query',
50865     /**
50866      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50867      * when mode = 'remote' (defaults to 'Loading...')
50868      */
50869     loadingText: 'Loading...',
50870     /**
50871      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50872      */
50873     resizable: false,
50874     /**
50875      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50876      */
50877     handleHeight : 8,
50878     /**
50879      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50880      * traditional select (defaults to true)
50881      */
50882     editable: true,
50883     /**
50884      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50885      */
50886     allQuery: '',
50887     /**
50888      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50889      */
50890     mode: 'remote',
50891     /**
50892      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50893      * listWidth has a higher value)
50894      */
50895     minListWidth : 70,
50896     /**
50897      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50898      * allow the user to set arbitrary text into the field (defaults to false)
50899      */
50900     forceSelection:false,
50901     /**
50902      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50903      * if typeAhead = true (defaults to 250)
50904      */
50905     typeAheadDelay : 250,
50906     /**
50907      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50908      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50909      */
50910     valueNotFoundText : undefined,
50911     
50912     /**
50913      * @cfg {String} defaultValue The value displayed after loading the store.
50914      */
50915     defaultValue: '',
50916     
50917     /**
50918      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50919      */
50920     blockFocus : false,
50921     
50922     /**
50923      * @cfg {Boolean} disableClear Disable showing of clear button.
50924      */
50925     disableClear : false,
50926     /**
50927      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50928      */
50929     alwaysQuery : false,
50930     
50931     //private
50932     addicon : false,
50933     editicon: false,
50934     
50935     // element that contains real text value.. (when hidden is used..)
50936      
50937     // private
50938     onRender : function(ct, position){
50939         Roo.form.Field.prototype.onRender.call(this, ct, position);
50940         
50941         if(this.store){
50942             this.store.on('beforeload', this.onBeforeLoad, this);
50943             this.store.on('load', this.onLoad, this);
50944             this.store.on('loadexception', this.onLoadException, this);
50945             this.store.load({});
50946         }
50947         
50948         
50949         
50950     },
50951
50952     // private
50953     initEvents : function(){
50954         //Roo.form.ComboBox.superclass.initEvents.call(this);
50955  
50956     },
50957
50958     onDestroy : function(){
50959        
50960         if(this.store){
50961             this.store.un('beforeload', this.onBeforeLoad, this);
50962             this.store.un('load', this.onLoad, this);
50963             this.store.un('loadexception', this.onLoadException, this);
50964         }
50965         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50966     },
50967
50968     // private
50969     fireKey : function(e){
50970         if(e.isNavKeyPress() && !this.list.isVisible()){
50971             this.fireEvent("specialkey", this, e);
50972         }
50973     },
50974
50975     // private
50976     onResize: function(w, h){
50977         
50978         return; 
50979     
50980         
50981     },
50982
50983     /**
50984      * Allow or prevent the user from directly editing the field text.  If false is passed,
50985      * the user will only be able to select from the items defined in the dropdown list.  This method
50986      * is the runtime equivalent of setting the 'editable' config option at config time.
50987      * @param {Boolean} value True to allow the user to directly edit the field text
50988      */
50989     setEditable : function(value){
50990          
50991     },
50992
50993     // private
50994     onBeforeLoad : function(){
50995         
50996         Roo.log("Select before load");
50997         return;
50998     
50999         this.innerList.update(this.loadingText ?
51000                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51001         //this.restrictHeight();
51002         this.selectedIndex = -1;
51003     },
51004
51005     // private
51006     onLoad : function(){
51007
51008     
51009         var dom = this.el.dom;
51010         dom.innerHTML = '';
51011          var od = dom.ownerDocument;
51012          
51013         if (this.emptyText) {
51014             var op = od.createElement('option');
51015             op.setAttribute('value', '');
51016             op.innerHTML = String.format('{0}', this.emptyText);
51017             dom.appendChild(op);
51018         }
51019         if(this.store.getCount() > 0){
51020            
51021             var vf = this.valueField;
51022             var df = this.displayField;
51023             this.store.data.each(function(r) {
51024                 // which colmsn to use... testing - cdoe / title..
51025                 var op = od.createElement('option');
51026                 op.setAttribute('value', r.data[vf]);
51027                 op.innerHTML = String.format('{0}', r.data[df]);
51028                 dom.appendChild(op);
51029             });
51030             if (typeof(this.defaultValue != 'undefined')) {
51031                 this.setValue(this.defaultValue);
51032             }
51033             
51034              
51035         }else{
51036             //this.onEmptyResults();
51037         }
51038         //this.el.focus();
51039     },
51040     // private
51041     onLoadException : function()
51042     {
51043         dom.innerHTML = '';
51044             
51045         Roo.log("Select on load exception");
51046         return;
51047     
51048         this.collapse();
51049         Roo.log(this.store.reader.jsonData);
51050         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51051             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51052         }
51053         
51054         
51055     },
51056     // private
51057     onTypeAhead : function(){
51058          
51059     },
51060
51061     // private
51062     onSelect : function(record, index){
51063         Roo.log('on select?');
51064         return;
51065         if(this.fireEvent('beforeselect', this, record, index) !== false){
51066             this.setFromData(index > -1 ? record.data : false);
51067             this.collapse();
51068             this.fireEvent('select', this, record, index);
51069         }
51070     },
51071
51072     /**
51073      * Returns the currently selected field value or empty string if no value is set.
51074      * @return {String} value The selected value
51075      */
51076     getValue : function(){
51077         var dom = this.el.dom;
51078         this.value = dom.options[dom.selectedIndex].value;
51079         return this.value;
51080         
51081     },
51082
51083     /**
51084      * Clears any text/value currently set in the field
51085      */
51086     clearValue : function(){
51087         this.value = '';
51088         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51089         
51090     },
51091
51092     /**
51093      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51094      * will be displayed in the field.  If the value does not match the data value of an existing item,
51095      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51096      * Otherwise the field will be blank (although the value will still be set).
51097      * @param {String} value The value to match
51098      */
51099     setValue : function(v){
51100         var d = this.el.dom;
51101         for (var i =0; i < d.options.length;i++) {
51102             if (v == d.options[i].value) {
51103                 d.selectedIndex = i;
51104                 this.value = v;
51105                 return;
51106             }
51107         }
51108         this.clearValue();
51109     },
51110     /**
51111      * @property {Object} the last set data for the element
51112      */
51113     
51114     lastData : false,
51115     /**
51116      * Sets the value of the field based on a object which is related to the record format for the store.
51117      * @param {Object} value the value to set as. or false on reset?
51118      */
51119     setFromData : function(o){
51120         Roo.log('setfrom data?');
51121          
51122         
51123         
51124     },
51125     // private
51126     reset : function(){
51127         this.clearValue();
51128     },
51129     // private
51130     findRecord : function(prop, value){
51131         
51132         return false;
51133     
51134         var record;
51135         if(this.store.getCount() > 0){
51136             this.store.each(function(r){
51137                 if(r.data[prop] == value){
51138                     record = r;
51139                     return false;
51140                 }
51141                 return true;
51142             });
51143         }
51144         return record;
51145     },
51146     
51147     getName: function()
51148     {
51149         // returns hidden if it's set..
51150         if (!this.rendered) {return ''};
51151         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51152         
51153     },
51154      
51155
51156     
51157
51158     // private
51159     onEmptyResults : function(){
51160         Roo.log('empty results');
51161         //this.collapse();
51162     },
51163
51164     /**
51165      * Returns true if the dropdown list is expanded, else false.
51166      */
51167     isExpanded : function(){
51168         return false;
51169     },
51170
51171     /**
51172      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51173      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51174      * @param {String} value The data value of the item to select
51175      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51176      * selected item if it is not currently in view (defaults to true)
51177      * @return {Boolean} True if the value matched an item in the list, else false
51178      */
51179     selectByValue : function(v, scrollIntoView){
51180         Roo.log('select By Value');
51181         return false;
51182     
51183         if(v !== undefined && v !== null){
51184             var r = this.findRecord(this.valueField || this.displayField, v);
51185             if(r){
51186                 this.select(this.store.indexOf(r), scrollIntoView);
51187                 return true;
51188             }
51189         }
51190         return false;
51191     },
51192
51193     /**
51194      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51195      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51196      * @param {Number} index The zero-based index of the list item to select
51197      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51198      * selected item if it is not currently in view (defaults to true)
51199      */
51200     select : function(index, scrollIntoView){
51201         Roo.log('select ');
51202         return  ;
51203         
51204         this.selectedIndex = index;
51205         this.view.select(index);
51206         if(scrollIntoView !== false){
51207             var el = this.view.getNode(index);
51208             if(el){
51209                 this.innerList.scrollChildIntoView(el, false);
51210             }
51211         }
51212     },
51213
51214       
51215
51216     // private
51217     validateBlur : function(){
51218         
51219         return;
51220         
51221     },
51222
51223     // private
51224     initQuery : function(){
51225         this.doQuery(this.getRawValue());
51226     },
51227
51228     // private
51229     doForce : function(){
51230         if(this.el.dom.value.length > 0){
51231             this.el.dom.value =
51232                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51233              
51234         }
51235     },
51236
51237     /**
51238      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51239      * query allowing the query action to be canceled if needed.
51240      * @param {String} query The SQL query to execute
51241      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51242      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51243      * saved in the current store (defaults to false)
51244      */
51245     doQuery : function(q, forceAll){
51246         
51247         Roo.log('doQuery?');
51248         if(q === undefined || q === null){
51249             q = '';
51250         }
51251         var qe = {
51252             query: q,
51253             forceAll: forceAll,
51254             combo: this,
51255             cancel:false
51256         };
51257         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51258             return false;
51259         }
51260         q = qe.query;
51261         forceAll = qe.forceAll;
51262         if(forceAll === true || (q.length >= this.minChars)){
51263             if(this.lastQuery != q || this.alwaysQuery){
51264                 this.lastQuery = q;
51265                 if(this.mode == 'local'){
51266                     this.selectedIndex = -1;
51267                     if(forceAll){
51268                         this.store.clearFilter();
51269                     }else{
51270                         this.store.filter(this.displayField, q);
51271                     }
51272                     this.onLoad();
51273                 }else{
51274                     this.store.baseParams[this.queryParam] = q;
51275                     this.store.load({
51276                         params: this.getParams(q)
51277                     });
51278                     this.expand();
51279                 }
51280             }else{
51281                 this.selectedIndex = -1;
51282                 this.onLoad();   
51283             }
51284         }
51285     },
51286
51287     // private
51288     getParams : function(q){
51289         var p = {};
51290         //p[this.queryParam] = q;
51291         if(this.pageSize){
51292             p.start = 0;
51293             p.limit = this.pageSize;
51294         }
51295         return p;
51296     },
51297
51298     /**
51299      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51300      */
51301     collapse : function(){
51302         
51303     },
51304
51305     // private
51306     collapseIf : function(e){
51307         
51308     },
51309
51310     /**
51311      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51312      */
51313     expand : function(){
51314         
51315     } ,
51316
51317     // private
51318      
51319
51320     /** 
51321     * @cfg {Boolean} grow 
51322     * @hide 
51323     */
51324     /** 
51325     * @cfg {Number} growMin 
51326     * @hide 
51327     */
51328     /** 
51329     * @cfg {Number} growMax 
51330     * @hide 
51331     */
51332     /**
51333      * @hide
51334      * @method autoSize
51335      */
51336     
51337     setWidth : function()
51338     {
51339         
51340     },
51341     getResizeEl : function(){
51342         return this.el;
51343     }
51344 });//<script type="text/javasscript">
51345  
51346
51347 /**
51348  * @class Roo.DDView
51349  * A DnD enabled version of Roo.View.
51350  * @param {Element/String} container The Element in which to create the View.
51351  * @param {String} tpl The template string used to create the markup for each element of the View
51352  * @param {Object} config The configuration properties. These include all the config options of
51353  * {@link Roo.View} plus some specific to this class.<br>
51354  * <p>
51355  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51356  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51357  * <p>
51358  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51359 .x-view-drag-insert-above {
51360         border-top:1px dotted #3366cc;
51361 }
51362 .x-view-drag-insert-below {
51363         border-bottom:1px dotted #3366cc;
51364 }
51365 </code></pre>
51366  * 
51367  */
51368  
51369 Roo.DDView = function(container, tpl, config) {
51370     Roo.DDView.superclass.constructor.apply(this, arguments);
51371     this.getEl().setStyle("outline", "0px none");
51372     this.getEl().unselectable();
51373     if (this.dragGroup) {
51374                 this.setDraggable(this.dragGroup.split(","));
51375     }
51376     if (this.dropGroup) {
51377                 this.setDroppable(this.dropGroup.split(","));
51378     }
51379     if (this.deletable) {
51380         this.setDeletable();
51381     }
51382     this.isDirtyFlag = false;
51383         this.addEvents({
51384                 "drop" : true
51385         });
51386 };
51387
51388 Roo.extend(Roo.DDView, Roo.View, {
51389 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51390 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51391 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51392 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51393
51394         isFormField: true,
51395
51396         reset: Roo.emptyFn,
51397         
51398         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51399
51400         validate: function() {
51401                 return true;
51402         },
51403         
51404         destroy: function() {
51405                 this.purgeListeners();
51406                 this.getEl.removeAllListeners();
51407                 this.getEl().remove();
51408                 if (this.dragZone) {
51409                         if (this.dragZone.destroy) {
51410                                 this.dragZone.destroy();
51411                         }
51412                 }
51413                 if (this.dropZone) {
51414                         if (this.dropZone.destroy) {
51415                                 this.dropZone.destroy();
51416                         }
51417                 }
51418         },
51419
51420 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51421         getName: function() {
51422                 return this.name;
51423         },
51424
51425 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51426         setValue: function(v) {
51427                 if (!this.store) {
51428                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51429                 }
51430                 var data = {};
51431                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51432                 this.store.proxy = new Roo.data.MemoryProxy(data);
51433                 this.store.load();
51434         },
51435
51436 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51437         getValue: function() {
51438                 var result = '(';
51439                 this.store.each(function(rec) {
51440                         result += rec.id + ',';
51441                 });
51442                 return result.substr(0, result.length - 1) + ')';
51443         },
51444         
51445         getIds: function() {
51446                 var i = 0, result = new Array(this.store.getCount());
51447                 this.store.each(function(rec) {
51448                         result[i++] = rec.id;
51449                 });
51450                 return result;
51451         },
51452         
51453         isDirty: function() {
51454                 return this.isDirtyFlag;
51455         },
51456
51457 /**
51458  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51459  *      whole Element becomes the target, and this causes the drop gesture to append.
51460  */
51461     getTargetFromEvent : function(e) {
51462                 var target = e.getTarget();
51463                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51464                 target = target.parentNode;
51465                 }
51466                 if (!target) {
51467                         target = this.el.dom.lastChild || this.el.dom;
51468                 }
51469                 return target;
51470     },
51471
51472 /**
51473  *      Create the drag data which consists of an object which has the property "ddel" as
51474  *      the drag proxy element. 
51475  */
51476     getDragData : function(e) {
51477         var target = this.findItemFromChild(e.getTarget());
51478                 if(target) {
51479                         this.handleSelection(e);
51480                         var selNodes = this.getSelectedNodes();
51481             var dragData = {
51482                 source: this,
51483                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51484                 nodes: selNodes,
51485                 records: []
51486                         };
51487                         var selectedIndices = this.getSelectedIndexes();
51488                         for (var i = 0; i < selectedIndices.length; i++) {
51489                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51490                         }
51491                         if (selNodes.length == 1) {
51492                                 dragData.ddel = target.cloneNode(true); // the div element
51493                         } else {
51494                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51495                                 div.className = 'multi-proxy';
51496                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51497                                         div.appendChild(selNodes[i].cloneNode(true));
51498                                 }
51499                                 dragData.ddel = div;
51500                         }
51501             //console.log(dragData)
51502             //console.log(dragData.ddel.innerHTML)
51503                         return dragData;
51504                 }
51505         //console.log('nodragData')
51506                 return false;
51507     },
51508     
51509 /**     Specify to which ddGroup items in this DDView may be dragged. */
51510     setDraggable: function(ddGroup) {
51511         if (ddGroup instanceof Array) {
51512                 Roo.each(ddGroup, this.setDraggable, this);
51513                 return;
51514         }
51515         if (this.dragZone) {
51516                 this.dragZone.addToGroup(ddGroup);
51517         } else {
51518                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51519                                 containerScroll: true,
51520                                 ddGroup: ddGroup 
51521
51522                         });
51523 //                      Draggability implies selection. DragZone's mousedown selects the element.
51524                         if (!this.multiSelect) { this.singleSelect = true; }
51525
51526 //                      Wire the DragZone's handlers up to methods in *this*
51527                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51528                 }
51529     },
51530
51531 /**     Specify from which ddGroup this DDView accepts drops. */
51532     setDroppable: function(ddGroup) {
51533         if (ddGroup instanceof Array) {
51534                 Roo.each(ddGroup, this.setDroppable, this);
51535                 return;
51536         }
51537         if (this.dropZone) {
51538                 this.dropZone.addToGroup(ddGroup);
51539         } else {
51540                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51541                                 containerScroll: true,
51542                                 ddGroup: ddGroup
51543                         });
51544
51545 //                      Wire the DropZone's handlers up to methods in *this*
51546                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51547                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51548                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51549                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51550                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51551                 }
51552     },
51553
51554 /**     Decide whether to drop above or below a View node. */
51555     getDropPoint : function(e, n, dd){
51556         if (n == this.el.dom) { return "above"; }
51557                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51558                 var c = t + (b - t) / 2;
51559                 var y = Roo.lib.Event.getPageY(e);
51560                 if(y <= c) {
51561                         return "above";
51562                 }else{
51563                         return "below";
51564                 }
51565     },
51566
51567     onNodeEnter : function(n, dd, e, data){
51568                 return false;
51569     },
51570     
51571     onNodeOver : function(n, dd, e, data){
51572                 var pt = this.getDropPoint(e, n, dd);
51573                 // set the insert point style on the target node
51574                 var dragElClass = this.dropNotAllowed;
51575                 if (pt) {
51576                         var targetElClass;
51577                         if (pt == "above"){
51578                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51579                                 targetElClass = "x-view-drag-insert-above";
51580                         } else {
51581                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51582                                 targetElClass = "x-view-drag-insert-below";
51583                         }
51584                         if (this.lastInsertClass != targetElClass){
51585                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51586                                 this.lastInsertClass = targetElClass;
51587                         }
51588                 }
51589                 return dragElClass;
51590         },
51591
51592     onNodeOut : function(n, dd, e, data){
51593                 this.removeDropIndicators(n);
51594     },
51595
51596     onNodeDrop : function(n, dd, e, data){
51597         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51598                 return false;
51599         }
51600         var pt = this.getDropPoint(e, n, dd);
51601                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51602                 if (pt == "below") { insertAt++; }
51603                 for (var i = 0; i < data.records.length; i++) {
51604                         var r = data.records[i];
51605                         var dup = this.store.getById(r.id);
51606                         if (dup && (dd != this.dragZone)) {
51607                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51608                         } else {
51609                                 if (data.copy) {
51610                                         this.store.insert(insertAt++, r.copy());
51611                                 } else {
51612                                         data.source.isDirtyFlag = true;
51613                                         r.store.remove(r);
51614                                         this.store.insert(insertAt++, r);
51615                                 }
51616                                 this.isDirtyFlag = true;
51617                         }
51618                 }
51619                 this.dragZone.cachedTarget = null;
51620                 return true;
51621     },
51622
51623     removeDropIndicators : function(n){
51624                 if(n){
51625                         Roo.fly(n).removeClass([
51626                                 "x-view-drag-insert-above",
51627                                 "x-view-drag-insert-below"]);
51628                         this.lastInsertClass = "_noclass";
51629                 }
51630     },
51631
51632 /**
51633  *      Utility method. Add a delete option to the DDView's context menu.
51634  *      @param {String} imageUrl The URL of the "delete" icon image.
51635  */
51636         setDeletable: function(imageUrl) {
51637                 if (!this.singleSelect && !this.multiSelect) {
51638                         this.singleSelect = true;
51639                 }
51640                 var c = this.getContextMenu();
51641                 this.contextMenu.on("itemclick", function(item) {
51642                         switch (item.id) {
51643                                 case "delete":
51644                                         this.remove(this.getSelectedIndexes());
51645                                         break;
51646                         }
51647                 }, this);
51648                 this.contextMenu.add({
51649                         icon: imageUrl,
51650                         id: "delete",
51651                         text: 'Delete'
51652                 });
51653         },
51654         
51655 /**     Return the context menu for this DDView. */
51656         getContextMenu: function() {
51657                 if (!this.contextMenu) {
51658 //                      Create the View's context menu
51659                         this.contextMenu = new Roo.menu.Menu({
51660                                 id: this.id + "-contextmenu"
51661                         });
51662                         this.el.on("contextmenu", this.showContextMenu, this);
51663                 }
51664                 return this.contextMenu;
51665         },
51666         
51667         disableContextMenu: function() {
51668                 if (this.contextMenu) {
51669                         this.el.un("contextmenu", this.showContextMenu, this);
51670                 }
51671         },
51672
51673         showContextMenu: function(e, item) {
51674         item = this.findItemFromChild(e.getTarget());
51675                 if (item) {
51676                         e.stopEvent();
51677                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51678                         this.contextMenu.showAt(e.getXY());
51679             }
51680     },
51681
51682 /**
51683  *      Remove {@link Roo.data.Record}s at the specified indices.
51684  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51685  */
51686     remove: function(selectedIndices) {
51687                 selectedIndices = [].concat(selectedIndices);
51688                 for (var i = 0; i < selectedIndices.length; i++) {
51689                         var rec = this.store.getAt(selectedIndices[i]);
51690                         this.store.remove(rec);
51691                 }
51692     },
51693
51694 /**
51695  *      Double click fires the event, but also, if this is draggable, and there is only one other
51696  *      related DropZone, it transfers the selected node.
51697  */
51698     onDblClick : function(e){
51699         var item = this.findItemFromChild(e.getTarget());
51700         if(item){
51701             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51702                 return false;
51703             }
51704             if (this.dragGroup) {
51705                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51706                     while (targets.indexOf(this.dropZone) > -1) {
51707                             targets.remove(this.dropZone);
51708                                 }
51709                     if (targets.length == 1) {
51710                                         this.dragZone.cachedTarget = null;
51711                         var el = Roo.get(targets[0].getEl());
51712                         var box = el.getBox(true);
51713                         targets[0].onNodeDrop(el.dom, {
51714                                 target: el.dom,
51715                                 xy: [box.x, box.y + box.height - 1]
51716                         }, null, this.getDragData(e));
51717                     }
51718                 }
51719         }
51720     },
51721     
51722     handleSelection: function(e) {
51723                 this.dragZone.cachedTarget = null;
51724         var item = this.findItemFromChild(e.getTarget());
51725         if (!item) {
51726                 this.clearSelections(true);
51727                 return;
51728         }
51729                 if (item && (this.multiSelect || this.singleSelect)){
51730                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51731                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51732                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51733                                 this.unselect(item);
51734                         } else {
51735                                 this.select(item, this.multiSelect && e.ctrlKey);
51736                                 this.lastSelection = item;
51737                         }
51738                 }
51739     },
51740
51741     onItemClick : function(item, index, e){
51742                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51743                         return false;
51744                 }
51745                 return true;
51746     },
51747
51748     unselect : function(nodeInfo, suppressEvent){
51749                 var node = this.getNode(nodeInfo);
51750                 if(node && this.isSelected(node)){
51751                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51752                                 Roo.fly(node).removeClass(this.selectedClass);
51753                                 this.selections.remove(node);
51754                                 if(!suppressEvent){
51755                                         this.fireEvent("selectionchange", this, this.selections);
51756                                 }
51757                         }
51758                 }
51759     }
51760 });
51761 /*
51762  * Based on:
51763  * Ext JS Library 1.1.1
51764  * Copyright(c) 2006-2007, Ext JS, LLC.
51765  *
51766  * Originally Released Under LGPL - original licence link has changed is not relivant.
51767  *
51768  * Fork - LGPL
51769  * <script type="text/javascript">
51770  */
51771  
51772 /**
51773  * @class Roo.LayoutManager
51774  * @extends Roo.util.Observable
51775  * Base class for layout managers.
51776  */
51777 Roo.LayoutManager = function(container, config){
51778     Roo.LayoutManager.superclass.constructor.call(this);
51779     this.el = Roo.get(container);
51780     // ie scrollbar fix
51781     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51782         document.body.scroll = "no";
51783     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51784         this.el.position('relative');
51785     }
51786     this.id = this.el.id;
51787     this.el.addClass("x-layout-container");
51788     /** false to disable window resize monitoring @type Boolean */
51789     this.monitorWindowResize = true;
51790     this.regions = {};
51791     this.addEvents({
51792         /**
51793          * @event layout
51794          * Fires when a layout is performed. 
51795          * @param {Roo.LayoutManager} this
51796          */
51797         "layout" : true,
51798         /**
51799          * @event regionresized
51800          * Fires when the user resizes a region. 
51801          * @param {Roo.LayoutRegion} region The resized region
51802          * @param {Number} newSize The new size (width for east/west, height for north/south)
51803          */
51804         "regionresized" : true,
51805         /**
51806          * @event regioncollapsed
51807          * Fires when a region is collapsed. 
51808          * @param {Roo.LayoutRegion} region The collapsed region
51809          */
51810         "regioncollapsed" : true,
51811         /**
51812          * @event regionexpanded
51813          * Fires when a region is expanded.  
51814          * @param {Roo.LayoutRegion} region The expanded region
51815          */
51816         "regionexpanded" : true
51817     });
51818     this.updating = false;
51819     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51820 };
51821
51822 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51823     /**
51824      * Returns true if this layout is currently being updated
51825      * @return {Boolean}
51826      */
51827     isUpdating : function(){
51828         return this.updating; 
51829     },
51830     
51831     /**
51832      * Suspend the LayoutManager from doing auto-layouts while
51833      * making multiple add or remove calls
51834      */
51835     beginUpdate : function(){
51836         this.updating = true;    
51837     },
51838     
51839     /**
51840      * Restore auto-layouts and optionally disable the manager from performing a layout
51841      * @param {Boolean} noLayout true to disable a layout update 
51842      */
51843     endUpdate : function(noLayout){
51844         this.updating = false;
51845         if(!noLayout){
51846             this.layout();
51847         }    
51848     },
51849     
51850     layout: function(){
51851         
51852     },
51853     
51854     onRegionResized : function(region, newSize){
51855         this.fireEvent("regionresized", region, newSize);
51856         this.layout();
51857     },
51858     
51859     onRegionCollapsed : function(region){
51860         this.fireEvent("regioncollapsed", region);
51861     },
51862     
51863     onRegionExpanded : function(region){
51864         this.fireEvent("regionexpanded", region);
51865     },
51866         
51867     /**
51868      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51869      * performs box-model adjustments.
51870      * @return {Object} The size as an object {width: (the width), height: (the height)}
51871      */
51872     getViewSize : function(){
51873         var size;
51874         if(this.el.dom != document.body){
51875             size = this.el.getSize();
51876         }else{
51877             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51878         }
51879         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51880         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51881         return size;
51882     },
51883     
51884     /**
51885      * Returns the Element this layout is bound to.
51886      * @return {Roo.Element}
51887      */
51888     getEl : function(){
51889         return this.el;
51890     },
51891     
51892     /**
51893      * Returns the specified region.
51894      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51895      * @return {Roo.LayoutRegion}
51896      */
51897     getRegion : function(target){
51898         return this.regions[target.toLowerCase()];
51899     },
51900     
51901     onWindowResize : function(){
51902         if(this.monitorWindowResize){
51903             this.layout();
51904         }
51905     }
51906 });/*
51907  * Based on:
51908  * Ext JS Library 1.1.1
51909  * Copyright(c) 2006-2007, Ext JS, LLC.
51910  *
51911  * Originally Released Under LGPL - original licence link has changed is not relivant.
51912  *
51913  * Fork - LGPL
51914  * <script type="text/javascript">
51915  */
51916 /**
51917  * @class Roo.BorderLayout
51918  * @extends Roo.LayoutManager
51919  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51920  * please see: <br><br>
51921  * <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>
51922  * <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>
51923  * Example:
51924  <pre><code>
51925  var layout = new Roo.BorderLayout(document.body, {
51926     north: {
51927         initialSize: 25,
51928         titlebar: false
51929     },
51930     west: {
51931         split:true,
51932         initialSize: 200,
51933         minSize: 175,
51934         maxSize: 400,
51935         titlebar: true,
51936         collapsible: true
51937     },
51938     east: {
51939         split:true,
51940         initialSize: 202,
51941         minSize: 175,
51942         maxSize: 400,
51943         titlebar: true,
51944         collapsible: true
51945     },
51946     south: {
51947         split:true,
51948         initialSize: 100,
51949         minSize: 100,
51950         maxSize: 200,
51951         titlebar: true,
51952         collapsible: true
51953     },
51954     center: {
51955         titlebar: true,
51956         autoScroll:true,
51957         resizeTabs: true,
51958         minTabWidth: 50,
51959         preferredTabWidth: 150
51960     }
51961 });
51962
51963 // shorthand
51964 var CP = Roo.ContentPanel;
51965
51966 layout.beginUpdate();
51967 layout.add("north", new CP("north", "North"));
51968 layout.add("south", new CP("south", {title: "South", closable: true}));
51969 layout.add("west", new CP("west", {title: "West"}));
51970 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51971 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51972 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51973 layout.getRegion("center").showPanel("center1");
51974 layout.endUpdate();
51975 </code></pre>
51976
51977 <b>The container the layout is rendered into can be either the body element or any other element.
51978 If it is not the body element, the container needs to either be an absolute positioned element,
51979 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51980 the container size if it is not the body element.</b>
51981
51982 * @constructor
51983 * Create a new BorderLayout
51984 * @param {String/HTMLElement/Element} container The container this layout is bound to
51985 * @param {Object} config Configuration options
51986  */
51987 Roo.BorderLayout = function(container, config){
51988     config = config || {};
51989     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51990     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51991     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51992         var target = this.factory.validRegions[i];
51993         if(config[target]){
51994             this.addRegion(target, config[target]);
51995         }
51996     }
51997 };
51998
51999 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52000     /**
52001      * Creates and adds a new region if it doesn't already exist.
52002      * @param {String} target The target region key (north, south, east, west or center).
52003      * @param {Object} config The regions config object
52004      * @return {BorderLayoutRegion} The new region
52005      */
52006     addRegion : function(target, config){
52007         if(!this.regions[target]){
52008             var r = this.factory.create(target, this, config);
52009             this.bindRegion(target, r);
52010         }
52011         return this.regions[target];
52012     },
52013
52014     // private (kinda)
52015     bindRegion : function(name, r){
52016         this.regions[name] = r;
52017         r.on("visibilitychange", this.layout, this);
52018         r.on("paneladded", this.layout, this);
52019         r.on("panelremoved", this.layout, this);
52020         r.on("invalidated", this.layout, this);
52021         r.on("resized", this.onRegionResized, this);
52022         r.on("collapsed", this.onRegionCollapsed, this);
52023         r.on("expanded", this.onRegionExpanded, this);
52024     },
52025
52026     /**
52027      * Performs a layout update.
52028      */
52029     layout : function(){
52030         if(this.updating) {
52031             return;
52032         }
52033         var size = this.getViewSize();
52034         var w = size.width;
52035         var h = size.height;
52036         var centerW = w;
52037         var centerH = h;
52038         var centerY = 0;
52039         var centerX = 0;
52040         //var x = 0, y = 0;
52041
52042         var rs = this.regions;
52043         var north = rs["north"];
52044         var south = rs["south"]; 
52045         var west = rs["west"];
52046         var east = rs["east"];
52047         var center = rs["center"];
52048         //if(this.hideOnLayout){ // not supported anymore
52049             //c.el.setStyle("display", "none");
52050         //}
52051         if(north && north.isVisible()){
52052             var b = north.getBox();
52053             var m = north.getMargins();
52054             b.width = w - (m.left+m.right);
52055             b.x = m.left;
52056             b.y = m.top;
52057             centerY = b.height + b.y + m.bottom;
52058             centerH -= centerY;
52059             north.updateBox(this.safeBox(b));
52060         }
52061         if(south && south.isVisible()){
52062             var b = south.getBox();
52063             var m = south.getMargins();
52064             b.width = w - (m.left+m.right);
52065             b.x = m.left;
52066             var totalHeight = (b.height + m.top + m.bottom);
52067             b.y = h - totalHeight + m.top;
52068             centerH -= totalHeight;
52069             south.updateBox(this.safeBox(b));
52070         }
52071         if(west && west.isVisible()){
52072             var b = west.getBox();
52073             var m = west.getMargins();
52074             b.height = centerH - (m.top+m.bottom);
52075             b.x = m.left;
52076             b.y = centerY + m.top;
52077             var totalWidth = (b.width + m.left + m.right);
52078             centerX += totalWidth;
52079             centerW -= totalWidth;
52080             west.updateBox(this.safeBox(b));
52081         }
52082         if(east && east.isVisible()){
52083             var b = east.getBox();
52084             var m = east.getMargins();
52085             b.height = centerH - (m.top+m.bottom);
52086             var totalWidth = (b.width + m.left + m.right);
52087             b.x = w - totalWidth + m.left;
52088             b.y = centerY + m.top;
52089             centerW -= totalWidth;
52090             east.updateBox(this.safeBox(b));
52091         }
52092         if(center){
52093             var m = center.getMargins();
52094             var centerBox = {
52095                 x: centerX + m.left,
52096                 y: centerY + m.top,
52097                 width: centerW - (m.left+m.right),
52098                 height: centerH - (m.top+m.bottom)
52099             };
52100             //if(this.hideOnLayout){
52101                 //center.el.setStyle("display", "block");
52102             //}
52103             center.updateBox(this.safeBox(centerBox));
52104         }
52105         this.el.repaint();
52106         this.fireEvent("layout", this);
52107     },
52108
52109     // private
52110     safeBox : function(box){
52111         box.width = Math.max(0, box.width);
52112         box.height = Math.max(0, box.height);
52113         return box;
52114     },
52115
52116     /**
52117      * Adds a ContentPanel (or subclass) to this layout.
52118      * @param {String} target The target region key (north, south, east, west or center).
52119      * @param {Roo.ContentPanel} panel The panel to add
52120      * @return {Roo.ContentPanel} The added panel
52121      */
52122     add : function(target, panel){
52123          
52124         target = target.toLowerCase();
52125         return this.regions[target].add(panel);
52126     },
52127
52128     /**
52129      * Remove a ContentPanel (or subclass) to this layout.
52130      * @param {String} target The target region key (north, south, east, west or center).
52131      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52132      * @return {Roo.ContentPanel} The removed panel
52133      */
52134     remove : function(target, panel){
52135         target = target.toLowerCase();
52136         return this.regions[target].remove(panel);
52137     },
52138
52139     /**
52140      * Searches all regions for a panel with the specified id
52141      * @param {String} panelId
52142      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52143      */
52144     findPanel : function(panelId){
52145         var rs = this.regions;
52146         for(var target in rs){
52147             if(typeof rs[target] != "function"){
52148                 var p = rs[target].getPanel(panelId);
52149                 if(p){
52150                     return p;
52151                 }
52152             }
52153         }
52154         return null;
52155     },
52156
52157     /**
52158      * Searches all regions for a panel with the specified id and activates (shows) it.
52159      * @param {String/ContentPanel} panelId The panels id or the panel itself
52160      * @return {Roo.ContentPanel} The shown panel or null
52161      */
52162     showPanel : function(panelId) {
52163       var rs = this.regions;
52164       for(var target in rs){
52165          var r = rs[target];
52166          if(typeof r != "function"){
52167             if(r.hasPanel(panelId)){
52168                return r.showPanel(panelId);
52169             }
52170          }
52171       }
52172       return null;
52173    },
52174
52175    /**
52176      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52177      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52178      */
52179     restoreState : function(provider){
52180         if(!provider){
52181             provider = Roo.state.Manager;
52182         }
52183         var sm = new Roo.LayoutStateManager();
52184         sm.init(this, provider);
52185     },
52186
52187     /**
52188      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52189      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52190      * a valid ContentPanel config object.  Example:
52191      * <pre><code>
52192 // Create the main layout
52193 var layout = new Roo.BorderLayout('main-ct', {
52194     west: {
52195         split:true,
52196         minSize: 175,
52197         titlebar: true
52198     },
52199     center: {
52200         title:'Components'
52201     }
52202 }, 'main-ct');
52203
52204 // Create and add multiple ContentPanels at once via configs
52205 layout.batchAdd({
52206    west: {
52207        id: 'source-files',
52208        autoCreate:true,
52209        title:'Ext Source Files',
52210        autoScroll:true,
52211        fitToFrame:true
52212    },
52213    center : {
52214        el: cview,
52215        autoScroll:true,
52216        fitToFrame:true,
52217        toolbar: tb,
52218        resizeEl:'cbody'
52219    }
52220 });
52221 </code></pre>
52222      * @param {Object} regions An object containing ContentPanel configs by region name
52223      */
52224     batchAdd : function(regions){
52225         this.beginUpdate();
52226         for(var rname in regions){
52227             var lr = this.regions[rname];
52228             if(lr){
52229                 this.addTypedPanels(lr, regions[rname]);
52230             }
52231         }
52232         this.endUpdate();
52233     },
52234
52235     // private
52236     addTypedPanels : function(lr, ps){
52237         if(typeof ps == 'string'){
52238             lr.add(new Roo.ContentPanel(ps));
52239         }
52240         else if(ps instanceof Array){
52241             for(var i =0, len = ps.length; i < len; i++){
52242                 this.addTypedPanels(lr, ps[i]);
52243             }
52244         }
52245         else if(!ps.events){ // raw config?
52246             var el = ps.el;
52247             delete ps.el; // prevent conflict
52248             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52249         }
52250         else {  // panel object assumed!
52251             lr.add(ps);
52252         }
52253     },
52254     /**
52255      * Adds a xtype elements to the layout.
52256      * <pre><code>
52257
52258 layout.addxtype({
52259        xtype : 'ContentPanel',
52260        region: 'west',
52261        items: [ .... ]
52262    }
52263 );
52264
52265 layout.addxtype({
52266         xtype : 'NestedLayoutPanel',
52267         region: 'west',
52268         layout: {
52269            center: { },
52270            west: { }   
52271         },
52272         items : [ ... list of content panels or nested layout panels.. ]
52273    }
52274 );
52275 </code></pre>
52276      * @param {Object} cfg Xtype definition of item to add.
52277      */
52278     addxtype : function(cfg)
52279     {
52280         // basically accepts a pannel...
52281         // can accept a layout region..!?!?
52282         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52283         
52284         if (!cfg.xtype.match(/Panel$/)) {
52285             return false;
52286         }
52287         var ret = false;
52288         
52289         if (typeof(cfg.region) == 'undefined') {
52290             Roo.log("Failed to add Panel, region was not set");
52291             Roo.log(cfg);
52292             return false;
52293         }
52294         var region = cfg.region;
52295         delete cfg.region;
52296         
52297           
52298         var xitems = [];
52299         if (cfg.items) {
52300             xitems = cfg.items;
52301             delete cfg.items;
52302         }
52303         var nb = false;
52304         
52305         switch(cfg.xtype) 
52306         {
52307             case 'ContentPanel':  // ContentPanel (el, cfg)
52308             case 'ScrollPanel':  // ContentPanel (el, cfg)
52309             case 'ViewPanel': 
52310                 if(cfg.autoCreate) {
52311                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52312                 } else {
52313                     var el = this.el.createChild();
52314                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52315                 }
52316                 
52317                 this.add(region, ret);
52318                 break;
52319             
52320             
52321             case 'TreePanel': // our new panel!
52322                 cfg.el = this.el.createChild();
52323                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52324                 this.add(region, ret);
52325                 break;
52326             
52327             case 'NestedLayoutPanel': 
52328                 // create a new Layout (which is  a Border Layout...
52329                 var el = this.el.createChild();
52330                 var clayout = cfg.layout;
52331                 delete cfg.layout;
52332                 clayout.items   = clayout.items  || [];
52333                 // replace this exitems with the clayout ones..
52334                 xitems = clayout.items;
52335                  
52336                 
52337                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52338                     cfg.background = false;
52339                 }
52340                 var layout = new Roo.BorderLayout(el, clayout);
52341                 
52342                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52343                 //console.log('adding nested layout panel '  + cfg.toSource());
52344                 this.add(region, ret);
52345                 nb = {}; /// find first...
52346                 break;
52347                 
52348             case 'GridPanel': 
52349             
52350                 // needs grid and region
52351                 
52352                 //var el = this.getRegion(region).el.createChild();
52353                 var el = this.el.createChild();
52354                 // create the grid first...
52355                 
52356                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52357                 delete cfg.grid;
52358                 if (region == 'center' && this.active ) {
52359                     cfg.background = false;
52360                 }
52361                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52362                 
52363                 this.add(region, ret);
52364                 if (cfg.background) {
52365                     ret.on('activate', function(gp) {
52366                         if (!gp.grid.rendered) {
52367                             gp.grid.render();
52368                         }
52369                     });
52370                 } else {
52371                     grid.render();
52372                 }
52373                 break;
52374            
52375            
52376            
52377                 
52378                 
52379                 
52380             default:
52381                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52382                     
52383                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52384                     this.add(region, ret);
52385                 } else {
52386                 
52387                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52388                     return null;
52389                 }
52390                 
52391              // GridPanel (grid, cfg)
52392             
52393         }
52394         this.beginUpdate();
52395         // add children..
52396         var region = '';
52397         var abn = {};
52398         Roo.each(xitems, function(i)  {
52399             region = nb && i.region ? i.region : false;
52400             
52401             var add = ret.addxtype(i);
52402            
52403             if (region) {
52404                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52405                 if (!i.background) {
52406                     abn[region] = nb[region] ;
52407                 }
52408             }
52409             
52410         });
52411         this.endUpdate();
52412
52413         // make the last non-background panel active..
52414         //if (nb) { Roo.log(abn); }
52415         if (nb) {
52416             
52417             for(var r in abn) {
52418                 region = this.getRegion(r);
52419                 if (region) {
52420                     // tried using nb[r], but it does not work..
52421                      
52422                     region.showPanel(abn[r]);
52423                    
52424                 }
52425             }
52426         }
52427         return ret;
52428         
52429     }
52430 });
52431
52432 /**
52433  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52434  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52435  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52436  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52437  * <pre><code>
52438 // shorthand
52439 var CP = Roo.ContentPanel;
52440
52441 var layout = Roo.BorderLayout.create({
52442     north: {
52443         initialSize: 25,
52444         titlebar: false,
52445         panels: [new CP("north", "North")]
52446     },
52447     west: {
52448         split:true,
52449         initialSize: 200,
52450         minSize: 175,
52451         maxSize: 400,
52452         titlebar: true,
52453         collapsible: true,
52454         panels: [new CP("west", {title: "West"})]
52455     },
52456     east: {
52457         split:true,
52458         initialSize: 202,
52459         minSize: 175,
52460         maxSize: 400,
52461         titlebar: true,
52462         collapsible: true,
52463         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52464     },
52465     south: {
52466         split:true,
52467         initialSize: 100,
52468         minSize: 100,
52469         maxSize: 200,
52470         titlebar: true,
52471         collapsible: true,
52472         panels: [new CP("south", {title: "South", closable: true})]
52473     },
52474     center: {
52475         titlebar: true,
52476         autoScroll:true,
52477         resizeTabs: true,
52478         minTabWidth: 50,
52479         preferredTabWidth: 150,
52480         panels: [
52481             new CP("center1", {title: "Close Me", closable: true}),
52482             new CP("center2", {title: "Center Panel", closable: false})
52483         ]
52484     }
52485 }, document.body);
52486
52487 layout.getRegion("center").showPanel("center1");
52488 </code></pre>
52489  * @param config
52490  * @param targetEl
52491  */
52492 Roo.BorderLayout.create = function(config, targetEl){
52493     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52494     layout.beginUpdate();
52495     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52496     for(var j = 0, jlen = regions.length; j < jlen; j++){
52497         var lr = regions[j];
52498         if(layout.regions[lr] && config[lr].panels){
52499             var r = layout.regions[lr];
52500             var ps = config[lr].panels;
52501             layout.addTypedPanels(r, ps);
52502         }
52503     }
52504     layout.endUpdate();
52505     return layout;
52506 };
52507
52508 // private
52509 Roo.BorderLayout.RegionFactory = {
52510     // private
52511     validRegions : ["north","south","east","west","center"],
52512
52513     // private
52514     create : function(target, mgr, config){
52515         target = target.toLowerCase();
52516         if(config.lightweight || config.basic){
52517             return new Roo.BasicLayoutRegion(mgr, config, target);
52518         }
52519         switch(target){
52520             case "north":
52521                 return new Roo.NorthLayoutRegion(mgr, config);
52522             case "south":
52523                 return new Roo.SouthLayoutRegion(mgr, config);
52524             case "east":
52525                 return new Roo.EastLayoutRegion(mgr, config);
52526             case "west":
52527                 return new Roo.WestLayoutRegion(mgr, config);
52528             case "center":
52529                 return new Roo.CenterLayoutRegion(mgr, config);
52530         }
52531         throw 'Layout region "'+target+'" not supported.';
52532     }
52533 };/*
52534  * Based on:
52535  * Ext JS Library 1.1.1
52536  * Copyright(c) 2006-2007, Ext JS, LLC.
52537  *
52538  * Originally Released Under LGPL - original licence link has changed is not relivant.
52539  *
52540  * Fork - LGPL
52541  * <script type="text/javascript">
52542  */
52543  
52544 /**
52545  * @class Roo.BasicLayoutRegion
52546  * @extends Roo.util.Observable
52547  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52548  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52549  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52550  */
52551 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52552     this.mgr = mgr;
52553     this.position  = pos;
52554     this.events = {
52555         /**
52556          * @scope Roo.BasicLayoutRegion
52557          */
52558         
52559         /**
52560          * @event beforeremove
52561          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52562          * @param {Roo.LayoutRegion} this
52563          * @param {Roo.ContentPanel} panel The panel
52564          * @param {Object} e The cancel event object
52565          */
52566         "beforeremove" : true,
52567         /**
52568          * @event invalidated
52569          * Fires when the layout for this region is changed.
52570          * @param {Roo.LayoutRegion} this
52571          */
52572         "invalidated" : true,
52573         /**
52574          * @event visibilitychange
52575          * Fires when this region is shown or hidden 
52576          * @param {Roo.LayoutRegion} this
52577          * @param {Boolean} visibility true or false
52578          */
52579         "visibilitychange" : true,
52580         /**
52581          * @event paneladded
52582          * Fires when a panel is added. 
52583          * @param {Roo.LayoutRegion} this
52584          * @param {Roo.ContentPanel} panel The panel
52585          */
52586         "paneladded" : true,
52587         /**
52588          * @event panelremoved
52589          * Fires when a panel is removed. 
52590          * @param {Roo.LayoutRegion} this
52591          * @param {Roo.ContentPanel} panel The panel
52592          */
52593         "panelremoved" : true,
52594         /**
52595          * @event beforecollapse
52596          * Fires when this region before collapse.
52597          * @param {Roo.LayoutRegion} this
52598          */
52599         "beforecollapse" : true,
52600         /**
52601          * @event collapsed
52602          * Fires when this region is collapsed.
52603          * @param {Roo.LayoutRegion} this
52604          */
52605         "collapsed" : true,
52606         /**
52607          * @event expanded
52608          * Fires when this region is expanded.
52609          * @param {Roo.LayoutRegion} this
52610          */
52611         "expanded" : true,
52612         /**
52613          * @event slideshow
52614          * Fires when this region is slid into view.
52615          * @param {Roo.LayoutRegion} this
52616          */
52617         "slideshow" : true,
52618         /**
52619          * @event slidehide
52620          * Fires when this region slides out of view. 
52621          * @param {Roo.LayoutRegion} this
52622          */
52623         "slidehide" : true,
52624         /**
52625          * @event panelactivated
52626          * Fires when a panel is activated. 
52627          * @param {Roo.LayoutRegion} this
52628          * @param {Roo.ContentPanel} panel The activated panel
52629          */
52630         "panelactivated" : true,
52631         /**
52632          * @event resized
52633          * Fires when the user resizes this region. 
52634          * @param {Roo.LayoutRegion} this
52635          * @param {Number} newSize The new size (width for east/west, height for north/south)
52636          */
52637         "resized" : true
52638     };
52639     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52640     this.panels = new Roo.util.MixedCollection();
52641     this.panels.getKey = this.getPanelId.createDelegate(this);
52642     this.box = null;
52643     this.activePanel = null;
52644     // ensure listeners are added...
52645     
52646     if (config.listeners || config.events) {
52647         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52648             listeners : config.listeners || {},
52649             events : config.events || {}
52650         });
52651     }
52652     
52653     if(skipConfig !== true){
52654         this.applyConfig(config);
52655     }
52656 };
52657
52658 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52659     getPanelId : function(p){
52660         return p.getId();
52661     },
52662     
52663     applyConfig : function(config){
52664         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52665         this.config = config;
52666         
52667     },
52668     
52669     /**
52670      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52671      * the width, for horizontal (north, south) the height.
52672      * @param {Number} newSize The new width or height
52673      */
52674     resizeTo : function(newSize){
52675         var el = this.el ? this.el :
52676                  (this.activePanel ? this.activePanel.getEl() : null);
52677         if(el){
52678             switch(this.position){
52679                 case "east":
52680                 case "west":
52681                     el.setWidth(newSize);
52682                     this.fireEvent("resized", this, newSize);
52683                 break;
52684                 case "north":
52685                 case "south":
52686                     el.setHeight(newSize);
52687                     this.fireEvent("resized", this, newSize);
52688                 break;                
52689             }
52690         }
52691     },
52692     
52693     getBox : function(){
52694         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52695     },
52696     
52697     getMargins : function(){
52698         return this.margins;
52699     },
52700     
52701     updateBox : function(box){
52702         this.box = box;
52703         var el = this.activePanel.getEl();
52704         el.dom.style.left = box.x + "px";
52705         el.dom.style.top = box.y + "px";
52706         this.activePanel.setSize(box.width, box.height);
52707     },
52708     
52709     /**
52710      * Returns the container element for this region.
52711      * @return {Roo.Element}
52712      */
52713     getEl : function(){
52714         return this.activePanel;
52715     },
52716     
52717     /**
52718      * Returns true if this region is currently visible.
52719      * @return {Boolean}
52720      */
52721     isVisible : function(){
52722         return this.activePanel ? true : false;
52723     },
52724     
52725     setActivePanel : function(panel){
52726         panel = this.getPanel(panel);
52727         if(this.activePanel && this.activePanel != panel){
52728             this.activePanel.setActiveState(false);
52729             this.activePanel.getEl().setLeftTop(-10000,-10000);
52730         }
52731         this.activePanel = panel;
52732         panel.setActiveState(true);
52733         if(this.box){
52734             panel.setSize(this.box.width, this.box.height);
52735         }
52736         this.fireEvent("panelactivated", this, panel);
52737         this.fireEvent("invalidated");
52738     },
52739     
52740     /**
52741      * Show the specified panel.
52742      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52743      * @return {Roo.ContentPanel} The shown panel or null
52744      */
52745     showPanel : function(panel){
52746         if(panel = this.getPanel(panel)){
52747             this.setActivePanel(panel);
52748         }
52749         return panel;
52750     },
52751     
52752     /**
52753      * Get the active panel for this region.
52754      * @return {Roo.ContentPanel} The active panel or null
52755      */
52756     getActivePanel : function(){
52757         return this.activePanel;
52758     },
52759     
52760     /**
52761      * Add the passed ContentPanel(s)
52762      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52763      * @return {Roo.ContentPanel} The panel added (if only one was added)
52764      */
52765     add : function(panel){
52766         if(arguments.length > 1){
52767             for(var i = 0, len = arguments.length; i < len; i++) {
52768                 this.add(arguments[i]);
52769             }
52770             return null;
52771         }
52772         if(this.hasPanel(panel)){
52773             this.showPanel(panel);
52774             return panel;
52775         }
52776         var el = panel.getEl();
52777         if(el.dom.parentNode != this.mgr.el.dom){
52778             this.mgr.el.dom.appendChild(el.dom);
52779         }
52780         if(panel.setRegion){
52781             panel.setRegion(this);
52782         }
52783         this.panels.add(panel);
52784         el.setStyle("position", "absolute");
52785         if(!panel.background){
52786             this.setActivePanel(panel);
52787             if(this.config.initialSize && this.panels.getCount()==1){
52788                 this.resizeTo(this.config.initialSize);
52789             }
52790         }
52791         this.fireEvent("paneladded", this, panel);
52792         return panel;
52793     },
52794     
52795     /**
52796      * Returns true if the panel is in this region.
52797      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52798      * @return {Boolean}
52799      */
52800     hasPanel : function(panel){
52801         if(typeof panel == "object"){ // must be panel obj
52802             panel = panel.getId();
52803         }
52804         return this.getPanel(panel) ? true : false;
52805     },
52806     
52807     /**
52808      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52809      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52810      * @param {Boolean} preservePanel Overrides the config preservePanel option
52811      * @return {Roo.ContentPanel} The panel that was removed
52812      */
52813     remove : function(panel, preservePanel){
52814         panel = this.getPanel(panel);
52815         if(!panel){
52816             return null;
52817         }
52818         var e = {};
52819         this.fireEvent("beforeremove", this, panel, e);
52820         if(e.cancel === true){
52821             return null;
52822         }
52823         var panelId = panel.getId();
52824         this.panels.removeKey(panelId);
52825         return panel;
52826     },
52827     
52828     /**
52829      * Returns the panel specified or null if it's not in this region.
52830      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52831      * @return {Roo.ContentPanel}
52832      */
52833     getPanel : function(id){
52834         if(typeof id == "object"){ // must be panel obj
52835             return id;
52836         }
52837         return this.panels.get(id);
52838     },
52839     
52840     /**
52841      * Returns this regions position (north/south/east/west/center).
52842      * @return {String} 
52843      */
52844     getPosition: function(){
52845         return this.position;    
52846     }
52847 });/*
52848  * Based on:
52849  * Ext JS Library 1.1.1
52850  * Copyright(c) 2006-2007, Ext JS, LLC.
52851  *
52852  * Originally Released Under LGPL - original licence link has changed is not relivant.
52853  *
52854  * Fork - LGPL
52855  * <script type="text/javascript">
52856  */
52857  
52858 /**
52859  * @class Roo.LayoutRegion
52860  * @extends Roo.BasicLayoutRegion
52861  * This class represents a region in a layout manager.
52862  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52863  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52864  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52865  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52866  * @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})
52867  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52868  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52869  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52870  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52871  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52872  * @cfg {String}    title           The title for the region (overrides panel titles)
52873  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52874  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52875  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52876  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52877  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52878  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52879  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52880  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52881  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52882  * @cfg {Boolean}   showPin         True to show a pin button
52883  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52884  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52885  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52886  * @cfg {Number}    width           For East/West panels
52887  * @cfg {Number}    height          For North/South panels
52888  * @cfg {Boolean}   split           To show the splitter
52889  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52890  */
52891 Roo.LayoutRegion = function(mgr, config, pos){
52892     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52893     var dh = Roo.DomHelper;
52894     /** This region's container element 
52895     * @type Roo.Element */
52896     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52897     /** This region's title element 
52898     * @type Roo.Element */
52899
52900     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52901         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52902         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52903     ]}, true);
52904     this.titleEl.enableDisplayMode();
52905     /** This region's title text element 
52906     * @type HTMLElement */
52907     this.titleTextEl = this.titleEl.dom.firstChild;
52908     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52909     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52910     this.closeBtn.enableDisplayMode();
52911     this.closeBtn.on("click", this.closeClicked, this);
52912     this.closeBtn.hide();
52913
52914     this.createBody(config);
52915     this.visible = true;
52916     this.collapsed = false;
52917
52918     if(config.hideWhenEmpty){
52919         this.hide();
52920         this.on("paneladded", this.validateVisibility, this);
52921         this.on("panelremoved", this.validateVisibility, this);
52922     }
52923     this.applyConfig(config);
52924 };
52925
52926 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52927
52928     createBody : function(){
52929         /** This region's body element 
52930         * @type Roo.Element */
52931         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52932     },
52933
52934     applyConfig : function(c){
52935         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52936             var dh = Roo.DomHelper;
52937             if(c.titlebar !== false){
52938                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52939                 this.collapseBtn.on("click", this.collapse, this);
52940                 this.collapseBtn.enableDisplayMode();
52941
52942                 if(c.showPin === true || this.showPin){
52943                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52944                     this.stickBtn.enableDisplayMode();
52945                     this.stickBtn.on("click", this.expand, this);
52946                     this.stickBtn.hide();
52947                 }
52948             }
52949             /** This region's collapsed element
52950             * @type Roo.Element */
52951             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52952                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52953             ]}, true);
52954             if(c.floatable !== false){
52955                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52956                this.collapsedEl.on("click", this.collapseClick, this);
52957             }
52958
52959             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52960                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52961                    id: "message", unselectable: "on", style:{"float":"left"}});
52962                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52963              }
52964             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52965             this.expandBtn.on("click", this.expand, this);
52966         }
52967         if(this.collapseBtn){
52968             this.collapseBtn.setVisible(c.collapsible == true);
52969         }
52970         this.cmargins = c.cmargins || this.cmargins ||
52971                          (this.position == "west" || this.position == "east" ?
52972                              {top: 0, left: 2, right:2, bottom: 0} :
52973                              {top: 2, left: 0, right:0, bottom: 2});
52974         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52975         this.bottomTabs = c.tabPosition != "top";
52976         this.autoScroll = c.autoScroll || false;
52977         if(this.autoScroll){
52978             this.bodyEl.setStyle("overflow", "auto");
52979         }else{
52980             this.bodyEl.setStyle("overflow", "hidden");
52981         }
52982         //if(c.titlebar !== false){
52983             if((!c.titlebar && !c.title) || c.titlebar === false){
52984                 this.titleEl.hide();
52985             }else{
52986                 this.titleEl.show();
52987                 if(c.title){
52988                     this.titleTextEl.innerHTML = c.title;
52989                 }
52990             }
52991         //}
52992         this.duration = c.duration || .30;
52993         this.slideDuration = c.slideDuration || .45;
52994         this.config = c;
52995         if(c.collapsed){
52996             this.collapse(true);
52997         }
52998         if(c.hidden){
52999             this.hide();
53000         }
53001     },
53002     /**
53003      * Returns true if this region is currently visible.
53004      * @return {Boolean}
53005      */
53006     isVisible : function(){
53007         return this.visible;
53008     },
53009
53010     /**
53011      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53012      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53013      */
53014     setCollapsedTitle : function(title){
53015         title = title || "&#160;";
53016         if(this.collapsedTitleTextEl){
53017             this.collapsedTitleTextEl.innerHTML = title;
53018         }
53019     },
53020
53021     getBox : function(){
53022         var b;
53023         if(!this.collapsed){
53024             b = this.el.getBox(false, true);
53025         }else{
53026             b = this.collapsedEl.getBox(false, true);
53027         }
53028         return b;
53029     },
53030
53031     getMargins : function(){
53032         return this.collapsed ? this.cmargins : this.margins;
53033     },
53034
53035     highlight : function(){
53036         this.el.addClass("x-layout-panel-dragover");
53037     },
53038
53039     unhighlight : function(){
53040         this.el.removeClass("x-layout-panel-dragover");
53041     },
53042
53043     updateBox : function(box){
53044         this.box = box;
53045         if(!this.collapsed){
53046             this.el.dom.style.left = box.x + "px";
53047             this.el.dom.style.top = box.y + "px";
53048             this.updateBody(box.width, box.height);
53049         }else{
53050             this.collapsedEl.dom.style.left = box.x + "px";
53051             this.collapsedEl.dom.style.top = box.y + "px";
53052             this.collapsedEl.setSize(box.width, box.height);
53053         }
53054         if(this.tabs){
53055             this.tabs.autoSizeTabs();
53056         }
53057     },
53058
53059     updateBody : function(w, h){
53060         if(w !== null){
53061             this.el.setWidth(w);
53062             w -= this.el.getBorderWidth("rl");
53063             if(this.config.adjustments){
53064                 w += this.config.adjustments[0];
53065             }
53066         }
53067         if(h !== null){
53068             this.el.setHeight(h);
53069             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53070             h -= this.el.getBorderWidth("tb");
53071             if(this.config.adjustments){
53072                 h += this.config.adjustments[1];
53073             }
53074             this.bodyEl.setHeight(h);
53075             if(this.tabs){
53076                 h = this.tabs.syncHeight(h);
53077             }
53078         }
53079         if(this.panelSize){
53080             w = w !== null ? w : this.panelSize.width;
53081             h = h !== null ? h : this.panelSize.height;
53082         }
53083         if(this.activePanel){
53084             var el = this.activePanel.getEl();
53085             w = w !== null ? w : el.getWidth();
53086             h = h !== null ? h : el.getHeight();
53087             this.panelSize = {width: w, height: h};
53088             this.activePanel.setSize(w, h);
53089         }
53090         if(Roo.isIE && this.tabs){
53091             this.tabs.el.repaint();
53092         }
53093     },
53094
53095     /**
53096      * Returns the container element for this region.
53097      * @return {Roo.Element}
53098      */
53099     getEl : function(){
53100         return this.el;
53101     },
53102
53103     /**
53104      * Hides this region.
53105      */
53106     hide : function(){
53107         if(!this.collapsed){
53108             this.el.dom.style.left = "-2000px";
53109             this.el.hide();
53110         }else{
53111             this.collapsedEl.dom.style.left = "-2000px";
53112             this.collapsedEl.hide();
53113         }
53114         this.visible = false;
53115         this.fireEvent("visibilitychange", this, false);
53116     },
53117
53118     /**
53119      * Shows this region if it was previously hidden.
53120      */
53121     show : function(){
53122         if(!this.collapsed){
53123             this.el.show();
53124         }else{
53125             this.collapsedEl.show();
53126         }
53127         this.visible = true;
53128         this.fireEvent("visibilitychange", this, true);
53129     },
53130
53131     closeClicked : function(){
53132         if(this.activePanel){
53133             this.remove(this.activePanel);
53134         }
53135     },
53136
53137     collapseClick : function(e){
53138         if(this.isSlid){
53139            e.stopPropagation();
53140            this.slideIn();
53141         }else{
53142            e.stopPropagation();
53143            this.slideOut();
53144         }
53145     },
53146
53147     /**
53148      * Collapses this region.
53149      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53150      */
53151     collapse : function(skipAnim, skipCheck){
53152         if(this.collapsed) {
53153             return;
53154         }
53155         
53156         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53157             
53158             this.collapsed = true;
53159             if(this.split){
53160                 this.split.el.hide();
53161             }
53162             if(this.config.animate && skipAnim !== true){
53163                 this.fireEvent("invalidated", this);
53164                 this.animateCollapse();
53165             }else{
53166                 this.el.setLocation(-20000,-20000);
53167                 this.el.hide();
53168                 this.collapsedEl.show();
53169                 this.fireEvent("collapsed", this);
53170                 this.fireEvent("invalidated", this);
53171             }
53172         }
53173         
53174     },
53175
53176     animateCollapse : function(){
53177         // overridden
53178     },
53179
53180     /**
53181      * Expands this region if it was previously collapsed.
53182      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53183      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53184      */
53185     expand : function(e, skipAnim){
53186         if(e) {
53187             e.stopPropagation();
53188         }
53189         if(!this.collapsed || this.el.hasActiveFx()) {
53190             return;
53191         }
53192         if(this.isSlid){
53193             this.afterSlideIn();
53194             skipAnim = true;
53195         }
53196         this.collapsed = false;
53197         if(this.config.animate && skipAnim !== true){
53198             this.animateExpand();
53199         }else{
53200             this.el.show();
53201             if(this.split){
53202                 this.split.el.show();
53203             }
53204             this.collapsedEl.setLocation(-2000,-2000);
53205             this.collapsedEl.hide();
53206             this.fireEvent("invalidated", this);
53207             this.fireEvent("expanded", this);
53208         }
53209     },
53210
53211     animateExpand : function(){
53212         // overridden
53213     },
53214
53215     initTabs : function()
53216     {
53217         this.bodyEl.setStyle("overflow", "hidden");
53218         var ts = new Roo.TabPanel(
53219                 this.bodyEl.dom,
53220                 {
53221                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53222                     disableTooltips: this.config.disableTabTips,
53223                     toolbar : this.config.toolbar
53224                 }
53225         );
53226         if(this.config.hideTabs){
53227             ts.stripWrap.setDisplayed(false);
53228         }
53229         this.tabs = ts;
53230         ts.resizeTabs = this.config.resizeTabs === true;
53231         ts.minTabWidth = this.config.minTabWidth || 40;
53232         ts.maxTabWidth = this.config.maxTabWidth || 250;
53233         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53234         ts.monitorResize = false;
53235         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53236         ts.bodyEl.addClass('x-layout-tabs-body');
53237         this.panels.each(this.initPanelAsTab, this);
53238     },
53239
53240     initPanelAsTab : function(panel){
53241         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53242                     this.config.closeOnTab && panel.isClosable());
53243         if(panel.tabTip !== undefined){
53244             ti.setTooltip(panel.tabTip);
53245         }
53246         ti.on("activate", function(){
53247               this.setActivePanel(panel);
53248         }, this);
53249         if(this.config.closeOnTab){
53250             ti.on("beforeclose", function(t, e){
53251                 e.cancel = true;
53252                 this.remove(panel);
53253             }, this);
53254         }
53255         return ti;
53256     },
53257
53258     updatePanelTitle : function(panel, title){
53259         if(this.activePanel == panel){
53260             this.updateTitle(title);
53261         }
53262         if(this.tabs){
53263             var ti = this.tabs.getTab(panel.getEl().id);
53264             ti.setText(title);
53265             if(panel.tabTip !== undefined){
53266                 ti.setTooltip(panel.tabTip);
53267             }
53268         }
53269     },
53270
53271     updateTitle : function(title){
53272         if(this.titleTextEl && !this.config.title){
53273             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53274         }
53275     },
53276
53277     setActivePanel : function(panel){
53278         panel = this.getPanel(panel);
53279         if(this.activePanel && this.activePanel != panel){
53280             this.activePanel.setActiveState(false);
53281         }
53282         this.activePanel = panel;
53283         panel.setActiveState(true);
53284         if(this.panelSize){
53285             panel.setSize(this.panelSize.width, this.panelSize.height);
53286         }
53287         if(this.closeBtn){
53288             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53289         }
53290         this.updateTitle(panel.getTitle());
53291         if(this.tabs){
53292             this.fireEvent("invalidated", this);
53293         }
53294         this.fireEvent("panelactivated", this, panel);
53295     },
53296
53297     /**
53298      * Shows the specified panel.
53299      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53300      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53301      */
53302     showPanel : function(panel)
53303     {
53304         panel = this.getPanel(panel);
53305         if(panel){
53306             if(this.tabs){
53307                 var tab = this.tabs.getTab(panel.getEl().id);
53308                 if(tab.isHidden()){
53309                     this.tabs.unhideTab(tab.id);
53310                 }
53311                 tab.activate();
53312             }else{
53313                 this.setActivePanel(panel);
53314             }
53315         }
53316         return panel;
53317     },
53318
53319     /**
53320      * Get the active panel for this region.
53321      * @return {Roo.ContentPanel} The active panel or null
53322      */
53323     getActivePanel : function(){
53324         return this.activePanel;
53325     },
53326
53327     validateVisibility : function(){
53328         if(this.panels.getCount() < 1){
53329             this.updateTitle("&#160;");
53330             this.closeBtn.hide();
53331             this.hide();
53332         }else{
53333             if(!this.isVisible()){
53334                 this.show();
53335             }
53336         }
53337     },
53338
53339     /**
53340      * Adds the passed ContentPanel(s) to this region.
53341      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53342      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53343      */
53344     add : function(panel){
53345         if(arguments.length > 1){
53346             for(var i = 0, len = arguments.length; i < len; i++) {
53347                 this.add(arguments[i]);
53348             }
53349             return null;
53350         }
53351         if(this.hasPanel(panel)){
53352             this.showPanel(panel);
53353             return panel;
53354         }
53355         panel.setRegion(this);
53356         this.panels.add(panel);
53357         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53358             this.bodyEl.dom.appendChild(panel.getEl().dom);
53359             if(panel.background !== true){
53360                 this.setActivePanel(panel);
53361             }
53362             this.fireEvent("paneladded", this, panel);
53363             return panel;
53364         }
53365         if(!this.tabs){
53366             this.initTabs();
53367         }else{
53368             this.initPanelAsTab(panel);
53369         }
53370         if(panel.background !== true){
53371             this.tabs.activate(panel.getEl().id);
53372         }
53373         this.fireEvent("paneladded", this, panel);
53374         return panel;
53375     },
53376
53377     /**
53378      * Hides the tab for the specified panel.
53379      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53380      */
53381     hidePanel : function(panel){
53382         if(this.tabs && (panel = this.getPanel(panel))){
53383             this.tabs.hideTab(panel.getEl().id);
53384         }
53385     },
53386
53387     /**
53388      * Unhides the tab for a previously hidden panel.
53389      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53390      */
53391     unhidePanel : function(panel){
53392         if(this.tabs && (panel = this.getPanel(panel))){
53393             this.tabs.unhideTab(panel.getEl().id);
53394         }
53395     },
53396
53397     clearPanels : function(){
53398         while(this.panels.getCount() > 0){
53399              this.remove(this.panels.first());
53400         }
53401     },
53402
53403     /**
53404      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53405      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53406      * @param {Boolean} preservePanel Overrides the config preservePanel option
53407      * @return {Roo.ContentPanel} The panel that was removed
53408      */
53409     remove : function(panel, preservePanel){
53410         panel = this.getPanel(panel);
53411         if(!panel){
53412             return null;
53413         }
53414         var e = {};
53415         this.fireEvent("beforeremove", this, panel, e);
53416         if(e.cancel === true){
53417             return null;
53418         }
53419         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53420         var panelId = panel.getId();
53421         this.panels.removeKey(panelId);
53422         if(preservePanel){
53423             document.body.appendChild(panel.getEl().dom);
53424         }
53425         if(this.tabs){
53426             this.tabs.removeTab(panel.getEl().id);
53427         }else if (!preservePanel){
53428             this.bodyEl.dom.removeChild(panel.getEl().dom);
53429         }
53430         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53431             var p = this.panels.first();
53432             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53433             tempEl.appendChild(p.getEl().dom);
53434             this.bodyEl.update("");
53435             this.bodyEl.dom.appendChild(p.getEl().dom);
53436             tempEl = null;
53437             this.updateTitle(p.getTitle());
53438             this.tabs = null;
53439             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53440             this.setActivePanel(p);
53441         }
53442         panel.setRegion(null);
53443         if(this.activePanel == panel){
53444             this.activePanel = null;
53445         }
53446         if(this.config.autoDestroy !== false && preservePanel !== true){
53447             try{panel.destroy();}catch(e){}
53448         }
53449         this.fireEvent("panelremoved", this, panel);
53450         return panel;
53451     },
53452
53453     /**
53454      * Returns the TabPanel component used by this region
53455      * @return {Roo.TabPanel}
53456      */
53457     getTabs : function(){
53458         return this.tabs;
53459     },
53460
53461     createTool : function(parentEl, className){
53462         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53463             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53464         btn.addClassOnOver("x-layout-tools-button-over");
53465         return btn;
53466     }
53467 });/*
53468  * Based on:
53469  * Ext JS Library 1.1.1
53470  * Copyright(c) 2006-2007, Ext JS, LLC.
53471  *
53472  * Originally Released Under LGPL - original licence link has changed is not relivant.
53473  *
53474  * Fork - LGPL
53475  * <script type="text/javascript">
53476  */
53477  
53478
53479
53480 /**
53481  * @class Roo.SplitLayoutRegion
53482  * @extends Roo.LayoutRegion
53483  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53484  */
53485 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53486     this.cursor = cursor;
53487     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53488 };
53489
53490 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53491     splitTip : "Drag to resize.",
53492     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53493     useSplitTips : false,
53494
53495     applyConfig : function(config){
53496         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53497         if(config.split){
53498             if(!this.split){
53499                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53500                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53501                 /** The SplitBar for this region 
53502                 * @type Roo.SplitBar */
53503                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53504                 this.split.on("moved", this.onSplitMove, this);
53505                 this.split.useShim = config.useShim === true;
53506                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53507                 if(this.useSplitTips){
53508                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53509                 }
53510                 if(config.collapsible){
53511                     this.split.el.on("dblclick", this.collapse,  this);
53512                 }
53513             }
53514             if(typeof config.minSize != "undefined"){
53515                 this.split.minSize = config.minSize;
53516             }
53517             if(typeof config.maxSize != "undefined"){
53518                 this.split.maxSize = config.maxSize;
53519             }
53520             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53521                 this.hideSplitter();
53522             }
53523         }
53524     },
53525
53526     getHMaxSize : function(){
53527          var cmax = this.config.maxSize || 10000;
53528          var center = this.mgr.getRegion("center");
53529          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53530     },
53531
53532     getVMaxSize : function(){
53533          var cmax = this.config.maxSize || 10000;
53534          var center = this.mgr.getRegion("center");
53535          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53536     },
53537
53538     onSplitMove : function(split, newSize){
53539         this.fireEvent("resized", this, newSize);
53540     },
53541     
53542     /** 
53543      * Returns the {@link Roo.SplitBar} for this region.
53544      * @return {Roo.SplitBar}
53545      */
53546     getSplitBar : function(){
53547         return this.split;
53548     },
53549     
53550     hide : function(){
53551         this.hideSplitter();
53552         Roo.SplitLayoutRegion.superclass.hide.call(this);
53553     },
53554
53555     hideSplitter : function(){
53556         if(this.split){
53557             this.split.el.setLocation(-2000,-2000);
53558             this.split.el.hide();
53559         }
53560     },
53561
53562     show : function(){
53563         if(this.split){
53564             this.split.el.show();
53565         }
53566         Roo.SplitLayoutRegion.superclass.show.call(this);
53567     },
53568     
53569     beforeSlide: function(){
53570         if(Roo.isGecko){// firefox overflow auto bug workaround
53571             this.bodyEl.clip();
53572             if(this.tabs) {
53573                 this.tabs.bodyEl.clip();
53574             }
53575             if(this.activePanel){
53576                 this.activePanel.getEl().clip();
53577                 
53578                 if(this.activePanel.beforeSlide){
53579                     this.activePanel.beforeSlide();
53580                 }
53581             }
53582         }
53583     },
53584     
53585     afterSlide : function(){
53586         if(Roo.isGecko){// firefox overflow auto bug workaround
53587             this.bodyEl.unclip();
53588             if(this.tabs) {
53589                 this.tabs.bodyEl.unclip();
53590             }
53591             if(this.activePanel){
53592                 this.activePanel.getEl().unclip();
53593                 if(this.activePanel.afterSlide){
53594                     this.activePanel.afterSlide();
53595                 }
53596             }
53597         }
53598     },
53599
53600     initAutoHide : function(){
53601         if(this.autoHide !== false){
53602             if(!this.autoHideHd){
53603                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53604                 this.autoHideHd = {
53605                     "mouseout": function(e){
53606                         if(!e.within(this.el, true)){
53607                             st.delay(500);
53608                         }
53609                     },
53610                     "mouseover" : function(e){
53611                         st.cancel();
53612                     },
53613                     scope : this
53614                 };
53615             }
53616             this.el.on(this.autoHideHd);
53617         }
53618     },
53619
53620     clearAutoHide : function(){
53621         if(this.autoHide !== false){
53622             this.el.un("mouseout", this.autoHideHd.mouseout);
53623             this.el.un("mouseover", this.autoHideHd.mouseover);
53624         }
53625     },
53626
53627     clearMonitor : function(){
53628         Roo.get(document).un("click", this.slideInIf, this);
53629     },
53630
53631     // these names are backwards but not changed for compat
53632     slideOut : function(){
53633         if(this.isSlid || this.el.hasActiveFx()){
53634             return;
53635         }
53636         this.isSlid = true;
53637         if(this.collapseBtn){
53638             this.collapseBtn.hide();
53639         }
53640         this.closeBtnState = this.closeBtn.getStyle('display');
53641         this.closeBtn.hide();
53642         if(this.stickBtn){
53643             this.stickBtn.show();
53644         }
53645         this.el.show();
53646         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53647         this.beforeSlide();
53648         this.el.setStyle("z-index", 10001);
53649         this.el.slideIn(this.getSlideAnchor(), {
53650             callback: function(){
53651                 this.afterSlide();
53652                 this.initAutoHide();
53653                 Roo.get(document).on("click", this.slideInIf, this);
53654                 this.fireEvent("slideshow", this);
53655             },
53656             scope: this,
53657             block: true
53658         });
53659     },
53660
53661     afterSlideIn : function(){
53662         this.clearAutoHide();
53663         this.isSlid = false;
53664         this.clearMonitor();
53665         this.el.setStyle("z-index", "");
53666         if(this.collapseBtn){
53667             this.collapseBtn.show();
53668         }
53669         this.closeBtn.setStyle('display', this.closeBtnState);
53670         if(this.stickBtn){
53671             this.stickBtn.hide();
53672         }
53673         this.fireEvent("slidehide", this);
53674     },
53675
53676     slideIn : function(cb){
53677         if(!this.isSlid || this.el.hasActiveFx()){
53678             Roo.callback(cb);
53679             return;
53680         }
53681         this.isSlid = false;
53682         this.beforeSlide();
53683         this.el.slideOut(this.getSlideAnchor(), {
53684             callback: function(){
53685                 this.el.setLeftTop(-10000, -10000);
53686                 this.afterSlide();
53687                 this.afterSlideIn();
53688                 Roo.callback(cb);
53689             },
53690             scope: this,
53691             block: true
53692         });
53693     },
53694     
53695     slideInIf : function(e){
53696         if(!e.within(this.el)){
53697             this.slideIn();
53698         }
53699     },
53700
53701     animateCollapse : function(){
53702         this.beforeSlide();
53703         this.el.setStyle("z-index", 20000);
53704         var anchor = this.getSlideAnchor();
53705         this.el.slideOut(anchor, {
53706             callback : function(){
53707                 this.el.setStyle("z-index", "");
53708                 this.collapsedEl.slideIn(anchor, {duration:.3});
53709                 this.afterSlide();
53710                 this.el.setLocation(-10000,-10000);
53711                 this.el.hide();
53712                 this.fireEvent("collapsed", this);
53713             },
53714             scope: this,
53715             block: true
53716         });
53717     },
53718
53719     animateExpand : function(){
53720         this.beforeSlide();
53721         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53722         this.el.setStyle("z-index", 20000);
53723         this.collapsedEl.hide({
53724             duration:.1
53725         });
53726         this.el.slideIn(this.getSlideAnchor(), {
53727             callback : function(){
53728                 this.el.setStyle("z-index", "");
53729                 this.afterSlide();
53730                 if(this.split){
53731                     this.split.el.show();
53732                 }
53733                 this.fireEvent("invalidated", this);
53734                 this.fireEvent("expanded", this);
53735             },
53736             scope: this,
53737             block: true
53738         });
53739     },
53740
53741     anchors : {
53742         "west" : "left",
53743         "east" : "right",
53744         "north" : "top",
53745         "south" : "bottom"
53746     },
53747
53748     sanchors : {
53749         "west" : "l",
53750         "east" : "r",
53751         "north" : "t",
53752         "south" : "b"
53753     },
53754
53755     canchors : {
53756         "west" : "tl-tr",
53757         "east" : "tr-tl",
53758         "north" : "tl-bl",
53759         "south" : "bl-tl"
53760     },
53761
53762     getAnchor : function(){
53763         return this.anchors[this.position];
53764     },
53765
53766     getCollapseAnchor : function(){
53767         return this.canchors[this.position];
53768     },
53769
53770     getSlideAnchor : function(){
53771         return this.sanchors[this.position];
53772     },
53773
53774     getAlignAdj : function(){
53775         var cm = this.cmargins;
53776         switch(this.position){
53777             case "west":
53778                 return [0, 0];
53779             break;
53780             case "east":
53781                 return [0, 0];
53782             break;
53783             case "north":
53784                 return [0, 0];
53785             break;
53786             case "south":
53787                 return [0, 0];
53788             break;
53789         }
53790     },
53791
53792     getExpandAdj : function(){
53793         var c = this.collapsedEl, cm = this.cmargins;
53794         switch(this.position){
53795             case "west":
53796                 return [-(cm.right+c.getWidth()+cm.left), 0];
53797             break;
53798             case "east":
53799                 return [cm.right+c.getWidth()+cm.left, 0];
53800             break;
53801             case "north":
53802                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53803             break;
53804             case "south":
53805                 return [0, cm.top+cm.bottom+c.getHeight()];
53806             break;
53807         }
53808     }
53809 });/*
53810  * Based on:
53811  * Ext JS Library 1.1.1
53812  * Copyright(c) 2006-2007, Ext JS, LLC.
53813  *
53814  * Originally Released Under LGPL - original licence link has changed is not relivant.
53815  *
53816  * Fork - LGPL
53817  * <script type="text/javascript">
53818  */
53819 /*
53820  * These classes are private internal classes
53821  */
53822 Roo.CenterLayoutRegion = function(mgr, config){
53823     Roo.LayoutRegion.call(this, mgr, config, "center");
53824     this.visible = true;
53825     this.minWidth = config.minWidth || 20;
53826     this.minHeight = config.minHeight || 20;
53827 };
53828
53829 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53830     hide : function(){
53831         // center panel can't be hidden
53832     },
53833     
53834     show : function(){
53835         // center panel can't be hidden
53836     },
53837     
53838     getMinWidth: function(){
53839         return this.minWidth;
53840     },
53841     
53842     getMinHeight: function(){
53843         return this.minHeight;
53844     }
53845 });
53846
53847
53848 Roo.NorthLayoutRegion = function(mgr, config){
53849     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53850     if(this.split){
53851         this.split.placement = Roo.SplitBar.TOP;
53852         this.split.orientation = Roo.SplitBar.VERTICAL;
53853         this.split.el.addClass("x-layout-split-v");
53854     }
53855     var size = config.initialSize || config.height;
53856     if(typeof size != "undefined"){
53857         this.el.setHeight(size);
53858     }
53859 };
53860 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53861     orientation: Roo.SplitBar.VERTICAL,
53862     getBox : function(){
53863         if(this.collapsed){
53864             return this.collapsedEl.getBox();
53865         }
53866         var box = this.el.getBox();
53867         if(this.split){
53868             box.height += this.split.el.getHeight();
53869         }
53870         return box;
53871     },
53872     
53873     updateBox : function(box){
53874         if(this.split && !this.collapsed){
53875             box.height -= this.split.el.getHeight();
53876             this.split.el.setLeft(box.x);
53877             this.split.el.setTop(box.y+box.height);
53878             this.split.el.setWidth(box.width);
53879         }
53880         if(this.collapsed){
53881             this.updateBody(box.width, null);
53882         }
53883         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53884     }
53885 });
53886
53887 Roo.SouthLayoutRegion = function(mgr, config){
53888     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53889     if(this.split){
53890         this.split.placement = Roo.SplitBar.BOTTOM;
53891         this.split.orientation = Roo.SplitBar.VERTICAL;
53892         this.split.el.addClass("x-layout-split-v");
53893     }
53894     var size = config.initialSize || config.height;
53895     if(typeof size != "undefined"){
53896         this.el.setHeight(size);
53897     }
53898 };
53899 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53900     orientation: Roo.SplitBar.VERTICAL,
53901     getBox : function(){
53902         if(this.collapsed){
53903             return this.collapsedEl.getBox();
53904         }
53905         var box = this.el.getBox();
53906         if(this.split){
53907             var sh = this.split.el.getHeight();
53908             box.height += sh;
53909             box.y -= sh;
53910         }
53911         return box;
53912     },
53913     
53914     updateBox : function(box){
53915         if(this.split && !this.collapsed){
53916             var sh = this.split.el.getHeight();
53917             box.height -= sh;
53918             box.y += sh;
53919             this.split.el.setLeft(box.x);
53920             this.split.el.setTop(box.y-sh);
53921             this.split.el.setWidth(box.width);
53922         }
53923         if(this.collapsed){
53924             this.updateBody(box.width, null);
53925         }
53926         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53927     }
53928 });
53929
53930 Roo.EastLayoutRegion = function(mgr, config){
53931     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53932     if(this.split){
53933         this.split.placement = Roo.SplitBar.RIGHT;
53934         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53935         this.split.el.addClass("x-layout-split-h");
53936     }
53937     var size = config.initialSize || config.width;
53938     if(typeof size != "undefined"){
53939         this.el.setWidth(size);
53940     }
53941 };
53942 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53943     orientation: Roo.SplitBar.HORIZONTAL,
53944     getBox : function(){
53945         if(this.collapsed){
53946             return this.collapsedEl.getBox();
53947         }
53948         var box = this.el.getBox();
53949         if(this.split){
53950             var sw = this.split.el.getWidth();
53951             box.width += sw;
53952             box.x -= sw;
53953         }
53954         return box;
53955     },
53956
53957     updateBox : function(box){
53958         if(this.split && !this.collapsed){
53959             var sw = this.split.el.getWidth();
53960             box.width -= sw;
53961             this.split.el.setLeft(box.x);
53962             this.split.el.setTop(box.y);
53963             this.split.el.setHeight(box.height);
53964             box.x += sw;
53965         }
53966         if(this.collapsed){
53967             this.updateBody(null, box.height);
53968         }
53969         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53970     }
53971 });
53972
53973 Roo.WestLayoutRegion = function(mgr, config){
53974     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53975     if(this.split){
53976         this.split.placement = Roo.SplitBar.LEFT;
53977         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53978         this.split.el.addClass("x-layout-split-h");
53979     }
53980     var size = config.initialSize || config.width;
53981     if(typeof size != "undefined"){
53982         this.el.setWidth(size);
53983     }
53984 };
53985 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53986     orientation: Roo.SplitBar.HORIZONTAL,
53987     getBox : function(){
53988         if(this.collapsed){
53989             return this.collapsedEl.getBox();
53990         }
53991         var box = this.el.getBox();
53992         if(this.split){
53993             box.width += this.split.el.getWidth();
53994         }
53995         return box;
53996     },
53997     
53998     updateBox : function(box){
53999         if(this.split && !this.collapsed){
54000             var sw = this.split.el.getWidth();
54001             box.width -= sw;
54002             this.split.el.setLeft(box.x+box.width);
54003             this.split.el.setTop(box.y);
54004             this.split.el.setHeight(box.height);
54005         }
54006         if(this.collapsed){
54007             this.updateBody(null, box.height);
54008         }
54009         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54010     }
54011 });
54012 /*
54013  * Based on:
54014  * Ext JS Library 1.1.1
54015  * Copyright(c) 2006-2007, Ext JS, LLC.
54016  *
54017  * Originally Released Under LGPL - original licence link has changed is not relivant.
54018  *
54019  * Fork - LGPL
54020  * <script type="text/javascript">
54021  */
54022  
54023  
54024 /*
54025  * Private internal class for reading and applying state
54026  */
54027 Roo.LayoutStateManager = function(layout){
54028      // default empty state
54029      this.state = {
54030         north: {},
54031         south: {},
54032         east: {},
54033         west: {}       
54034     };
54035 };
54036
54037 Roo.LayoutStateManager.prototype = {
54038     init : function(layout, provider){
54039         this.provider = provider;
54040         var state = provider.get(layout.id+"-layout-state");
54041         if(state){
54042             var wasUpdating = layout.isUpdating();
54043             if(!wasUpdating){
54044                 layout.beginUpdate();
54045             }
54046             for(var key in state){
54047                 if(typeof state[key] != "function"){
54048                     var rstate = state[key];
54049                     var r = layout.getRegion(key);
54050                     if(r && rstate){
54051                         if(rstate.size){
54052                             r.resizeTo(rstate.size);
54053                         }
54054                         if(rstate.collapsed == true){
54055                             r.collapse(true);
54056                         }else{
54057                             r.expand(null, true);
54058                         }
54059                     }
54060                 }
54061             }
54062             if(!wasUpdating){
54063                 layout.endUpdate();
54064             }
54065             this.state = state; 
54066         }
54067         this.layout = layout;
54068         layout.on("regionresized", this.onRegionResized, this);
54069         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54070         layout.on("regionexpanded", this.onRegionExpanded, this);
54071     },
54072     
54073     storeState : function(){
54074         this.provider.set(this.layout.id+"-layout-state", this.state);
54075     },
54076     
54077     onRegionResized : function(region, newSize){
54078         this.state[region.getPosition()].size = newSize;
54079         this.storeState();
54080     },
54081     
54082     onRegionCollapsed : function(region){
54083         this.state[region.getPosition()].collapsed = true;
54084         this.storeState();
54085     },
54086     
54087     onRegionExpanded : function(region){
54088         this.state[region.getPosition()].collapsed = false;
54089         this.storeState();
54090     }
54091 };/*
54092  * Based on:
54093  * Ext JS Library 1.1.1
54094  * Copyright(c) 2006-2007, Ext JS, LLC.
54095  *
54096  * Originally Released Under LGPL - original licence link has changed is not relivant.
54097  *
54098  * Fork - LGPL
54099  * <script type="text/javascript">
54100  */
54101 /**
54102  * @class Roo.ContentPanel
54103  * @extends Roo.util.Observable
54104  * A basic ContentPanel element.
54105  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54106  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54107  * @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
54108  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54109  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54110  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54111  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54112  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54113  * @cfg {String} title          The title for this panel
54114  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54115  * @cfg {String} url            Calls {@link #setUrl} with this value
54116  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54117  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54118  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54119  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54120
54121  * @constructor
54122  * Create a new ContentPanel.
54123  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54124  * @param {String/Object} config A string to set only the title or a config object
54125  * @param {String} content (optional) Set the HTML content for this panel
54126  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54127  */
54128 Roo.ContentPanel = function(el, config, content){
54129     
54130      
54131     /*
54132     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54133         config = el;
54134         el = Roo.id();
54135     }
54136     if (config && config.parentLayout) { 
54137         el = config.parentLayout.el.createChild(); 
54138     }
54139     */
54140     if(el.autoCreate){ // xtype is available if this is called from factory
54141         config = el;
54142         el = Roo.id();
54143     }
54144     this.el = Roo.get(el);
54145     if(!this.el && config && config.autoCreate){
54146         if(typeof config.autoCreate == "object"){
54147             if(!config.autoCreate.id){
54148                 config.autoCreate.id = config.id||el;
54149             }
54150             this.el = Roo.DomHelper.append(document.body,
54151                         config.autoCreate, true);
54152         }else{
54153             this.el = Roo.DomHelper.append(document.body,
54154                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54155         }
54156     }
54157     this.closable = false;
54158     this.loaded = false;
54159     this.active = false;
54160     if(typeof config == "string"){
54161         this.title = config;
54162     }else{
54163         Roo.apply(this, config);
54164     }
54165     
54166     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54167         this.wrapEl = this.el.wrap();
54168         this.toolbar.container = this.el.insertSibling(false, 'before');
54169         this.toolbar = new Roo.Toolbar(this.toolbar);
54170     }
54171     
54172     // xtype created footer. - not sure if will work as we normally have to render first..
54173     if (this.footer && !this.footer.el && this.footer.xtype) {
54174         if (!this.wrapEl) {
54175             this.wrapEl = this.el.wrap();
54176         }
54177     
54178         this.footer.container = this.wrapEl.createChild();
54179          
54180         this.footer = Roo.factory(this.footer, Roo);
54181         
54182     }
54183     
54184     if(this.resizeEl){
54185         this.resizeEl = Roo.get(this.resizeEl, true);
54186     }else{
54187         this.resizeEl = this.el;
54188     }
54189     // handle view.xtype
54190     
54191  
54192     
54193     
54194     this.addEvents({
54195         /**
54196          * @event activate
54197          * Fires when this panel is activated. 
54198          * @param {Roo.ContentPanel} this
54199          */
54200         "activate" : true,
54201         /**
54202          * @event deactivate
54203          * Fires when this panel is activated. 
54204          * @param {Roo.ContentPanel} this
54205          */
54206         "deactivate" : true,
54207
54208         /**
54209          * @event resize
54210          * Fires when this panel is resized if fitToFrame is true.
54211          * @param {Roo.ContentPanel} this
54212          * @param {Number} width The width after any component adjustments
54213          * @param {Number} height The height after any component adjustments
54214          */
54215         "resize" : true,
54216         
54217          /**
54218          * @event render
54219          * Fires when this tab is created
54220          * @param {Roo.ContentPanel} this
54221          */
54222         "render" : true
54223          
54224         
54225     });
54226     
54227
54228     
54229     
54230     if(this.autoScroll){
54231         this.resizeEl.setStyle("overflow", "auto");
54232     } else {
54233         // fix randome scrolling
54234         this.el.on('scroll', function() {
54235             Roo.log('fix random scolling');
54236             this.scrollTo('top',0); 
54237         });
54238     }
54239     content = content || this.content;
54240     if(content){
54241         this.setContent(content);
54242     }
54243     if(config && config.url){
54244         this.setUrl(this.url, this.params, this.loadOnce);
54245     }
54246     
54247     
54248     
54249     Roo.ContentPanel.superclass.constructor.call(this);
54250     
54251     if (this.view && typeof(this.view.xtype) != 'undefined') {
54252         this.view.el = this.el.appendChild(document.createElement("div"));
54253         this.view = Roo.factory(this.view); 
54254         this.view.render  &&  this.view.render(false, '');  
54255     }
54256     
54257     
54258     this.fireEvent('render', this);
54259 };
54260
54261 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54262     tabTip:'',
54263     setRegion : function(region){
54264         this.region = region;
54265         if(region){
54266            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54267         }else{
54268            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54269         } 
54270     },
54271     
54272     /**
54273      * Returns the toolbar for this Panel if one was configured. 
54274      * @return {Roo.Toolbar} 
54275      */
54276     getToolbar : function(){
54277         return this.toolbar;
54278     },
54279     
54280     setActiveState : function(active){
54281         this.active = active;
54282         if(!active){
54283             this.fireEvent("deactivate", this);
54284         }else{
54285             this.fireEvent("activate", this);
54286         }
54287     },
54288     /**
54289      * Updates this panel's element
54290      * @param {String} content The new content
54291      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54292     */
54293     setContent : function(content, loadScripts){
54294         this.el.update(content, loadScripts);
54295     },
54296
54297     ignoreResize : function(w, h){
54298         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54299             return true;
54300         }else{
54301             this.lastSize = {width: w, height: h};
54302             return false;
54303         }
54304     },
54305     /**
54306      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54307      * @return {Roo.UpdateManager} The UpdateManager
54308      */
54309     getUpdateManager : function(){
54310         return this.el.getUpdateManager();
54311     },
54312      /**
54313      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54314      * @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:
54315 <pre><code>
54316 panel.load({
54317     url: "your-url.php",
54318     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54319     callback: yourFunction,
54320     scope: yourObject, //(optional scope)
54321     discardUrl: false,
54322     nocache: false,
54323     text: "Loading...",
54324     timeout: 30,
54325     scripts: false
54326 });
54327 </code></pre>
54328      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54329      * 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.
54330      * @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}
54331      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54332      * @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.
54333      * @return {Roo.ContentPanel} this
54334      */
54335     load : function(){
54336         var um = this.el.getUpdateManager();
54337         um.update.apply(um, arguments);
54338         return this;
54339     },
54340
54341
54342     /**
54343      * 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.
54344      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54345      * @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)
54346      * @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)
54347      * @return {Roo.UpdateManager} The UpdateManager
54348      */
54349     setUrl : function(url, params, loadOnce){
54350         if(this.refreshDelegate){
54351             this.removeListener("activate", this.refreshDelegate);
54352         }
54353         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54354         this.on("activate", this.refreshDelegate);
54355         return this.el.getUpdateManager();
54356     },
54357     
54358     _handleRefresh : function(url, params, loadOnce){
54359         if(!loadOnce || !this.loaded){
54360             var updater = this.el.getUpdateManager();
54361             updater.update(url, params, this._setLoaded.createDelegate(this));
54362         }
54363     },
54364     
54365     _setLoaded : function(){
54366         this.loaded = true;
54367     }, 
54368     
54369     /**
54370      * Returns this panel's id
54371      * @return {String} 
54372      */
54373     getId : function(){
54374         return this.el.id;
54375     },
54376     
54377     /** 
54378      * Returns this panel's element - used by regiosn to add.
54379      * @return {Roo.Element} 
54380      */
54381     getEl : function(){
54382         return this.wrapEl || this.el;
54383     },
54384     
54385     adjustForComponents : function(width, height)
54386     {
54387         //Roo.log('adjustForComponents ');
54388         if(this.resizeEl != this.el){
54389             width -= this.el.getFrameWidth('lr');
54390             height -= this.el.getFrameWidth('tb');
54391         }
54392         if(this.toolbar){
54393             var te = this.toolbar.getEl();
54394             height -= te.getHeight();
54395             te.setWidth(width);
54396         }
54397         if(this.footer){
54398             var te = this.footer.getEl();
54399             //Roo.log("footer:" + te.getHeight());
54400             
54401             height -= te.getHeight();
54402             te.setWidth(width);
54403         }
54404         
54405         
54406         if(this.adjustments){
54407             width += this.adjustments[0];
54408             height += this.adjustments[1];
54409         }
54410         return {"width": width, "height": height};
54411     },
54412     
54413     setSize : function(width, height){
54414         if(this.fitToFrame && !this.ignoreResize(width, height)){
54415             if(this.fitContainer && this.resizeEl != this.el){
54416                 this.el.setSize(width, height);
54417             }
54418             var size = this.adjustForComponents(width, height);
54419             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54420             this.fireEvent('resize', this, size.width, size.height);
54421         }
54422     },
54423     
54424     /**
54425      * Returns this panel's title
54426      * @return {String} 
54427      */
54428     getTitle : function(){
54429         return this.title;
54430     },
54431     
54432     /**
54433      * Set this panel's title
54434      * @param {String} title
54435      */
54436     setTitle : function(title){
54437         this.title = title;
54438         if(this.region){
54439             this.region.updatePanelTitle(this, title);
54440         }
54441     },
54442     
54443     /**
54444      * Returns true is this panel was configured to be closable
54445      * @return {Boolean} 
54446      */
54447     isClosable : function(){
54448         return this.closable;
54449     },
54450     
54451     beforeSlide : function(){
54452         this.el.clip();
54453         this.resizeEl.clip();
54454     },
54455     
54456     afterSlide : function(){
54457         this.el.unclip();
54458         this.resizeEl.unclip();
54459     },
54460     
54461     /**
54462      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54463      *   Will fail silently if the {@link #setUrl} method has not been called.
54464      *   This does not activate the panel, just updates its content.
54465      */
54466     refresh : function(){
54467         if(this.refreshDelegate){
54468            this.loaded = false;
54469            this.refreshDelegate();
54470         }
54471     },
54472     
54473     /**
54474      * Destroys this panel
54475      */
54476     destroy : function(){
54477         this.el.removeAllListeners();
54478         var tempEl = document.createElement("span");
54479         tempEl.appendChild(this.el.dom);
54480         tempEl.innerHTML = "";
54481         this.el.remove();
54482         this.el = null;
54483     },
54484     
54485     /**
54486      * form - if the content panel contains a form - this is a reference to it.
54487      * @type {Roo.form.Form}
54488      */
54489     form : false,
54490     /**
54491      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54492      *    This contains a reference to it.
54493      * @type {Roo.View}
54494      */
54495     view : false,
54496     
54497       /**
54498      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54499      * <pre><code>
54500
54501 layout.addxtype({
54502        xtype : 'Form',
54503        items: [ .... ]
54504    }
54505 );
54506
54507 </code></pre>
54508      * @param {Object} cfg Xtype definition of item to add.
54509      */
54510     
54511     addxtype : function(cfg) {
54512         // add form..
54513         if (cfg.xtype.match(/^Form$/)) {
54514             
54515             var el;
54516             //if (this.footer) {
54517             //    el = this.footer.container.insertSibling(false, 'before');
54518             //} else {
54519                 el = this.el.createChild();
54520             //}
54521
54522             this.form = new  Roo.form.Form(cfg);
54523             
54524             
54525             if ( this.form.allItems.length) {
54526                 this.form.render(el.dom);
54527             }
54528             return this.form;
54529         }
54530         // should only have one of theses..
54531         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54532             // views.. should not be just added - used named prop 'view''
54533             
54534             cfg.el = this.el.appendChild(document.createElement("div"));
54535             // factory?
54536             
54537             var ret = new Roo.factory(cfg);
54538              
54539              ret.render && ret.render(false, ''); // render blank..
54540             this.view = ret;
54541             return ret;
54542         }
54543         return false;
54544     }
54545 });
54546
54547 /**
54548  * @class Roo.GridPanel
54549  * @extends Roo.ContentPanel
54550  * @constructor
54551  * Create a new GridPanel.
54552  * @param {Roo.grid.Grid} grid The grid for this panel
54553  * @param {String/Object} config A string to set only the panel's title, or a config object
54554  */
54555 Roo.GridPanel = function(grid, config){
54556     
54557   
54558     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54559         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54560         
54561     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54562     
54563     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54564     
54565     if(this.toolbar){
54566         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54567     }
54568     // xtype created footer. - not sure if will work as we normally have to render first..
54569     if (this.footer && !this.footer.el && this.footer.xtype) {
54570         
54571         this.footer.container = this.grid.getView().getFooterPanel(true);
54572         this.footer.dataSource = this.grid.dataSource;
54573         this.footer = Roo.factory(this.footer, Roo);
54574         
54575     }
54576     
54577     grid.monitorWindowResize = false; // turn off autosizing
54578     grid.autoHeight = false;
54579     grid.autoWidth = false;
54580     this.grid = grid;
54581     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54582 };
54583
54584 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54585     getId : function(){
54586         return this.grid.id;
54587     },
54588     
54589     /**
54590      * Returns the grid for this panel
54591      * @return {Roo.grid.Grid} 
54592      */
54593     getGrid : function(){
54594         return this.grid;    
54595     },
54596     
54597     setSize : function(width, height){
54598         if(!this.ignoreResize(width, height)){
54599             var grid = this.grid;
54600             var size = this.adjustForComponents(width, height);
54601             grid.getGridEl().setSize(size.width, size.height);
54602             grid.autoSize();
54603         }
54604     },
54605     
54606     beforeSlide : function(){
54607         this.grid.getView().scroller.clip();
54608     },
54609     
54610     afterSlide : function(){
54611         this.grid.getView().scroller.unclip();
54612     },
54613     
54614     destroy : function(){
54615         this.grid.destroy();
54616         delete this.grid;
54617         Roo.GridPanel.superclass.destroy.call(this); 
54618     }
54619 });
54620
54621
54622 /**
54623  * @class Roo.NestedLayoutPanel
54624  * @extends Roo.ContentPanel
54625  * @constructor
54626  * Create a new NestedLayoutPanel.
54627  * 
54628  * 
54629  * @param {Roo.BorderLayout} layout The layout for this panel
54630  * @param {String/Object} config A string to set only the title or a config object
54631  */
54632 Roo.NestedLayoutPanel = function(layout, config)
54633 {
54634     // construct with only one argument..
54635     /* FIXME - implement nicer consturctors
54636     if (layout.layout) {
54637         config = layout;
54638         layout = config.layout;
54639         delete config.layout;
54640     }
54641     if (layout.xtype && !layout.getEl) {
54642         // then layout needs constructing..
54643         layout = Roo.factory(layout, Roo);
54644     }
54645     */
54646     
54647     
54648     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54649     
54650     layout.monitorWindowResize = false; // turn off autosizing
54651     this.layout = layout;
54652     this.layout.getEl().addClass("x-layout-nested-layout");
54653     
54654     
54655     
54656     
54657 };
54658
54659 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54660
54661     setSize : function(width, height){
54662         if(!this.ignoreResize(width, height)){
54663             var size = this.adjustForComponents(width, height);
54664             var el = this.layout.getEl();
54665             el.setSize(size.width, size.height);
54666             var touch = el.dom.offsetWidth;
54667             this.layout.layout();
54668             // ie requires a double layout on the first pass
54669             if(Roo.isIE && !this.initialized){
54670                 this.initialized = true;
54671                 this.layout.layout();
54672             }
54673         }
54674     },
54675     
54676     // activate all subpanels if not currently active..
54677     
54678     setActiveState : function(active){
54679         this.active = active;
54680         if(!active){
54681             this.fireEvent("deactivate", this);
54682             return;
54683         }
54684         
54685         this.fireEvent("activate", this);
54686         // not sure if this should happen before or after..
54687         if (!this.layout) {
54688             return; // should not happen..
54689         }
54690         var reg = false;
54691         for (var r in this.layout.regions) {
54692             reg = this.layout.getRegion(r);
54693             if (reg.getActivePanel()) {
54694                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54695                 reg.setActivePanel(reg.getActivePanel());
54696                 continue;
54697             }
54698             if (!reg.panels.length) {
54699                 continue;
54700             }
54701             reg.showPanel(reg.getPanel(0));
54702         }
54703         
54704         
54705         
54706         
54707     },
54708     
54709     /**
54710      * Returns the nested BorderLayout for this panel
54711      * @return {Roo.BorderLayout} 
54712      */
54713     getLayout : function(){
54714         return this.layout;
54715     },
54716     
54717      /**
54718      * Adds a xtype elements to the layout of the nested panel
54719      * <pre><code>
54720
54721 panel.addxtype({
54722        xtype : 'ContentPanel',
54723        region: 'west',
54724        items: [ .... ]
54725    }
54726 );
54727
54728 panel.addxtype({
54729         xtype : 'NestedLayoutPanel',
54730         region: 'west',
54731         layout: {
54732            center: { },
54733            west: { }   
54734         },
54735         items : [ ... list of content panels or nested layout panels.. ]
54736    }
54737 );
54738 </code></pre>
54739      * @param {Object} cfg Xtype definition of item to add.
54740      */
54741     addxtype : function(cfg) {
54742         return this.layout.addxtype(cfg);
54743     
54744     }
54745 });
54746
54747 Roo.ScrollPanel = function(el, config, content){
54748     config = config || {};
54749     config.fitToFrame = true;
54750     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54751     
54752     this.el.dom.style.overflow = "hidden";
54753     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54754     this.el.removeClass("x-layout-inactive-content");
54755     this.el.on("mousewheel", this.onWheel, this);
54756
54757     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54758     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54759     up.unselectable(); down.unselectable();
54760     up.on("click", this.scrollUp, this);
54761     down.on("click", this.scrollDown, this);
54762     up.addClassOnOver("x-scroller-btn-over");
54763     down.addClassOnOver("x-scroller-btn-over");
54764     up.addClassOnClick("x-scroller-btn-click");
54765     down.addClassOnClick("x-scroller-btn-click");
54766     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54767
54768     this.resizeEl = this.el;
54769     this.el = wrap; this.up = up; this.down = down;
54770 };
54771
54772 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54773     increment : 100,
54774     wheelIncrement : 5,
54775     scrollUp : function(){
54776         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54777     },
54778
54779     scrollDown : function(){
54780         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54781     },
54782
54783     afterScroll : function(){
54784         var el = this.resizeEl;
54785         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54786         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54787         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54788     },
54789
54790     setSize : function(){
54791         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54792         this.afterScroll();
54793     },
54794
54795     onWheel : function(e){
54796         var d = e.getWheelDelta();
54797         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54798         this.afterScroll();
54799         e.stopEvent();
54800     },
54801
54802     setContent : function(content, loadScripts){
54803         this.resizeEl.update(content, loadScripts);
54804     }
54805
54806 });
54807
54808
54809
54810
54811
54812
54813
54814
54815
54816 /**
54817  * @class Roo.TreePanel
54818  * @extends Roo.ContentPanel
54819  * @constructor
54820  * Create a new TreePanel. - defaults to fit/scoll contents.
54821  * @param {String/Object} config A string to set only the panel's title, or a config object
54822  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54823  */
54824 Roo.TreePanel = function(config){
54825     var el = config.el;
54826     var tree = config.tree;
54827     delete config.tree; 
54828     delete config.el; // hopefull!
54829     
54830     // wrapper for IE7 strict & safari scroll issue
54831     
54832     var treeEl = el.createChild();
54833     config.resizeEl = treeEl;
54834     
54835     
54836     
54837     Roo.TreePanel.superclass.constructor.call(this, el, config);
54838  
54839  
54840     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54841     //console.log(tree);
54842     this.on('activate', function()
54843     {
54844         if (this.tree.rendered) {
54845             return;
54846         }
54847         //console.log('render tree');
54848         this.tree.render();
54849     });
54850     // this should not be needed.. - it's actually the 'el' that resizes?
54851     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54852     
54853     //this.on('resize',  function (cp, w, h) {
54854     //        this.tree.innerCt.setWidth(w);
54855     //        this.tree.innerCt.setHeight(h);
54856     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54857     //});
54858
54859         
54860     
54861 };
54862
54863 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54864     fitToFrame : true,
54865     autoScroll : true
54866 });
54867
54868
54869
54870
54871
54872
54873
54874
54875
54876
54877
54878 /*
54879  * Based on:
54880  * Ext JS Library 1.1.1
54881  * Copyright(c) 2006-2007, Ext JS, LLC.
54882  *
54883  * Originally Released Under LGPL - original licence link has changed is not relivant.
54884  *
54885  * Fork - LGPL
54886  * <script type="text/javascript">
54887  */
54888  
54889
54890 /**
54891  * @class Roo.ReaderLayout
54892  * @extends Roo.BorderLayout
54893  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54894  * center region containing two nested regions (a top one for a list view and one for item preview below),
54895  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54896  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54897  * expedites the setup of the overall layout and regions for this common application style.
54898  * Example:
54899  <pre><code>
54900 var reader = new Roo.ReaderLayout();
54901 var CP = Roo.ContentPanel;  // shortcut for adding
54902
54903 reader.beginUpdate();
54904 reader.add("north", new CP("north", "North"));
54905 reader.add("west", new CP("west", {title: "West"}));
54906 reader.add("east", new CP("east", {title: "East"}));
54907
54908 reader.regions.listView.add(new CP("listView", "List"));
54909 reader.regions.preview.add(new CP("preview", "Preview"));
54910 reader.endUpdate();
54911 </code></pre>
54912 * @constructor
54913 * Create a new ReaderLayout
54914 * @param {Object} config Configuration options
54915 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54916 * document.body if omitted)
54917 */
54918 Roo.ReaderLayout = function(config, renderTo){
54919     var c = config || {size:{}};
54920     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54921         north: c.north !== false ? Roo.apply({
54922             split:false,
54923             initialSize: 32,
54924             titlebar: false
54925         }, c.north) : false,
54926         west: c.west !== false ? Roo.apply({
54927             split:true,
54928             initialSize: 200,
54929             minSize: 175,
54930             maxSize: 400,
54931             titlebar: true,
54932             collapsible: true,
54933             animate: true,
54934             margins:{left:5,right:0,bottom:5,top:5},
54935             cmargins:{left:5,right:5,bottom:5,top:5}
54936         }, c.west) : false,
54937         east: c.east !== false ? Roo.apply({
54938             split:true,
54939             initialSize: 200,
54940             minSize: 175,
54941             maxSize: 400,
54942             titlebar: true,
54943             collapsible: true,
54944             animate: true,
54945             margins:{left:0,right:5,bottom:5,top:5},
54946             cmargins:{left:5,right:5,bottom:5,top:5}
54947         }, c.east) : false,
54948         center: Roo.apply({
54949             tabPosition: 'top',
54950             autoScroll:false,
54951             closeOnTab: true,
54952             titlebar:false,
54953             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54954         }, c.center)
54955     });
54956
54957     this.el.addClass('x-reader');
54958
54959     this.beginUpdate();
54960
54961     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54962         south: c.preview !== false ? Roo.apply({
54963             split:true,
54964             initialSize: 200,
54965             minSize: 100,
54966             autoScroll:true,
54967             collapsible:true,
54968             titlebar: true,
54969             cmargins:{top:5,left:0, right:0, bottom:0}
54970         }, c.preview) : false,
54971         center: Roo.apply({
54972             autoScroll:false,
54973             titlebar:false,
54974             minHeight:200
54975         }, c.listView)
54976     });
54977     this.add('center', new Roo.NestedLayoutPanel(inner,
54978             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54979
54980     this.endUpdate();
54981
54982     this.regions.preview = inner.getRegion('south');
54983     this.regions.listView = inner.getRegion('center');
54984 };
54985
54986 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54987  * Based on:
54988  * Ext JS Library 1.1.1
54989  * Copyright(c) 2006-2007, Ext JS, LLC.
54990  *
54991  * Originally Released Under LGPL - original licence link has changed is not relivant.
54992  *
54993  * Fork - LGPL
54994  * <script type="text/javascript">
54995  */
54996  
54997 /**
54998  * @class Roo.grid.Grid
54999  * @extends Roo.util.Observable
55000  * This class represents the primary interface of a component based grid control.
55001  * <br><br>Usage:<pre><code>
55002  var grid = new Roo.grid.Grid("my-container-id", {
55003      ds: myDataStore,
55004      cm: myColModel,
55005      selModel: mySelectionModel,
55006      autoSizeColumns: true,
55007      monitorWindowResize: false,
55008      trackMouseOver: true
55009  });
55010  // set any options
55011  grid.render();
55012  * </code></pre>
55013  * <b>Common Problems:</b><br/>
55014  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55015  * element will correct this<br/>
55016  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55017  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55018  * are unpredictable.<br/>
55019  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55020  * grid to calculate dimensions/offsets.<br/>
55021   * @constructor
55022  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55023  * The container MUST have some type of size defined for the grid to fill. The container will be
55024  * automatically set to position relative if it isn't already.
55025  * @param {Object} config A config object that sets properties on this grid.
55026  */
55027 Roo.grid.Grid = function(container, config){
55028         // initialize the container
55029         this.container = Roo.get(container);
55030         this.container.update("");
55031         this.container.setStyle("overflow", "hidden");
55032     this.container.addClass('x-grid-container');
55033
55034     this.id = this.container.id;
55035
55036     Roo.apply(this, config);
55037     // check and correct shorthanded configs
55038     if(this.ds){
55039         this.dataSource = this.ds;
55040         delete this.ds;
55041     }
55042     if(this.cm){
55043         this.colModel = this.cm;
55044         delete this.cm;
55045     }
55046     if(this.sm){
55047         this.selModel = this.sm;
55048         delete this.sm;
55049     }
55050
55051     if (this.selModel) {
55052         this.selModel = Roo.factory(this.selModel, Roo.grid);
55053         this.sm = this.selModel;
55054         this.sm.xmodule = this.xmodule || false;
55055     }
55056     if (typeof(this.colModel.config) == 'undefined') {
55057         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55058         this.cm = this.colModel;
55059         this.cm.xmodule = this.xmodule || false;
55060     }
55061     if (this.dataSource) {
55062         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55063         this.ds = this.dataSource;
55064         this.ds.xmodule = this.xmodule || false;
55065          
55066     }
55067     
55068     
55069     
55070     if(this.width){
55071         this.container.setWidth(this.width);
55072     }
55073
55074     if(this.height){
55075         this.container.setHeight(this.height);
55076     }
55077     /** @private */
55078         this.addEvents({
55079         // raw events
55080         /**
55081          * @event click
55082          * The raw click event for the entire grid.
55083          * @param {Roo.EventObject} e
55084          */
55085         "click" : true,
55086         /**
55087          * @event dblclick
55088          * The raw dblclick event for the entire grid.
55089          * @param {Roo.EventObject} e
55090          */
55091         "dblclick" : true,
55092         /**
55093          * @event contextmenu
55094          * The raw contextmenu event for the entire grid.
55095          * @param {Roo.EventObject} e
55096          */
55097         "contextmenu" : true,
55098         /**
55099          * @event mousedown
55100          * The raw mousedown event for the entire grid.
55101          * @param {Roo.EventObject} e
55102          */
55103         "mousedown" : true,
55104         /**
55105          * @event mouseup
55106          * The raw mouseup event for the entire grid.
55107          * @param {Roo.EventObject} e
55108          */
55109         "mouseup" : true,
55110         /**
55111          * @event mouseover
55112          * The raw mouseover event for the entire grid.
55113          * @param {Roo.EventObject} e
55114          */
55115         "mouseover" : true,
55116         /**
55117          * @event mouseout
55118          * The raw mouseout event for the entire grid.
55119          * @param {Roo.EventObject} e
55120          */
55121         "mouseout" : true,
55122         /**
55123          * @event keypress
55124          * The raw keypress event for the entire grid.
55125          * @param {Roo.EventObject} e
55126          */
55127         "keypress" : true,
55128         /**
55129          * @event keydown
55130          * The raw keydown event for the entire grid.
55131          * @param {Roo.EventObject} e
55132          */
55133         "keydown" : true,
55134
55135         // custom events
55136
55137         /**
55138          * @event cellclick
55139          * Fires when a cell is clicked
55140          * @param {Grid} this
55141          * @param {Number} rowIndex
55142          * @param {Number} columnIndex
55143          * @param {Roo.EventObject} e
55144          */
55145         "cellclick" : true,
55146         /**
55147          * @event celldblclick
55148          * Fires when a cell is double clicked
55149          * @param {Grid} this
55150          * @param {Number} rowIndex
55151          * @param {Number} columnIndex
55152          * @param {Roo.EventObject} e
55153          */
55154         "celldblclick" : true,
55155         /**
55156          * @event rowclick
55157          * Fires when a row is clicked
55158          * @param {Grid} this
55159          * @param {Number} rowIndex
55160          * @param {Roo.EventObject} e
55161          */
55162         "rowclick" : true,
55163         /**
55164          * @event rowdblclick
55165          * Fires when a row is double clicked
55166          * @param {Grid} this
55167          * @param {Number} rowIndex
55168          * @param {Roo.EventObject} e
55169          */
55170         "rowdblclick" : true,
55171         /**
55172          * @event headerclick
55173          * Fires when a header is clicked
55174          * @param {Grid} this
55175          * @param {Number} columnIndex
55176          * @param {Roo.EventObject} e
55177          */
55178         "headerclick" : true,
55179         /**
55180          * @event headerdblclick
55181          * Fires when a header cell is double clicked
55182          * @param {Grid} this
55183          * @param {Number} columnIndex
55184          * @param {Roo.EventObject} e
55185          */
55186         "headerdblclick" : true,
55187         /**
55188          * @event rowcontextmenu
55189          * Fires when a row is right clicked
55190          * @param {Grid} this
55191          * @param {Number} rowIndex
55192          * @param {Roo.EventObject} e
55193          */
55194         "rowcontextmenu" : true,
55195         /**
55196          * @event cellcontextmenu
55197          * Fires when a cell is right clicked
55198          * @param {Grid} this
55199          * @param {Number} rowIndex
55200          * @param {Number} cellIndex
55201          * @param {Roo.EventObject} e
55202          */
55203          "cellcontextmenu" : true,
55204         /**
55205          * @event headercontextmenu
55206          * Fires when a header is right clicked
55207          * @param {Grid} this
55208          * @param {Number} columnIndex
55209          * @param {Roo.EventObject} e
55210          */
55211         "headercontextmenu" : true,
55212         /**
55213          * @event bodyscroll
55214          * Fires when the body element is scrolled
55215          * @param {Number} scrollLeft
55216          * @param {Number} scrollTop
55217          */
55218         "bodyscroll" : true,
55219         /**
55220          * @event columnresize
55221          * Fires when the user resizes a column
55222          * @param {Number} columnIndex
55223          * @param {Number} newSize
55224          */
55225         "columnresize" : true,
55226         /**
55227          * @event columnmove
55228          * Fires when the user moves a column
55229          * @param {Number} oldIndex
55230          * @param {Number} newIndex
55231          */
55232         "columnmove" : true,
55233         /**
55234          * @event startdrag
55235          * Fires when row(s) start being dragged
55236          * @param {Grid} this
55237          * @param {Roo.GridDD} dd The drag drop object
55238          * @param {event} e The raw browser event
55239          */
55240         "startdrag" : true,
55241         /**
55242          * @event enddrag
55243          * Fires when a drag operation is complete
55244          * @param {Grid} this
55245          * @param {Roo.GridDD} dd The drag drop object
55246          * @param {event} e The raw browser event
55247          */
55248         "enddrag" : true,
55249         /**
55250          * @event dragdrop
55251          * Fires when dragged row(s) are dropped on a valid DD target
55252          * @param {Grid} this
55253          * @param {Roo.GridDD} dd The drag drop object
55254          * @param {String} targetId The target drag drop object
55255          * @param {event} e The raw browser event
55256          */
55257         "dragdrop" : true,
55258         /**
55259          * @event dragover
55260          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55261          * @param {Grid} this
55262          * @param {Roo.GridDD} dd The drag drop object
55263          * @param {String} targetId The target drag drop object
55264          * @param {event} e The raw browser event
55265          */
55266         "dragover" : true,
55267         /**
55268          * @event dragenter
55269          *  Fires when the dragged row(s) first cross another DD target while being dragged
55270          * @param {Grid} this
55271          * @param {Roo.GridDD} dd The drag drop object
55272          * @param {String} targetId The target drag drop object
55273          * @param {event} e The raw browser event
55274          */
55275         "dragenter" : true,
55276         /**
55277          * @event dragout
55278          * Fires when the dragged row(s) leave another DD target while being dragged
55279          * @param {Grid} this
55280          * @param {Roo.GridDD} dd The drag drop object
55281          * @param {String} targetId The target drag drop object
55282          * @param {event} e The raw browser event
55283          */
55284         "dragout" : true,
55285         /**
55286          * @event rowclass
55287          * Fires when a row is rendered, so you can change add a style to it.
55288          * @param {GridView} gridview   The grid view
55289          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55290          */
55291         'rowclass' : true,
55292
55293         /**
55294          * @event render
55295          * Fires when the grid is rendered
55296          * @param {Grid} grid
55297          */
55298         'render' : true
55299     });
55300
55301     Roo.grid.Grid.superclass.constructor.call(this);
55302 };
55303 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55304     
55305     /**
55306      * @cfg {String} ddGroup - drag drop group.
55307      */
55308
55309     /**
55310      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55311      */
55312     minColumnWidth : 25,
55313
55314     /**
55315      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55316      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55317      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55318      */
55319     autoSizeColumns : false,
55320
55321     /**
55322      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55323      */
55324     autoSizeHeaders : true,
55325
55326     /**
55327      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55328      */
55329     monitorWindowResize : true,
55330
55331     /**
55332      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55333      * rows measured to get a columns size. Default is 0 (all rows).
55334      */
55335     maxRowsToMeasure : 0,
55336
55337     /**
55338      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55339      */
55340     trackMouseOver : true,
55341
55342     /**
55343     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55344     */
55345     
55346     /**
55347     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55348     */
55349     enableDragDrop : false,
55350     
55351     /**
55352     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55353     */
55354     enableColumnMove : true,
55355     
55356     /**
55357     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55358     */
55359     enableColumnHide : true,
55360     
55361     /**
55362     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55363     */
55364     enableRowHeightSync : false,
55365     
55366     /**
55367     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55368     */
55369     stripeRows : true,
55370     
55371     /**
55372     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55373     */
55374     autoHeight : false,
55375
55376     /**
55377      * @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.
55378      */
55379     autoExpandColumn : false,
55380
55381     /**
55382     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55383     * Default is 50.
55384     */
55385     autoExpandMin : 50,
55386
55387     /**
55388     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55389     */
55390     autoExpandMax : 1000,
55391
55392     /**
55393     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55394     */
55395     view : null,
55396
55397     /**
55398     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55399     */
55400     loadMask : false,
55401     /**
55402     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55403     */
55404     dropTarget: false,
55405     
55406    
55407     
55408     // private
55409     rendered : false,
55410
55411     /**
55412     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55413     * of a fixed width. Default is false.
55414     */
55415     /**
55416     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55417     */
55418     /**
55419      * Called once after all setup has been completed and the grid is ready to be rendered.
55420      * @return {Roo.grid.Grid} this
55421      */
55422     render : function()
55423     {
55424         var c = this.container;
55425         // try to detect autoHeight/width mode
55426         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55427             this.autoHeight = true;
55428         }
55429         var view = this.getView();
55430         view.init(this);
55431
55432         c.on("click", this.onClick, this);
55433         c.on("dblclick", this.onDblClick, this);
55434         c.on("contextmenu", this.onContextMenu, this);
55435         c.on("keydown", this.onKeyDown, this);
55436         if (Roo.isTouch) {
55437             c.on("touchstart", this.onTouchStart, this);
55438         }
55439
55440         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55441
55442         this.getSelectionModel().init(this);
55443
55444         view.render();
55445
55446         if(this.loadMask){
55447             this.loadMask = new Roo.LoadMask(this.container,
55448                     Roo.apply({store:this.dataSource}, this.loadMask));
55449         }
55450         
55451         
55452         if (this.toolbar && this.toolbar.xtype) {
55453             this.toolbar.container = this.getView().getHeaderPanel(true);
55454             this.toolbar = new Roo.Toolbar(this.toolbar);
55455         }
55456         if (this.footer && this.footer.xtype) {
55457             this.footer.dataSource = this.getDataSource();
55458             this.footer.container = this.getView().getFooterPanel(true);
55459             this.footer = Roo.factory(this.footer, Roo);
55460         }
55461         if (this.dropTarget && this.dropTarget.xtype) {
55462             delete this.dropTarget.xtype;
55463             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55464         }
55465         
55466         
55467         this.rendered = true;
55468         this.fireEvent('render', this);
55469         return this;
55470     },
55471
55472     /**
55473      * Reconfigures the grid to use a different Store and Column Model.
55474      * The View will be bound to the new objects and refreshed.
55475      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55476      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55477      */
55478     reconfigure : function(dataSource, colModel){
55479         if(this.loadMask){
55480             this.loadMask.destroy();
55481             this.loadMask = new Roo.LoadMask(this.container,
55482                     Roo.apply({store:dataSource}, this.loadMask));
55483         }
55484         this.view.bind(dataSource, colModel);
55485         this.dataSource = dataSource;
55486         this.colModel = colModel;
55487         this.view.refresh(true);
55488     },
55489     /**
55490      * addColumns
55491      * Add's a column, default at the end..
55492      
55493      * @param {int} position to add (default end)
55494      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55495      */
55496     addColumns : function(pos, ar)
55497     {
55498         
55499         for (var i =0;i< ar.length;i++) {
55500             var cfg = ar[i];
55501             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55502             this.cm.lookup[cfg.id] = cfg;
55503         }
55504         
55505         
55506         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55507             pos = this.cm.config.length; //this.cm.config.push(cfg);
55508         } 
55509         pos = Math.max(0,pos);
55510         ar.unshift(0);
55511         ar.unshift(pos);
55512         this.cm.config.splice.apply(this.cm.config, ar);
55513         
55514         
55515         
55516         this.view.generateRules(this.cm);
55517         this.view.refresh(true);
55518         
55519     },
55520     
55521     
55522     
55523     
55524     // private
55525     onKeyDown : function(e){
55526         this.fireEvent("keydown", e);
55527     },
55528
55529     /**
55530      * Destroy this grid.
55531      * @param {Boolean} removeEl True to remove the element
55532      */
55533     destroy : function(removeEl, keepListeners){
55534         if(this.loadMask){
55535             this.loadMask.destroy();
55536         }
55537         var c = this.container;
55538         c.removeAllListeners();
55539         this.view.destroy();
55540         this.colModel.purgeListeners();
55541         if(!keepListeners){
55542             this.purgeListeners();
55543         }
55544         c.update("");
55545         if(removeEl === true){
55546             c.remove();
55547         }
55548     },
55549
55550     // private
55551     processEvent : function(name, e){
55552         // does this fire select???
55553         //Roo.log('grid:processEvent '  + name);
55554         
55555         if (name != 'touchstart' ) {
55556             this.fireEvent(name, e);    
55557         }
55558         
55559         var t = e.getTarget();
55560         var v = this.view;
55561         var header = v.findHeaderIndex(t);
55562         if(header !== false){
55563             var ename = name == 'touchstart' ? 'click' : name;
55564              
55565             this.fireEvent("header" + ename, this, header, e);
55566         }else{
55567             var row = v.findRowIndex(t);
55568             var cell = v.findCellIndex(t);
55569             if (name == 'touchstart') {
55570                 // first touch is always a click.
55571                 // hopefull this happens after selection is updated.?
55572                 name = false;
55573                 
55574                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55575                     var cs = this.selModel.getSelectedCell();
55576                     if (row == cs[0] && cell == cs[1]){
55577                         name = 'dblclick';
55578                     }
55579                 }
55580                 if (typeof(this.selModel.getSelections) != 'undefined') {
55581                     var cs = this.selModel.getSelections();
55582                     var ds = this.dataSource;
55583                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55584                         name = 'dblclick';
55585                     }
55586                 }
55587                 if (!name) {
55588                     return;
55589                 }
55590             }
55591             
55592             
55593             if(row !== false){
55594                 this.fireEvent("row" + name, this, row, e);
55595                 if(cell !== false){
55596                     this.fireEvent("cell" + name, this, row, cell, e);
55597                 }
55598             }
55599         }
55600     },
55601
55602     // private
55603     onClick : function(e){
55604         this.processEvent("click", e);
55605     },
55606    // private
55607     onTouchStart : function(e){
55608         this.processEvent("touchstart", e);
55609     },
55610
55611     // private
55612     onContextMenu : function(e, t){
55613         this.processEvent("contextmenu", e);
55614     },
55615
55616     // private
55617     onDblClick : function(e){
55618         this.processEvent("dblclick", e);
55619     },
55620
55621     // private
55622     walkCells : function(row, col, step, fn, scope){
55623         var cm = this.colModel, clen = cm.getColumnCount();
55624         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55625         if(step < 0){
55626             if(col < 0){
55627                 row--;
55628                 first = false;
55629             }
55630             while(row >= 0){
55631                 if(!first){
55632                     col = clen-1;
55633                 }
55634                 first = false;
55635                 while(col >= 0){
55636                     if(fn.call(scope || this, row, col, cm) === true){
55637                         return [row, col];
55638                     }
55639                     col--;
55640                 }
55641                 row--;
55642             }
55643         } else {
55644             if(col >= clen){
55645                 row++;
55646                 first = false;
55647             }
55648             while(row < rlen){
55649                 if(!first){
55650                     col = 0;
55651                 }
55652                 first = false;
55653                 while(col < clen){
55654                     if(fn.call(scope || this, row, col, cm) === true){
55655                         return [row, col];
55656                     }
55657                     col++;
55658                 }
55659                 row++;
55660             }
55661         }
55662         return null;
55663     },
55664
55665     // private
55666     getSelections : function(){
55667         return this.selModel.getSelections();
55668     },
55669
55670     /**
55671      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55672      * but if manual update is required this method will initiate it.
55673      */
55674     autoSize : function(){
55675         if(this.rendered){
55676             this.view.layout();
55677             if(this.view.adjustForScroll){
55678                 this.view.adjustForScroll();
55679             }
55680         }
55681     },
55682
55683     /**
55684      * Returns the grid's underlying element.
55685      * @return {Element} The element
55686      */
55687     getGridEl : function(){
55688         return this.container;
55689     },
55690
55691     // private for compatibility, overridden by editor grid
55692     stopEditing : function(){},
55693
55694     /**
55695      * Returns the grid's SelectionModel.
55696      * @return {SelectionModel}
55697      */
55698     getSelectionModel : function(){
55699         if(!this.selModel){
55700             this.selModel = new Roo.grid.RowSelectionModel();
55701         }
55702         return this.selModel;
55703     },
55704
55705     /**
55706      * Returns the grid's DataSource.
55707      * @return {DataSource}
55708      */
55709     getDataSource : function(){
55710         return this.dataSource;
55711     },
55712
55713     /**
55714      * Returns the grid's ColumnModel.
55715      * @return {ColumnModel}
55716      */
55717     getColumnModel : function(){
55718         return this.colModel;
55719     },
55720
55721     /**
55722      * Returns the grid's GridView object.
55723      * @return {GridView}
55724      */
55725     getView : function(){
55726         if(!this.view){
55727             this.view = new Roo.grid.GridView(this.viewConfig);
55728         }
55729         return this.view;
55730     },
55731     /**
55732      * Called to get grid's drag proxy text, by default returns this.ddText.
55733      * @return {String}
55734      */
55735     getDragDropText : function(){
55736         var count = this.selModel.getCount();
55737         return String.format(this.ddText, count, count == 1 ? '' : 's');
55738     }
55739 });
55740 /**
55741  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55742  * %0 is replaced with the number of selected rows.
55743  * @type String
55744  */
55745 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55746  * Based on:
55747  * Ext JS Library 1.1.1
55748  * Copyright(c) 2006-2007, Ext JS, LLC.
55749  *
55750  * Originally Released Under LGPL - original licence link has changed is not relivant.
55751  *
55752  * Fork - LGPL
55753  * <script type="text/javascript">
55754  */
55755  
55756 Roo.grid.AbstractGridView = function(){
55757         this.grid = null;
55758         
55759         this.events = {
55760             "beforerowremoved" : true,
55761             "beforerowsinserted" : true,
55762             "beforerefresh" : true,
55763             "rowremoved" : true,
55764             "rowsinserted" : true,
55765             "rowupdated" : true,
55766             "refresh" : true
55767         };
55768     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55769 };
55770
55771 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55772     rowClass : "x-grid-row",
55773     cellClass : "x-grid-cell",
55774     tdClass : "x-grid-td",
55775     hdClass : "x-grid-hd",
55776     splitClass : "x-grid-hd-split",
55777     
55778     init: function(grid){
55779         this.grid = grid;
55780                 var cid = this.grid.getGridEl().id;
55781         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55782         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55783         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55784         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55785         },
55786         
55787     getColumnRenderers : function(){
55788         var renderers = [];
55789         var cm = this.grid.colModel;
55790         var colCount = cm.getColumnCount();
55791         for(var i = 0; i < colCount; i++){
55792             renderers[i] = cm.getRenderer(i);
55793         }
55794         return renderers;
55795     },
55796     
55797     getColumnIds : function(){
55798         var ids = [];
55799         var cm = this.grid.colModel;
55800         var colCount = cm.getColumnCount();
55801         for(var i = 0; i < colCount; i++){
55802             ids[i] = cm.getColumnId(i);
55803         }
55804         return ids;
55805     },
55806     
55807     getDataIndexes : function(){
55808         if(!this.indexMap){
55809             this.indexMap = this.buildIndexMap();
55810         }
55811         return this.indexMap.colToData;
55812     },
55813     
55814     getColumnIndexByDataIndex : function(dataIndex){
55815         if(!this.indexMap){
55816             this.indexMap = this.buildIndexMap();
55817         }
55818         return this.indexMap.dataToCol[dataIndex];
55819     },
55820     
55821     /**
55822      * Set a css style for a column dynamically. 
55823      * @param {Number} colIndex The index of the column
55824      * @param {String} name The css property name
55825      * @param {String} value The css value
55826      */
55827     setCSSStyle : function(colIndex, name, value){
55828         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55829         Roo.util.CSS.updateRule(selector, name, value);
55830     },
55831     
55832     generateRules : function(cm){
55833         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55834         Roo.util.CSS.removeStyleSheet(rulesId);
55835         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55836             var cid = cm.getColumnId(i);
55837             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55838                          this.tdSelector, cid, " {\n}\n",
55839                          this.hdSelector, cid, " {\n}\n",
55840                          this.splitSelector, cid, " {\n}\n");
55841         }
55842         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55843     }
55844 });/*
55845  * Based on:
55846  * Ext JS Library 1.1.1
55847  * Copyright(c) 2006-2007, Ext JS, LLC.
55848  *
55849  * Originally Released Under LGPL - original licence link has changed is not relivant.
55850  *
55851  * Fork - LGPL
55852  * <script type="text/javascript">
55853  */
55854
55855 // private
55856 // This is a support class used internally by the Grid components
55857 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55858     this.grid = grid;
55859     this.view = grid.getView();
55860     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55861     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55862     if(hd2){
55863         this.setHandleElId(Roo.id(hd));
55864         this.setOuterHandleElId(Roo.id(hd2));
55865     }
55866     this.scroll = false;
55867 };
55868 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55869     maxDragWidth: 120,
55870     getDragData : function(e){
55871         var t = Roo.lib.Event.getTarget(e);
55872         var h = this.view.findHeaderCell(t);
55873         if(h){
55874             return {ddel: h.firstChild, header:h};
55875         }
55876         return false;
55877     },
55878
55879     onInitDrag : function(e){
55880         this.view.headersDisabled = true;
55881         var clone = this.dragData.ddel.cloneNode(true);
55882         clone.id = Roo.id();
55883         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55884         this.proxy.update(clone);
55885         return true;
55886     },
55887
55888     afterValidDrop : function(){
55889         var v = this.view;
55890         setTimeout(function(){
55891             v.headersDisabled = false;
55892         }, 50);
55893     },
55894
55895     afterInvalidDrop : function(){
55896         var v = this.view;
55897         setTimeout(function(){
55898             v.headersDisabled = false;
55899         }, 50);
55900     }
55901 });
55902 /*
55903  * Based on:
55904  * Ext JS Library 1.1.1
55905  * Copyright(c) 2006-2007, Ext JS, LLC.
55906  *
55907  * Originally Released Under LGPL - original licence link has changed is not relivant.
55908  *
55909  * Fork - LGPL
55910  * <script type="text/javascript">
55911  */
55912 // private
55913 // This is a support class used internally by the Grid components
55914 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55915     this.grid = grid;
55916     this.view = grid.getView();
55917     // split the proxies so they don't interfere with mouse events
55918     this.proxyTop = Roo.DomHelper.append(document.body, {
55919         cls:"col-move-top", html:"&#160;"
55920     }, true);
55921     this.proxyBottom = Roo.DomHelper.append(document.body, {
55922         cls:"col-move-bottom", html:"&#160;"
55923     }, true);
55924     this.proxyTop.hide = this.proxyBottom.hide = function(){
55925         this.setLeftTop(-100,-100);
55926         this.setStyle("visibility", "hidden");
55927     };
55928     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55929     // temporarily disabled
55930     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55931     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55932 };
55933 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55934     proxyOffsets : [-4, -9],
55935     fly: Roo.Element.fly,
55936
55937     getTargetFromEvent : function(e){
55938         var t = Roo.lib.Event.getTarget(e);
55939         var cindex = this.view.findCellIndex(t);
55940         if(cindex !== false){
55941             return this.view.getHeaderCell(cindex);
55942         }
55943         return null;
55944     },
55945
55946     nextVisible : function(h){
55947         var v = this.view, cm = this.grid.colModel;
55948         h = h.nextSibling;
55949         while(h){
55950             if(!cm.isHidden(v.getCellIndex(h))){
55951                 return h;
55952             }
55953             h = h.nextSibling;
55954         }
55955         return null;
55956     },
55957
55958     prevVisible : function(h){
55959         var v = this.view, cm = this.grid.colModel;
55960         h = h.prevSibling;
55961         while(h){
55962             if(!cm.isHidden(v.getCellIndex(h))){
55963                 return h;
55964             }
55965             h = h.prevSibling;
55966         }
55967         return null;
55968     },
55969
55970     positionIndicator : function(h, n, e){
55971         var x = Roo.lib.Event.getPageX(e);
55972         var r = Roo.lib.Dom.getRegion(n.firstChild);
55973         var px, pt, py = r.top + this.proxyOffsets[1];
55974         if((r.right - x) <= (r.right-r.left)/2){
55975             px = r.right+this.view.borderWidth;
55976             pt = "after";
55977         }else{
55978             px = r.left;
55979             pt = "before";
55980         }
55981         var oldIndex = this.view.getCellIndex(h);
55982         var newIndex = this.view.getCellIndex(n);
55983
55984         if(this.grid.colModel.isFixed(newIndex)){
55985             return false;
55986         }
55987
55988         var locked = this.grid.colModel.isLocked(newIndex);
55989
55990         if(pt == "after"){
55991             newIndex++;
55992         }
55993         if(oldIndex < newIndex){
55994             newIndex--;
55995         }
55996         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55997             return false;
55998         }
55999         px +=  this.proxyOffsets[0];
56000         this.proxyTop.setLeftTop(px, py);
56001         this.proxyTop.show();
56002         if(!this.bottomOffset){
56003             this.bottomOffset = this.view.mainHd.getHeight();
56004         }
56005         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56006         this.proxyBottom.show();
56007         return pt;
56008     },
56009
56010     onNodeEnter : function(n, dd, e, data){
56011         if(data.header != n){
56012             this.positionIndicator(data.header, n, e);
56013         }
56014     },
56015
56016     onNodeOver : function(n, dd, e, data){
56017         var result = false;
56018         if(data.header != n){
56019             result = this.positionIndicator(data.header, n, e);
56020         }
56021         if(!result){
56022             this.proxyTop.hide();
56023             this.proxyBottom.hide();
56024         }
56025         return result ? this.dropAllowed : this.dropNotAllowed;
56026     },
56027
56028     onNodeOut : function(n, dd, e, data){
56029         this.proxyTop.hide();
56030         this.proxyBottom.hide();
56031     },
56032
56033     onNodeDrop : function(n, dd, e, data){
56034         var h = data.header;
56035         if(h != n){
56036             var cm = this.grid.colModel;
56037             var x = Roo.lib.Event.getPageX(e);
56038             var r = Roo.lib.Dom.getRegion(n.firstChild);
56039             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56040             var oldIndex = this.view.getCellIndex(h);
56041             var newIndex = this.view.getCellIndex(n);
56042             var locked = cm.isLocked(newIndex);
56043             if(pt == "after"){
56044                 newIndex++;
56045             }
56046             if(oldIndex < newIndex){
56047                 newIndex--;
56048             }
56049             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56050                 return false;
56051             }
56052             cm.setLocked(oldIndex, locked, true);
56053             cm.moveColumn(oldIndex, newIndex);
56054             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56055             return true;
56056         }
56057         return false;
56058     }
56059 });
56060 /*
56061  * Based on:
56062  * Ext JS Library 1.1.1
56063  * Copyright(c) 2006-2007, Ext JS, LLC.
56064  *
56065  * Originally Released Under LGPL - original licence link has changed is not relivant.
56066  *
56067  * Fork - LGPL
56068  * <script type="text/javascript">
56069  */
56070   
56071 /**
56072  * @class Roo.grid.GridView
56073  * @extends Roo.util.Observable
56074  *
56075  * @constructor
56076  * @param {Object} config
56077  */
56078 Roo.grid.GridView = function(config){
56079     Roo.grid.GridView.superclass.constructor.call(this);
56080     this.el = null;
56081
56082     Roo.apply(this, config);
56083 };
56084
56085 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56086
56087     unselectable :  'unselectable="on"',
56088     unselectableCls :  'x-unselectable',
56089     
56090     
56091     rowClass : "x-grid-row",
56092
56093     cellClass : "x-grid-col",
56094
56095     tdClass : "x-grid-td",
56096
56097     hdClass : "x-grid-hd",
56098
56099     splitClass : "x-grid-split",
56100
56101     sortClasses : ["sort-asc", "sort-desc"],
56102
56103     enableMoveAnim : false,
56104
56105     hlColor: "C3DAF9",
56106
56107     dh : Roo.DomHelper,
56108
56109     fly : Roo.Element.fly,
56110
56111     css : Roo.util.CSS,
56112
56113     borderWidth: 1,
56114
56115     splitOffset: 3,
56116
56117     scrollIncrement : 22,
56118
56119     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56120
56121     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56122
56123     bind : function(ds, cm){
56124         if(this.ds){
56125             this.ds.un("load", this.onLoad, this);
56126             this.ds.un("datachanged", this.onDataChange, this);
56127             this.ds.un("add", this.onAdd, this);
56128             this.ds.un("remove", this.onRemove, this);
56129             this.ds.un("update", this.onUpdate, this);
56130             this.ds.un("clear", this.onClear, this);
56131         }
56132         if(ds){
56133             ds.on("load", this.onLoad, this);
56134             ds.on("datachanged", this.onDataChange, this);
56135             ds.on("add", this.onAdd, this);
56136             ds.on("remove", this.onRemove, this);
56137             ds.on("update", this.onUpdate, this);
56138             ds.on("clear", this.onClear, this);
56139         }
56140         this.ds = ds;
56141
56142         if(this.cm){
56143             this.cm.un("widthchange", this.onColWidthChange, this);
56144             this.cm.un("headerchange", this.onHeaderChange, this);
56145             this.cm.un("hiddenchange", this.onHiddenChange, this);
56146             this.cm.un("columnmoved", this.onColumnMove, this);
56147             this.cm.un("columnlockchange", this.onColumnLock, this);
56148         }
56149         if(cm){
56150             this.generateRules(cm);
56151             cm.on("widthchange", this.onColWidthChange, this);
56152             cm.on("headerchange", this.onHeaderChange, this);
56153             cm.on("hiddenchange", this.onHiddenChange, this);
56154             cm.on("columnmoved", this.onColumnMove, this);
56155             cm.on("columnlockchange", this.onColumnLock, this);
56156         }
56157         this.cm = cm;
56158     },
56159
56160     init: function(grid){
56161         Roo.grid.GridView.superclass.init.call(this, grid);
56162
56163         this.bind(grid.dataSource, grid.colModel);
56164
56165         grid.on("headerclick", this.handleHeaderClick, this);
56166
56167         if(grid.trackMouseOver){
56168             grid.on("mouseover", this.onRowOver, this);
56169             grid.on("mouseout", this.onRowOut, this);
56170         }
56171         grid.cancelTextSelection = function(){};
56172         this.gridId = grid.id;
56173
56174         var tpls = this.templates || {};
56175
56176         if(!tpls.master){
56177             tpls.master = new Roo.Template(
56178                '<div class="x-grid" hidefocus="true">',
56179                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56180                   '<div class="x-grid-topbar"></div>',
56181                   '<div class="x-grid-scroller"><div></div></div>',
56182                   '<div class="x-grid-locked">',
56183                       '<div class="x-grid-header">{lockedHeader}</div>',
56184                       '<div class="x-grid-body">{lockedBody}</div>',
56185                   "</div>",
56186                   '<div class="x-grid-viewport">',
56187                       '<div class="x-grid-header">{header}</div>',
56188                       '<div class="x-grid-body">{body}</div>',
56189                   "</div>",
56190                   '<div class="x-grid-bottombar"></div>',
56191                  
56192                   '<div class="x-grid-resize-proxy">&#160;</div>',
56193                "</div>"
56194             );
56195             tpls.master.disableformats = true;
56196         }
56197
56198         if(!tpls.header){
56199             tpls.header = new Roo.Template(
56200                '<table border="0" cellspacing="0" cellpadding="0">',
56201                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56202                "</table>{splits}"
56203             );
56204             tpls.header.disableformats = true;
56205         }
56206         tpls.header.compile();
56207
56208         if(!tpls.hcell){
56209             tpls.hcell = new Roo.Template(
56210                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56211                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56212                 "</div></td>"
56213              );
56214              tpls.hcell.disableFormats = true;
56215         }
56216         tpls.hcell.compile();
56217
56218         if(!tpls.hsplit){
56219             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56220                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56221             tpls.hsplit.disableFormats = true;
56222         }
56223         tpls.hsplit.compile();
56224
56225         if(!tpls.body){
56226             tpls.body = new Roo.Template(
56227                '<table border="0" cellspacing="0" cellpadding="0">',
56228                "<tbody>{rows}</tbody>",
56229                "</table>"
56230             );
56231             tpls.body.disableFormats = true;
56232         }
56233         tpls.body.compile();
56234
56235         if(!tpls.row){
56236             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56237             tpls.row.disableFormats = true;
56238         }
56239         tpls.row.compile();
56240
56241         if(!tpls.cell){
56242             tpls.cell = new Roo.Template(
56243                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56244                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56245                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56246                 "</td>"
56247             );
56248             tpls.cell.disableFormats = true;
56249         }
56250         tpls.cell.compile();
56251
56252         this.templates = tpls;
56253     },
56254
56255     // remap these for backwards compat
56256     onColWidthChange : function(){
56257         this.updateColumns.apply(this, arguments);
56258     },
56259     onHeaderChange : function(){
56260         this.updateHeaders.apply(this, arguments);
56261     }, 
56262     onHiddenChange : function(){
56263         this.handleHiddenChange.apply(this, arguments);
56264     },
56265     onColumnMove : function(){
56266         this.handleColumnMove.apply(this, arguments);
56267     },
56268     onColumnLock : function(){
56269         this.handleLockChange.apply(this, arguments);
56270     },
56271
56272     onDataChange : function(){
56273         this.refresh();
56274         this.updateHeaderSortState();
56275     },
56276
56277     onClear : function(){
56278         this.refresh();
56279     },
56280
56281     onUpdate : function(ds, record){
56282         this.refreshRow(record);
56283     },
56284
56285     refreshRow : function(record){
56286         var ds = this.ds, index;
56287         if(typeof record == 'number'){
56288             index = record;
56289             record = ds.getAt(index);
56290         }else{
56291             index = ds.indexOf(record);
56292         }
56293         this.insertRows(ds, index, index, true);
56294         this.onRemove(ds, record, index+1, true);
56295         this.syncRowHeights(index, index);
56296         this.layout();
56297         this.fireEvent("rowupdated", this, index, record);
56298     },
56299
56300     onAdd : function(ds, records, index){
56301         this.insertRows(ds, index, index + (records.length-1));
56302     },
56303
56304     onRemove : function(ds, record, index, isUpdate){
56305         if(isUpdate !== true){
56306             this.fireEvent("beforerowremoved", this, index, record);
56307         }
56308         var bt = this.getBodyTable(), lt = this.getLockedTable();
56309         if(bt.rows[index]){
56310             bt.firstChild.removeChild(bt.rows[index]);
56311         }
56312         if(lt.rows[index]){
56313             lt.firstChild.removeChild(lt.rows[index]);
56314         }
56315         if(isUpdate !== true){
56316             this.stripeRows(index);
56317             this.syncRowHeights(index, index);
56318             this.layout();
56319             this.fireEvent("rowremoved", this, index, record);
56320         }
56321     },
56322
56323     onLoad : function(){
56324         this.scrollToTop();
56325     },
56326
56327     /**
56328      * Scrolls the grid to the top
56329      */
56330     scrollToTop : function(){
56331         if(this.scroller){
56332             this.scroller.dom.scrollTop = 0;
56333             this.syncScroll();
56334         }
56335     },
56336
56337     /**
56338      * Gets a panel in the header of the grid that can be used for toolbars etc.
56339      * After modifying the contents of this panel a call to grid.autoSize() may be
56340      * required to register any changes in size.
56341      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56342      * @return Roo.Element
56343      */
56344     getHeaderPanel : function(doShow){
56345         if(doShow){
56346             this.headerPanel.show();
56347         }
56348         return this.headerPanel;
56349     },
56350
56351     /**
56352      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56353      * After modifying the contents of this panel a call to grid.autoSize() may be
56354      * required to register any changes in size.
56355      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56356      * @return Roo.Element
56357      */
56358     getFooterPanel : function(doShow){
56359         if(doShow){
56360             this.footerPanel.show();
56361         }
56362         return this.footerPanel;
56363     },
56364
56365     initElements : function(){
56366         var E = Roo.Element;
56367         var el = this.grid.getGridEl().dom.firstChild;
56368         var cs = el.childNodes;
56369
56370         this.el = new E(el);
56371         
56372          this.focusEl = new E(el.firstChild);
56373         this.focusEl.swallowEvent("click", true);
56374         
56375         this.headerPanel = new E(cs[1]);
56376         this.headerPanel.enableDisplayMode("block");
56377
56378         this.scroller = new E(cs[2]);
56379         this.scrollSizer = new E(this.scroller.dom.firstChild);
56380
56381         this.lockedWrap = new E(cs[3]);
56382         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56383         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56384
56385         this.mainWrap = new E(cs[4]);
56386         this.mainHd = new E(this.mainWrap.dom.firstChild);
56387         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56388
56389         this.footerPanel = new E(cs[5]);
56390         this.footerPanel.enableDisplayMode("block");
56391
56392         this.resizeProxy = new E(cs[6]);
56393
56394         this.headerSelector = String.format(
56395            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56396            this.lockedHd.id, this.mainHd.id
56397         );
56398
56399         this.splitterSelector = String.format(
56400            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56401            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56402         );
56403     },
56404     idToCssName : function(s)
56405     {
56406         return s.replace(/[^a-z0-9]+/ig, '-');
56407     },
56408
56409     getHeaderCell : function(index){
56410         return Roo.DomQuery.select(this.headerSelector)[index];
56411     },
56412
56413     getHeaderCellMeasure : function(index){
56414         return this.getHeaderCell(index).firstChild;
56415     },
56416
56417     getHeaderCellText : function(index){
56418         return this.getHeaderCell(index).firstChild.firstChild;
56419     },
56420
56421     getLockedTable : function(){
56422         return this.lockedBody.dom.firstChild;
56423     },
56424
56425     getBodyTable : function(){
56426         return this.mainBody.dom.firstChild;
56427     },
56428
56429     getLockedRow : function(index){
56430         return this.getLockedTable().rows[index];
56431     },
56432
56433     getRow : function(index){
56434         return this.getBodyTable().rows[index];
56435     },
56436
56437     getRowComposite : function(index){
56438         if(!this.rowEl){
56439             this.rowEl = new Roo.CompositeElementLite();
56440         }
56441         var els = [], lrow, mrow;
56442         if(lrow = this.getLockedRow(index)){
56443             els.push(lrow);
56444         }
56445         if(mrow = this.getRow(index)){
56446             els.push(mrow);
56447         }
56448         this.rowEl.elements = els;
56449         return this.rowEl;
56450     },
56451     /**
56452      * Gets the 'td' of the cell
56453      * 
56454      * @param {Integer} rowIndex row to select
56455      * @param {Integer} colIndex column to select
56456      * 
56457      * @return {Object} 
56458      */
56459     getCell : function(rowIndex, colIndex){
56460         var locked = this.cm.getLockedCount();
56461         var source;
56462         if(colIndex < locked){
56463             source = this.lockedBody.dom.firstChild;
56464         }else{
56465             source = this.mainBody.dom.firstChild;
56466             colIndex -= locked;
56467         }
56468         return source.rows[rowIndex].childNodes[colIndex];
56469     },
56470
56471     getCellText : function(rowIndex, colIndex){
56472         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56473     },
56474
56475     getCellBox : function(cell){
56476         var b = this.fly(cell).getBox();
56477         if(Roo.isOpera){ // opera fails to report the Y
56478             b.y = cell.offsetTop + this.mainBody.getY();
56479         }
56480         return b;
56481     },
56482
56483     getCellIndex : function(cell){
56484         var id = String(cell.className).match(this.cellRE);
56485         if(id){
56486             return parseInt(id[1], 10);
56487         }
56488         return 0;
56489     },
56490
56491     findHeaderIndex : function(n){
56492         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56493         return r ? this.getCellIndex(r) : false;
56494     },
56495
56496     findHeaderCell : function(n){
56497         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56498         return r ? r : false;
56499     },
56500
56501     findRowIndex : function(n){
56502         if(!n){
56503             return false;
56504         }
56505         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56506         return r ? r.rowIndex : false;
56507     },
56508
56509     findCellIndex : function(node){
56510         var stop = this.el.dom;
56511         while(node && node != stop){
56512             if(this.findRE.test(node.className)){
56513                 return this.getCellIndex(node);
56514             }
56515             node = node.parentNode;
56516         }
56517         return false;
56518     },
56519
56520     getColumnId : function(index){
56521         return this.cm.getColumnId(index);
56522     },
56523
56524     getSplitters : function()
56525     {
56526         if(this.splitterSelector){
56527            return Roo.DomQuery.select(this.splitterSelector);
56528         }else{
56529             return null;
56530       }
56531     },
56532
56533     getSplitter : function(index){
56534         return this.getSplitters()[index];
56535     },
56536
56537     onRowOver : function(e, t){
56538         var row;
56539         if((row = this.findRowIndex(t)) !== false){
56540             this.getRowComposite(row).addClass("x-grid-row-over");
56541         }
56542     },
56543
56544     onRowOut : function(e, t){
56545         var row;
56546         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56547             this.getRowComposite(row).removeClass("x-grid-row-over");
56548         }
56549     },
56550
56551     renderHeaders : function(){
56552         var cm = this.cm;
56553         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56554         var cb = [], lb = [], sb = [], lsb = [], p = {};
56555         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56556             p.cellId = "x-grid-hd-0-" + i;
56557             p.splitId = "x-grid-csplit-0-" + i;
56558             p.id = cm.getColumnId(i);
56559             p.value = cm.getColumnHeader(i) || "";
56560             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56561             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56562             if(!cm.isLocked(i)){
56563                 cb[cb.length] = ct.apply(p);
56564                 sb[sb.length] = st.apply(p);
56565             }else{
56566                 lb[lb.length] = ct.apply(p);
56567                 lsb[lsb.length] = st.apply(p);
56568             }
56569         }
56570         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56571                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56572     },
56573
56574     updateHeaders : function(){
56575         var html = this.renderHeaders();
56576         this.lockedHd.update(html[0]);
56577         this.mainHd.update(html[1]);
56578     },
56579
56580     /**
56581      * Focuses the specified row.
56582      * @param {Number} row The row index
56583      */
56584     focusRow : function(row)
56585     {
56586         //Roo.log('GridView.focusRow');
56587         var x = this.scroller.dom.scrollLeft;
56588         this.focusCell(row, 0, false);
56589         this.scroller.dom.scrollLeft = x;
56590     },
56591
56592     /**
56593      * Focuses the specified cell.
56594      * @param {Number} row The row index
56595      * @param {Number} col The column index
56596      * @param {Boolean} hscroll false to disable horizontal scrolling
56597      */
56598     focusCell : function(row, col, hscroll)
56599     {
56600         //Roo.log('GridView.focusCell');
56601         var el = this.ensureVisible(row, col, hscroll);
56602         this.focusEl.alignTo(el, "tl-tl");
56603         if(Roo.isGecko){
56604             this.focusEl.focus();
56605         }else{
56606             this.focusEl.focus.defer(1, this.focusEl);
56607         }
56608     },
56609
56610     /**
56611      * Scrolls the specified cell into view
56612      * @param {Number} row The row index
56613      * @param {Number} col The column index
56614      * @param {Boolean} hscroll false to disable horizontal scrolling
56615      */
56616     ensureVisible : function(row, col, hscroll)
56617     {
56618         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56619         //return null; //disable for testing.
56620         if(typeof row != "number"){
56621             row = row.rowIndex;
56622         }
56623         if(row < 0 && row >= this.ds.getCount()){
56624             return  null;
56625         }
56626         col = (col !== undefined ? col : 0);
56627         var cm = this.grid.colModel;
56628         while(cm.isHidden(col)){
56629             col++;
56630         }
56631
56632         var el = this.getCell(row, col);
56633         if(!el){
56634             return null;
56635         }
56636         var c = this.scroller.dom;
56637
56638         var ctop = parseInt(el.offsetTop, 10);
56639         var cleft = parseInt(el.offsetLeft, 10);
56640         var cbot = ctop + el.offsetHeight;
56641         var cright = cleft + el.offsetWidth;
56642         
56643         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56644         var stop = parseInt(c.scrollTop, 10);
56645         var sleft = parseInt(c.scrollLeft, 10);
56646         var sbot = stop + ch;
56647         var sright = sleft + c.clientWidth;
56648         /*
56649         Roo.log('GridView.ensureVisible:' +
56650                 ' ctop:' + ctop +
56651                 ' c.clientHeight:' + c.clientHeight +
56652                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56653                 ' stop:' + stop +
56654                 ' cbot:' + cbot +
56655                 ' sbot:' + sbot +
56656                 ' ch:' + ch  
56657                 );
56658         */
56659         if(ctop < stop){
56660              c.scrollTop = ctop;
56661             //Roo.log("set scrolltop to ctop DISABLE?");
56662         }else if(cbot > sbot){
56663             //Roo.log("set scrolltop to cbot-ch");
56664             c.scrollTop = cbot-ch;
56665         }
56666         
56667         if(hscroll !== false){
56668             if(cleft < sleft){
56669                 c.scrollLeft = cleft;
56670             }else if(cright > sright){
56671                 c.scrollLeft = cright-c.clientWidth;
56672             }
56673         }
56674          
56675         return el;
56676     },
56677
56678     updateColumns : function(){
56679         this.grid.stopEditing();
56680         var cm = this.grid.colModel, colIds = this.getColumnIds();
56681         //var totalWidth = cm.getTotalWidth();
56682         var pos = 0;
56683         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56684             //if(cm.isHidden(i)) continue;
56685             var w = cm.getColumnWidth(i);
56686             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56687             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56688         }
56689         this.updateSplitters();
56690     },
56691
56692     generateRules : function(cm){
56693         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56694         Roo.util.CSS.removeStyleSheet(rulesId);
56695         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56696             var cid = cm.getColumnId(i);
56697             var align = '';
56698             if(cm.config[i].align){
56699                 align = 'text-align:'+cm.config[i].align+';';
56700             }
56701             var hidden = '';
56702             if(cm.isHidden(i)){
56703                 hidden = 'display:none;';
56704             }
56705             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56706             ruleBuf.push(
56707                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56708                     this.hdSelector, cid, " {\n", align, width, "}\n",
56709                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56710                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56711         }
56712         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56713     },
56714
56715     updateSplitters : function(){
56716         var cm = this.cm, s = this.getSplitters();
56717         if(s){ // splitters not created yet
56718             var pos = 0, locked = true;
56719             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56720                 if(cm.isHidden(i)) {
56721                     continue;
56722                 }
56723                 var w = cm.getColumnWidth(i); // make sure it's a number
56724                 if(!cm.isLocked(i) && locked){
56725                     pos = 0;
56726                     locked = false;
56727                 }
56728                 pos += w;
56729                 s[i].style.left = (pos-this.splitOffset) + "px";
56730             }
56731         }
56732     },
56733
56734     handleHiddenChange : function(colModel, colIndex, hidden){
56735         if(hidden){
56736             this.hideColumn(colIndex);
56737         }else{
56738             this.unhideColumn(colIndex);
56739         }
56740     },
56741
56742     hideColumn : function(colIndex){
56743         var cid = this.getColumnId(colIndex);
56744         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56745         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56746         if(Roo.isSafari){
56747             this.updateHeaders();
56748         }
56749         this.updateSplitters();
56750         this.layout();
56751     },
56752
56753     unhideColumn : function(colIndex){
56754         var cid = this.getColumnId(colIndex);
56755         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56756         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56757
56758         if(Roo.isSafari){
56759             this.updateHeaders();
56760         }
56761         this.updateSplitters();
56762         this.layout();
56763     },
56764
56765     insertRows : function(dm, firstRow, lastRow, isUpdate){
56766         if(firstRow == 0 && lastRow == dm.getCount()-1){
56767             this.refresh();
56768         }else{
56769             if(!isUpdate){
56770                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56771             }
56772             var s = this.getScrollState();
56773             var markup = this.renderRows(firstRow, lastRow);
56774             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56775             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56776             this.restoreScroll(s);
56777             if(!isUpdate){
56778                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56779                 this.syncRowHeights(firstRow, lastRow);
56780                 this.stripeRows(firstRow);
56781                 this.layout();
56782             }
56783         }
56784     },
56785
56786     bufferRows : function(markup, target, index){
56787         var before = null, trows = target.rows, tbody = target.tBodies[0];
56788         if(index < trows.length){
56789             before = trows[index];
56790         }
56791         var b = document.createElement("div");
56792         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56793         var rows = b.firstChild.rows;
56794         for(var i = 0, len = rows.length; i < len; i++){
56795             if(before){
56796                 tbody.insertBefore(rows[0], before);
56797             }else{
56798                 tbody.appendChild(rows[0]);
56799             }
56800         }
56801         b.innerHTML = "";
56802         b = null;
56803     },
56804
56805     deleteRows : function(dm, firstRow, lastRow){
56806         if(dm.getRowCount()<1){
56807             this.fireEvent("beforerefresh", this);
56808             this.mainBody.update("");
56809             this.lockedBody.update("");
56810             this.fireEvent("refresh", this);
56811         }else{
56812             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56813             var bt = this.getBodyTable();
56814             var tbody = bt.firstChild;
56815             var rows = bt.rows;
56816             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56817                 tbody.removeChild(rows[firstRow]);
56818             }
56819             this.stripeRows(firstRow);
56820             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56821         }
56822     },
56823
56824     updateRows : function(dataSource, firstRow, lastRow){
56825         var s = this.getScrollState();
56826         this.refresh();
56827         this.restoreScroll(s);
56828     },
56829
56830     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56831         if(!noRefresh){
56832            this.refresh();
56833         }
56834         this.updateHeaderSortState();
56835     },
56836
56837     getScrollState : function(){
56838         
56839         var sb = this.scroller.dom;
56840         return {left: sb.scrollLeft, top: sb.scrollTop};
56841     },
56842
56843     stripeRows : function(startRow){
56844         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56845             return;
56846         }
56847         startRow = startRow || 0;
56848         var rows = this.getBodyTable().rows;
56849         var lrows = this.getLockedTable().rows;
56850         var cls = ' x-grid-row-alt ';
56851         for(var i = startRow, len = rows.length; i < len; i++){
56852             var row = rows[i], lrow = lrows[i];
56853             var isAlt = ((i+1) % 2 == 0);
56854             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56855             if(isAlt == hasAlt){
56856                 continue;
56857             }
56858             if(isAlt){
56859                 row.className += " x-grid-row-alt";
56860             }else{
56861                 row.className = row.className.replace("x-grid-row-alt", "");
56862             }
56863             if(lrow){
56864                 lrow.className = row.className;
56865             }
56866         }
56867     },
56868
56869     restoreScroll : function(state){
56870         //Roo.log('GridView.restoreScroll');
56871         var sb = this.scroller.dom;
56872         sb.scrollLeft = state.left;
56873         sb.scrollTop = state.top;
56874         this.syncScroll();
56875     },
56876
56877     syncScroll : function(){
56878         //Roo.log('GridView.syncScroll');
56879         var sb = this.scroller.dom;
56880         var sh = this.mainHd.dom;
56881         var bs = this.mainBody.dom;
56882         var lv = this.lockedBody.dom;
56883         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56884         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56885     },
56886
56887     handleScroll : function(e){
56888         this.syncScroll();
56889         var sb = this.scroller.dom;
56890         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56891         e.stopEvent();
56892     },
56893
56894     handleWheel : function(e){
56895         var d = e.getWheelDelta();
56896         this.scroller.dom.scrollTop -= d*22;
56897         // set this here to prevent jumpy scrolling on large tables
56898         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56899         e.stopEvent();
56900     },
56901
56902     renderRows : function(startRow, endRow){
56903         // pull in all the crap needed to render rows
56904         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56905         var colCount = cm.getColumnCount();
56906
56907         if(ds.getCount() < 1){
56908             return ["", ""];
56909         }
56910
56911         // build a map for all the columns
56912         var cs = [];
56913         for(var i = 0; i < colCount; i++){
56914             var name = cm.getDataIndex(i);
56915             cs[i] = {
56916                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56917                 renderer : cm.getRenderer(i),
56918                 id : cm.getColumnId(i),
56919                 locked : cm.isLocked(i),
56920                 has_editor : cm.isCellEditable(i)
56921             };
56922         }
56923
56924         startRow = startRow || 0;
56925         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56926
56927         // records to render
56928         var rs = ds.getRange(startRow, endRow);
56929
56930         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56931     },
56932
56933     // As much as I hate to duplicate code, this was branched because FireFox really hates
56934     // [].join("") on strings. The performance difference was substantial enough to
56935     // branch this function
56936     doRender : Roo.isGecko ?
56937             function(cs, rs, ds, startRow, colCount, stripe){
56938                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56939                 // buffers
56940                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56941                 
56942                 var hasListener = this.grid.hasListener('rowclass');
56943                 var rowcfg = {};
56944                 for(var j = 0, len = rs.length; j < len; j++){
56945                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56946                     for(var i = 0; i < colCount; i++){
56947                         c = cs[i];
56948                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56949                         p.id = c.id;
56950                         p.css = p.attr = "";
56951                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56952                         if(p.value == undefined || p.value === "") {
56953                             p.value = "&#160;";
56954                         }
56955                         if(c.has_editor){
56956                             p.css += ' x-grid-editable-cell';
56957                         }
56958                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56959                             p.css +=  ' x-grid-dirty-cell';
56960                         }
56961                         var markup = ct.apply(p);
56962                         if(!c.locked){
56963                             cb+= markup;
56964                         }else{
56965                             lcb+= markup;
56966                         }
56967                     }
56968                     var alt = [];
56969                     if(stripe && ((rowIndex+1) % 2 == 0)){
56970                         alt.push("x-grid-row-alt")
56971                     }
56972                     if(r.dirty){
56973                         alt.push(  " x-grid-dirty-row");
56974                     }
56975                     rp.cells = lcb;
56976                     if(this.getRowClass){
56977                         alt.push(this.getRowClass(r, rowIndex));
56978                     }
56979                     if (hasListener) {
56980                         rowcfg = {
56981                              
56982                             record: r,
56983                             rowIndex : rowIndex,
56984                             rowClass : ''
56985                         };
56986                         this.grid.fireEvent('rowclass', this, rowcfg);
56987                         alt.push(rowcfg.rowClass);
56988                     }
56989                     rp.alt = alt.join(" ");
56990                     lbuf+= rt.apply(rp);
56991                     rp.cells = cb;
56992                     buf+=  rt.apply(rp);
56993                 }
56994                 return [lbuf, buf];
56995             } :
56996             function(cs, rs, ds, startRow, colCount, stripe){
56997                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56998                 // buffers
56999                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57000                 var hasListener = this.grid.hasListener('rowclass');
57001  
57002                 var rowcfg = {};
57003                 for(var j = 0, len = rs.length; j < len; j++){
57004                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57005                     for(var i = 0; i < colCount; i++){
57006                         c = cs[i];
57007                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57008                         p.id = c.id;
57009                         p.css = p.attr = "";
57010                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57011                         if(p.value == undefined || p.value === "") {
57012                             p.value = "&#160;";
57013                         }
57014                         //Roo.log(c);
57015                          if(c.has_editor){
57016                             p.css += ' x-grid-editable-cell';
57017                         }
57018                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57019                             p.css += ' x-grid-dirty-cell' 
57020                         }
57021                         
57022                         var markup = ct.apply(p);
57023                         if(!c.locked){
57024                             cb[cb.length] = markup;
57025                         }else{
57026                             lcb[lcb.length] = markup;
57027                         }
57028                     }
57029                     var alt = [];
57030                     if(stripe && ((rowIndex+1) % 2 == 0)){
57031                         alt.push( "x-grid-row-alt");
57032                     }
57033                     if(r.dirty){
57034                         alt.push(" x-grid-dirty-row");
57035                     }
57036                     rp.cells = lcb;
57037                     if(this.getRowClass){
57038                         alt.push( this.getRowClass(r, rowIndex));
57039                     }
57040                     if (hasListener) {
57041                         rowcfg = {
57042                              
57043                             record: r,
57044                             rowIndex : rowIndex,
57045                             rowClass : ''
57046                         };
57047                         this.grid.fireEvent('rowclass', this, rowcfg);
57048                         alt.push(rowcfg.rowClass);
57049                     }
57050                     
57051                     rp.alt = alt.join(" ");
57052                     rp.cells = lcb.join("");
57053                     lbuf[lbuf.length] = rt.apply(rp);
57054                     rp.cells = cb.join("");
57055                     buf[buf.length] =  rt.apply(rp);
57056                 }
57057                 return [lbuf.join(""), buf.join("")];
57058             },
57059
57060     renderBody : function(){
57061         var markup = this.renderRows();
57062         var bt = this.templates.body;
57063         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57064     },
57065
57066     /**
57067      * Refreshes the grid
57068      * @param {Boolean} headersToo
57069      */
57070     refresh : function(headersToo){
57071         this.fireEvent("beforerefresh", this);
57072         this.grid.stopEditing();
57073         var result = this.renderBody();
57074         this.lockedBody.update(result[0]);
57075         this.mainBody.update(result[1]);
57076         if(headersToo === true){
57077             this.updateHeaders();
57078             this.updateColumns();
57079             this.updateSplitters();
57080             this.updateHeaderSortState();
57081         }
57082         this.syncRowHeights();
57083         this.layout();
57084         this.fireEvent("refresh", this);
57085     },
57086
57087     handleColumnMove : function(cm, oldIndex, newIndex){
57088         this.indexMap = null;
57089         var s = this.getScrollState();
57090         this.refresh(true);
57091         this.restoreScroll(s);
57092         this.afterMove(newIndex);
57093     },
57094
57095     afterMove : function(colIndex){
57096         if(this.enableMoveAnim && Roo.enableFx){
57097             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57098         }
57099         // if multisort - fix sortOrder, and reload..
57100         if (this.grid.dataSource.multiSort) {
57101             // the we can call sort again..
57102             var dm = this.grid.dataSource;
57103             var cm = this.grid.colModel;
57104             var so = [];
57105             for(var i = 0; i < cm.config.length; i++ ) {
57106                 
57107                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57108                     continue; // dont' bother, it's not in sort list or being set.
57109                 }
57110                 
57111                 so.push(cm.config[i].dataIndex);
57112             };
57113             dm.sortOrder = so;
57114             dm.load(dm.lastOptions);
57115             
57116             
57117         }
57118         
57119     },
57120
57121     updateCell : function(dm, rowIndex, dataIndex){
57122         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57123         if(typeof colIndex == "undefined"){ // not present in grid
57124             return;
57125         }
57126         var cm = this.grid.colModel;
57127         var cell = this.getCell(rowIndex, colIndex);
57128         var cellText = this.getCellText(rowIndex, colIndex);
57129
57130         var p = {
57131             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57132             id : cm.getColumnId(colIndex),
57133             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57134         };
57135         var renderer = cm.getRenderer(colIndex);
57136         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57137         if(typeof val == "undefined" || val === "") {
57138             val = "&#160;";
57139         }
57140         cellText.innerHTML = val;
57141         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57142         this.syncRowHeights(rowIndex, rowIndex);
57143     },
57144
57145     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57146         var maxWidth = 0;
57147         if(this.grid.autoSizeHeaders){
57148             var h = this.getHeaderCellMeasure(colIndex);
57149             maxWidth = Math.max(maxWidth, h.scrollWidth);
57150         }
57151         var tb, index;
57152         if(this.cm.isLocked(colIndex)){
57153             tb = this.getLockedTable();
57154             index = colIndex;
57155         }else{
57156             tb = this.getBodyTable();
57157             index = colIndex - this.cm.getLockedCount();
57158         }
57159         if(tb && tb.rows){
57160             var rows = tb.rows;
57161             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57162             for(var i = 0; i < stopIndex; i++){
57163                 var cell = rows[i].childNodes[index].firstChild;
57164                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57165             }
57166         }
57167         return maxWidth + /*margin for error in IE*/ 5;
57168     },
57169     /**
57170      * Autofit a column to its content.
57171      * @param {Number} colIndex
57172      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57173      */
57174      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57175          if(this.cm.isHidden(colIndex)){
57176              return; // can't calc a hidden column
57177          }
57178         if(forceMinSize){
57179             var cid = this.cm.getColumnId(colIndex);
57180             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57181            if(this.grid.autoSizeHeaders){
57182                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57183            }
57184         }
57185         var newWidth = this.calcColumnWidth(colIndex);
57186         this.cm.setColumnWidth(colIndex,
57187             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57188         if(!suppressEvent){
57189             this.grid.fireEvent("columnresize", colIndex, newWidth);
57190         }
57191     },
57192
57193     /**
57194      * Autofits all columns to their content and then expands to fit any extra space in the grid
57195      */
57196      autoSizeColumns : function(){
57197         var cm = this.grid.colModel;
57198         var colCount = cm.getColumnCount();
57199         for(var i = 0; i < colCount; i++){
57200             this.autoSizeColumn(i, true, true);
57201         }
57202         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57203             this.fitColumns();
57204         }else{
57205             this.updateColumns();
57206             this.layout();
57207         }
57208     },
57209
57210     /**
57211      * Autofits all columns to the grid's width proportionate with their current size
57212      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57213      */
57214     fitColumns : function(reserveScrollSpace){
57215         var cm = this.grid.colModel;
57216         var colCount = cm.getColumnCount();
57217         var cols = [];
57218         var width = 0;
57219         var i, w;
57220         for (i = 0; i < colCount; i++){
57221             if(!cm.isHidden(i) && !cm.isFixed(i)){
57222                 w = cm.getColumnWidth(i);
57223                 cols.push(i);
57224                 cols.push(w);
57225                 width += w;
57226             }
57227         }
57228         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57229         if(reserveScrollSpace){
57230             avail -= 17;
57231         }
57232         var frac = (avail - cm.getTotalWidth())/width;
57233         while (cols.length){
57234             w = cols.pop();
57235             i = cols.pop();
57236             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57237         }
57238         this.updateColumns();
57239         this.layout();
57240     },
57241
57242     onRowSelect : function(rowIndex){
57243         var row = this.getRowComposite(rowIndex);
57244         row.addClass("x-grid-row-selected");
57245     },
57246
57247     onRowDeselect : function(rowIndex){
57248         var row = this.getRowComposite(rowIndex);
57249         row.removeClass("x-grid-row-selected");
57250     },
57251
57252     onCellSelect : function(row, col){
57253         var cell = this.getCell(row, col);
57254         if(cell){
57255             Roo.fly(cell).addClass("x-grid-cell-selected");
57256         }
57257     },
57258
57259     onCellDeselect : function(row, col){
57260         var cell = this.getCell(row, col);
57261         if(cell){
57262             Roo.fly(cell).removeClass("x-grid-cell-selected");
57263         }
57264     },
57265
57266     updateHeaderSortState : function(){
57267         
57268         // sort state can be single { field: xxx, direction : yyy}
57269         // or   { xxx=>ASC , yyy : DESC ..... }
57270         
57271         var mstate = {};
57272         if (!this.ds.multiSort) { 
57273             var state = this.ds.getSortState();
57274             if(!state){
57275                 return;
57276             }
57277             mstate[state.field] = state.direction;
57278             // FIXME... - this is not used here.. but might be elsewhere..
57279             this.sortState = state;
57280             
57281         } else {
57282             mstate = this.ds.sortToggle;
57283         }
57284         //remove existing sort classes..
57285         
57286         var sc = this.sortClasses;
57287         var hds = this.el.select(this.headerSelector).removeClass(sc);
57288         
57289         for(var f in mstate) {
57290         
57291             var sortColumn = this.cm.findColumnIndex(f);
57292             
57293             if(sortColumn != -1){
57294                 var sortDir = mstate[f];        
57295                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57296             }
57297         }
57298         
57299          
57300         
57301     },
57302
57303
57304     handleHeaderClick : function(g, index,e){
57305         
57306         Roo.log("header click");
57307         
57308         if (Roo.isTouch) {
57309             // touch events on header are handled by context
57310             this.handleHdCtx(g,index,e);
57311             return;
57312         }
57313         
57314         
57315         if(this.headersDisabled){
57316             return;
57317         }
57318         var dm = g.dataSource, cm = g.colModel;
57319         if(!cm.isSortable(index)){
57320             return;
57321         }
57322         g.stopEditing();
57323         
57324         if (dm.multiSort) {
57325             // update the sortOrder
57326             var so = [];
57327             for(var i = 0; i < cm.config.length; i++ ) {
57328                 
57329                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57330                     continue; // dont' bother, it's not in sort list or being set.
57331                 }
57332                 
57333                 so.push(cm.config[i].dataIndex);
57334             };
57335             dm.sortOrder = so;
57336         }
57337         
57338         
57339         dm.sort(cm.getDataIndex(index));
57340     },
57341
57342
57343     destroy : function(){
57344         if(this.colMenu){
57345             this.colMenu.removeAll();
57346             Roo.menu.MenuMgr.unregister(this.colMenu);
57347             this.colMenu.getEl().remove();
57348             delete this.colMenu;
57349         }
57350         if(this.hmenu){
57351             this.hmenu.removeAll();
57352             Roo.menu.MenuMgr.unregister(this.hmenu);
57353             this.hmenu.getEl().remove();
57354             delete this.hmenu;
57355         }
57356         if(this.grid.enableColumnMove){
57357             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57358             if(dds){
57359                 for(var dd in dds){
57360                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57361                         var elid = dds[dd].dragElId;
57362                         dds[dd].unreg();
57363                         Roo.get(elid).remove();
57364                     } else if(dds[dd].config.isTarget){
57365                         dds[dd].proxyTop.remove();
57366                         dds[dd].proxyBottom.remove();
57367                         dds[dd].unreg();
57368                     }
57369                     if(Roo.dd.DDM.locationCache[dd]){
57370                         delete Roo.dd.DDM.locationCache[dd];
57371                     }
57372                 }
57373                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57374             }
57375         }
57376         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57377         this.bind(null, null);
57378         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57379     },
57380
57381     handleLockChange : function(){
57382         this.refresh(true);
57383     },
57384
57385     onDenyColumnLock : function(){
57386
57387     },
57388
57389     onDenyColumnHide : function(){
57390
57391     },
57392
57393     handleHdMenuClick : function(item){
57394         var index = this.hdCtxIndex;
57395         var cm = this.cm, ds = this.ds;
57396         switch(item.id){
57397             case "asc":
57398                 ds.sort(cm.getDataIndex(index), "ASC");
57399                 break;
57400             case "desc":
57401                 ds.sort(cm.getDataIndex(index), "DESC");
57402                 break;
57403             case "lock":
57404                 var lc = cm.getLockedCount();
57405                 if(cm.getColumnCount(true) <= lc+1){
57406                     this.onDenyColumnLock();
57407                     return;
57408                 }
57409                 if(lc != index){
57410                     cm.setLocked(index, true, true);
57411                     cm.moveColumn(index, lc);
57412                     this.grid.fireEvent("columnmove", index, lc);
57413                 }else{
57414                     cm.setLocked(index, true);
57415                 }
57416             break;
57417             case "unlock":
57418                 var lc = cm.getLockedCount();
57419                 if((lc-1) != index){
57420                     cm.setLocked(index, false, true);
57421                     cm.moveColumn(index, lc-1);
57422                     this.grid.fireEvent("columnmove", index, lc-1);
57423                 }else{
57424                     cm.setLocked(index, false);
57425                 }
57426             break;
57427             case 'wider': // used to expand cols on touch..
57428             case 'narrow':
57429                 var cw = cm.getColumnWidth(index);
57430                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57431                 cw = Math.max(0, cw);
57432                 cw = Math.min(cw,4000);
57433                 cm.setColumnWidth(index, cw);
57434                 break;
57435                 
57436             default:
57437                 index = cm.getIndexById(item.id.substr(4));
57438                 if(index != -1){
57439                     if(item.checked && cm.getColumnCount(true) <= 1){
57440                         this.onDenyColumnHide();
57441                         return false;
57442                     }
57443                     cm.setHidden(index, item.checked);
57444                 }
57445         }
57446         return true;
57447     },
57448
57449     beforeColMenuShow : function(){
57450         var cm = this.cm,  colCount = cm.getColumnCount();
57451         this.colMenu.removeAll();
57452         for(var i = 0; i < colCount; i++){
57453             this.colMenu.add(new Roo.menu.CheckItem({
57454                 id: "col-"+cm.getColumnId(i),
57455                 text: cm.getColumnHeader(i),
57456                 checked: !cm.isHidden(i),
57457                 hideOnClick:false
57458             }));
57459         }
57460     },
57461
57462     handleHdCtx : function(g, index, e){
57463         e.stopEvent();
57464         var hd = this.getHeaderCell(index);
57465         this.hdCtxIndex = index;
57466         var ms = this.hmenu.items, cm = this.cm;
57467         ms.get("asc").setDisabled(!cm.isSortable(index));
57468         ms.get("desc").setDisabled(!cm.isSortable(index));
57469         if(this.grid.enableColLock !== false){
57470             ms.get("lock").setDisabled(cm.isLocked(index));
57471             ms.get("unlock").setDisabled(!cm.isLocked(index));
57472         }
57473         this.hmenu.show(hd, "tl-bl");
57474     },
57475
57476     handleHdOver : function(e){
57477         var hd = this.findHeaderCell(e.getTarget());
57478         if(hd && !this.headersDisabled){
57479             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57480                this.fly(hd).addClass("x-grid-hd-over");
57481             }
57482         }
57483     },
57484
57485     handleHdOut : function(e){
57486         var hd = this.findHeaderCell(e.getTarget());
57487         if(hd){
57488             this.fly(hd).removeClass("x-grid-hd-over");
57489         }
57490     },
57491
57492     handleSplitDblClick : function(e, t){
57493         var i = this.getCellIndex(t);
57494         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57495             this.autoSizeColumn(i, true);
57496             this.layout();
57497         }
57498     },
57499
57500     render : function(){
57501
57502         var cm = this.cm;
57503         var colCount = cm.getColumnCount();
57504
57505         if(this.grid.monitorWindowResize === true){
57506             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57507         }
57508         var header = this.renderHeaders();
57509         var body = this.templates.body.apply({rows:""});
57510         var html = this.templates.master.apply({
57511             lockedBody: body,
57512             body: body,
57513             lockedHeader: header[0],
57514             header: header[1]
57515         });
57516
57517         //this.updateColumns();
57518
57519         this.grid.getGridEl().dom.innerHTML = html;
57520
57521         this.initElements();
57522         
57523         // a kludge to fix the random scolling effect in webkit
57524         this.el.on("scroll", function() {
57525             this.el.dom.scrollTop=0; // hopefully not recursive..
57526         },this);
57527
57528         this.scroller.on("scroll", this.handleScroll, this);
57529         this.lockedBody.on("mousewheel", this.handleWheel, this);
57530         this.mainBody.on("mousewheel", this.handleWheel, this);
57531
57532         this.mainHd.on("mouseover", this.handleHdOver, this);
57533         this.mainHd.on("mouseout", this.handleHdOut, this);
57534         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57535                 {delegate: "."+this.splitClass});
57536
57537         this.lockedHd.on("mouseover", this.handleHdOver, this);
57538         this.lockedHd.on("mouseout", this.handleHdOut, this);
57539         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57540                 {delegate: "."+this.splitClass});
57541
57542         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57543             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57544         }
57545
57546         this.updateSplitters();
57547
57548         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57549             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57550             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57551         }
57552
57553         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57554             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57555             this.hmenu.add(
57556                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57557                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57558             );
57559             if(this.grid.enableColLock !== false){
57560                 this.hmenu.add('-',
57561                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57562                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57563                 );
57564             }
57565             if (Roo.isTouch) {
57566                  this.hmenu.add('-',
57567                     {id:"wider", text: this.columnsWiderText},
57568                     {id:"narrow", text: this.columnsNarrowText }
57569                 );
57570                 
57571                  
57572             }
57573             
57574             if(this.grid.enableColumnHide !== false){
57575
57576                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57577                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57578                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57579
57580                 this.hmenu.add('-',
57581                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57582                 );
57583             }
57584             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57585
57586             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57587         }
57588
57589         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57590             this.dd = new Roo.grid.GridDragZone(this.grid, {
57591                 ddGroup : this.grid.ddGroup || 'GridDD'
57592             });
57593             
57594         }
57595
57596         /*
57597         for(var i = 0; i < colCount; i++){
57598             if(cm.isHidden(i)){
57599                 this.hideColumn(i);
57600             }
57601             if(cm.config[i].align){
57602                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57603                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57604             }
57605         }*/
57606         
57607         this.updateHeaderSortState();
57608
57609         this.beforeInitialResize();
57610         this.layout(true);
57611
57612         // two part rendering gives faster view to the user
57613         this.renderPhase2.defer(1, this);
57614     },
57615
57616     renderPhase2 : function(){
57617         // render the rows now
57618         this.refresh();
57619         if(this.grid.autoSizeColumns){
57620             this.autoSizeColumns();
57621         }
57622     },
57623
57624     beforeInitialResize : function(){
57625
57626     },
57627
57628     onColumnSplitterMoved : function(i, w){
57629         this.userResized = true;
57630         var cm = this.grid.colModel;
57631         cm.setColumnWidth(i, w, true);
57632         var cid = cm.getColumnId(i);
57633         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57634         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57635         this.updateSplitters();
57636         this.layout();
57637         this.grid.fireEvent("columnresize", i, w);
57638     },
57639
57640     syncRowHeights : function(startIndex, endIndex){
57641         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57642             startIndex = startIndex || 0;
57643             var mrows = this.getBodyTable().rows;
57644             var lrows = this.getLockedTable().rows;
57645             var len = mrows.length-1;
57646             endIndex = Math.min(endIndex || len, len);
57647             for(var i = startIndex; i <= endIndex; i++){
57648                 var m = mrows[i], l = lrows[i];
57649                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57650                 m.style.height = l.style.height = h + "px";
57651             }
57652         }
57653     },
57654
57655     layout : function(initialRender, is2ndPass){
57656         var g = this.grid;
57657         var auto = g.autoHeight;
57658         var scrollOffset = 16;
57659         var c = g.getGridEl(), cm = this.cm,
57660                 expandCol = g.autoExpandColumn,
57661                 gv = this;
57662         //c.beginMeasure();
57663
57664         if(!c.dom.offsetWidth){ // display:none?
57665             if(initialRender){
57666                 this.lockedWrap.show();
57667                 this.mainWrap.show();
57668             }
57669             return;
57670         }
57671
57672         var hasLock = this.cm.isLocked(0);
57673
57674         var tbh = this.headerPanel.getHeight();
57675         var bbh = this.footerPanel.getHeight();
57676
57677         if(auto){
57678             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57679             var newHeight = ch + c.getBorderWidth("tb");
57680             if(g.maxHeight){
57681                 newHeight = Math.min(g.maxHeight, newHeight);
57682             }
57683             c.setHeight(newHeight);
57684         }
57685
57686         if(g.autoWidth){
57687             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57688         }
57689
57690         var s = this.scroller;
57691
57692         var csize = c.getSize(true);
57693
57694         this.el.setSize(csize.width, csize.height);
57695
57696         this.headerPanel.setWidth(csize.width);
57697         this.footerPanel.setWidth(csize.width);
57698
57699         var hdHeight = this.mainHd.getHeight();
57700         var vw = csize.width;
57701         var vh = csize.height - (tbh + bbh);
57702
57703         s.setSize(vw, vh);
57704
57705         var bt = this.getBodyTable();
57706         
57707         if(cm.getLockedCount() == cm.config.length){
57708             bt = this.getLockedTable();
57709         }
57710         
57711         var ltWidth = hasLock ?
57712                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57713
57714         var scrollHeight = bt.offsetHeight;
57715         var scrollWidth = ltWidth + bt.offsetWidth;
57716         var vscroll = false, hscroll = false;
57717
57718         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57719
57720         var lw = this.lockedWrap, mw = this.mainWrap;
57721         var lb = this.lockedBody, mb = this.mainBody;
57722
57723         setTimeout(function(){
57724             var t = s.dom.offsetTop;
57725             var w = s.dom.clientWidth,
57726                 h = s.dom.clientHeight;
57727
57728             lw.setTop(t);
57729             lw.setSize(ltWidth, h);
57730
57731             mw.setLeftTop(ltWidth, t);
57732             mw.setSize(w-ltWidth, h);
57733
57734             lb.setHeight(h-hdHeight);
57735             mb.setHeight(h-hdHeight);
57736
57737             if(is2ndPass !== true && !gv.userResized && expandCol){
57738                 // high speed resize without full column calculation
57739                 
57740                 var ci = cm.getIndexById(expandCol);
57741                 if (ci < 0) {
57742                     ci = cm.findColumnIndex(expandCol);
57743                 }
57744                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57745                 var expandId = cm.getColumnId(ci);
57746                 var  tw = cm.getTotalWidth(false);
57747                 var currentWidth = cm.getColumnWidth(ci);
57748                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57749                 if(currentWidth != cw){
57750                     cm.setColumnWidth(ci, cw, true);
57751                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57752                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57753                     gv.updateSplitters();
57754                     gv.layout(false, true);
57755                 }
57756             }
57757
57758             if(initialRender){
57759                 lw.show();
57760                 mw.show();
57761             }
57762             //c.endMeasure();
57763         }, 10);
57764     },
57765
57766     onWindowResize : function(){
57767         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57768             return;
57769         }
57770         this.layout();
57771     },
57772
57773     appendFooter : function(parentEl){
57774         return null;
57775     },
57776
57777     sortAscText : "Sort Ascending",
57778     sortDescText : "Sort Descending",
57779     lockText : "Lock Column",
57780     unlockText : "Unlock Column",
57781     columnsText : "Columns",
57782  
57783     columnsWiderText : "Wider",
57784     columnsNarrowText : "Thinner"
57785 });
57786
57787
57788 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57789     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57790     this.proxy.el.addClass('x-grid3-col-dd');
57791 };
57792
57793 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57794     handleMouseDown : function(e){
57795
57796     },
57797
57798     callHandleMouseDown : function(e){
57799         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57800     }
57801 });
57802 /*
57803  * Based on:
57804  * Ext JS Library 1.1.1
57805  * Copyright(c) 2006-2007, Ext JS, LLC.
57806  *
57807  * Originally Released Under LGPL - original licence link has changed is not relivant.
57808  *
57809  * Fork - LGPL
57810  * <script type="text/javascript">
57811  */
57812  
57813 // private
57814 // This is a support class used internally by the Grid components
57815 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57816     this.grid = grid;
57817     this.view = grid.getView();
57818     this.proxy = this.view.resizeProxy;
57819     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57820         "gridSplitters" + this.grid.getGridEl().id, {
57821         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57822     });
57823     this.setHandleElId(Roo.id(hd));
57824     this.setOuterHandleElId(Roo.id(hd2));
57825     this.scroll = false;
57826 };
57827 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57828     fly: Roo.Element.fly,
57829
57830     b4StartDrag : function(x, y){
57831         this.view.headersDisabled = true;
57832         this.proxy.setHeight(this.view.mainWrap.getHeight());
57833         var w = this.cm.getColumnWidth(this.cellIndex);
57834         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57835         this.resetConstraints();
57836         this.setXConstraint(minw, 1000);
57837         this.setYConstraint(0, 0);
57838         this.minX = x - minw;
57839         this.maxX = x + 1000;
57840         this.startPos = x;
57841         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57842     },
57843
57844
57845     handleMouseDown : function(e){
57846         ev = Roo.EventObject.setEvent(e);
57847         var t = this.fly(ev.getTarget());
57848         if(t.hasClass("x-grid-split")){
57849             this.cellIndex = this.view.getCellIndex(t.dom);
57850             this.split = t.dom;
57851             this.cm = this.grid.colModel;
57852             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57853                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57854             }
57855         }
57856     },
57857
57858     endDrag : function(e){
57859         this.view.headersDisabled = false;
57860         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57861         var diff = endX - this.startPos;
57862         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57863     },
57864
57865     autoOffset : function(){
57866         this.setDelta(0,0);
57867     }
57868 });/*
57869  * Based on:
57870  * Ext JS Library 1.1.1
57871  * Copyright(c) 2006-2007, Ext JS, LLC.
57872  *
57873  * Originally Released Under LGPL - original licence link has changed is not relivant.
57874  *
57875  * Fork - LGPL
57876  * <script type="text/javascript">
57877  */
57878  
57879 // private
57880 // This is a support class used internally by the Grid components
57881 Roo.grid.GridDragZone = function(grid, config){
57882     this.view = grid.getView();
57883     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57884     if(this.view.lockedBody){
57885         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57886         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57887     }
57888     this.scroll = false;
57889     this.grid = grid;
57890     this.ddel = document.createElement('div');
57891     this.ddel.className = 'x-grid-dd-wrap';
57892 };
57893
57894 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57895     ddGroup : "GridDD",
57896
57897     getDragData : function(e){
57898         var t = Roo.lib.Event.getTarget(e);
57899         var rowIndex = this.view.findRowIndex(t);
57900         var sm = this.grid.selModel;
57901             
57902         //Roo.log(rowIndex);
57903         
57904         if (sm.getSelectedCell) {
57905             // cell selection..
57906             if (!sm.getSelectedCell()) {
57907                 return false;
57908             }
57909             if (rowIndex != sm.getSelectedCell()[0]) {
57910                 return false;
57911             }
57912         
57913         }
57914         
57915         if(rowIndex !== false){
57916             
57917             // if editorgrid.. 
57918             
57919             
57920             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57921                
57922             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57923               //  
57924             //}
57925             if (e.hasModifier()){
57926                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57927             }
57928             
57929             Roo.log("getDragData");
57930             
57931             return {
57932                 grid: this.grid,
57933                 ddel: this.ddel,
57934                 rowIndex: rowIndex,
57935                 selections:sm.getSelections ? sm.getSelections() : (
57936                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57937                 )
57938             };
57939         }
57940         return false;
57941     },
57942
57943     onInitDrag : function(e){
57944         var data = this.dragData;
57945         this.ddel.innerHTML = this.grid.getDragDropText();
57946         this.proxy.update(this.ddel);
57947         // fire start drag?
57948     },
57949
57950     afterRepair : function(){
57951         this.dragging = false;
57952     },
57953
57954     getRepairXY : function(e, data){
57955         return false;
57956     },
57957
57958     onEndDrag : function(data, e){
57959         // fire end drag?
57960     },
57961
57962     onValidDrop : function(dd, e, id){
57963         // fire drag drop?
57964         this.hideProxy();
57965     },
57966
57967     beforeInvalidDrop : function(e, id){
57968
57969     }
57970 });/*
57971  * Based on:
57972  * Ext JS Library 1.1.1
57973  * Copyright(c) 2006-2007, Ext JS, LLC.
57974  *
57975  * Originally Released Under LGPL - original licence link has changed is not relivant.
57976  *
57977  * Fork - LGPL
57978  * <script type="text/javascript">
57979  */
57980  
57981
57982 /**
57983  * @class Roo.grid.ColumnModel
57984  * @extends Roo.util.Observable
57985  * This is the default implementation of a ColumnModel used by the Grid. It defines
57986  * the columns in the grid.
57987  * <br>Usage:<br>
57988  <pre><code>
57989  var colModel = new Roo.grid.ColumnModel([
57990         {header: "Ticker", width: 60, sortable: true, locked: true},
57991         {header: "Company Name", width: 150, sortable: true},
57992         {header: "Market Cap.", width: 100, sortable: true},
57993         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57994         {header: "Employees", width: 100, sortable: true, resizable: false}
57995  ]);
57996  </code></pre>
57997  * <p>
57998  
57999  * The config options listed for this class are options which may appear in each
58000  * individual column definition.
58001  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58002  * @constructor
58003  * @param {Object} config An Array of column config objects. See this class's
58004  * config objects for details.
58005 */
58006 Roo.grid.ColumnModel = function(config){
58007         /**
58008      * The config passed into the constructor
58009      */
58010     this.config = config;
58011     this.lookup = {};
58012
58013     // if no id, create one
58014     // if the column does not have a dataIndex mapping,
58015     // map it to the order it is in the config
58016     for(var i = 0, len = config.length; i < len; i++){
58017         var c = config[i];
58018         if(typeof c.dataIndex == "undefined"){
58019             c.dataIndex = i;
58020         }
58021         if(typeof c.renderer == "string"){
58022             c.renderer = Roo.util.Format[c.renderer];
58023         }
58024         if(typeof c.id == "undefined"){
58025             c.id = Roo.id();
58026         }
58027         if(c.editor && c.editor.xtype){
58028             c.editor  = Roo.factory(c.editor, Roo.grid);
58029         }
58030         if(c.editor && c.editor.isFormField){
58031             c.editor = new Roo.grid.GridEditor(c.editor);
58032         }
58033         this.lookup[c.id] = c;
58034     }
58035
58036     /**
58037      * The width of columns which have no width specified (defaults to 100)
58038      * @type Number
58039      */
58040     this.defaultWidth = 100;
58041
58042     /**
58043      * Default sortable of columns which have no sortable specified (defaults to false)
58044      * @type Boolean
58045      */
58046     this.defaultSortable = false;
58047
58048     this.addEvents({
58049         /**
58050              * @event widthchange
58051              * Fires when the width of a column changes.
58052              * @param {ColumnModel} this
58053              * @param {Number} columnIndex The column index
58054              * @param {Number} newWidth The new width
58055              */
58056             "widthchange": true,
58057         /**
58058              * @event headerchange
58059              * Fires when the text of a header changes.
58060              * @param {ColumnModel} this
58061              * @param {Number} columnIndex The column index
58062              * @param {Number} newText The new header text
58063              */
58064             "headerchange": true,
58065         /**
58066              * @event hiddenchange
58067              * Fires when a column is hidden or "unhidden".
58068              * @param {ColumnModel} this
58069              * @param {Number} columnIndex The column index
58070              * @param {Boolean} hidden true if hidden, false otherwise
58071              */
58072             "hiddenchange": true,
58073             /**
58074          * @event columnmoved
58075          * Fires when a column is moved.
58076          * @param {ColumnModel} this
58077          * @param {Number} oldIndex
58078          * @param {Number} newIndex
58079          */
58080         "columnmoved" : true,
58081         /**
58082          * @event columlockchange
58083          * Fires when a column's locked state is changed
58084          * @param {ColumnModel} this
58085          * @param {Number} colIndex
58086          * @param {Boolean} locked true if locked
58087          */
58088         "columnlockchange" : true
58089     });
58090     Roo.grid.ColumnModel.superclass.constructor.call(this);
58091 };
58092 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58093     /**
58094      * @cfg {String} header The header text to display in the Grid view.
58095      */
58096     /**
58097      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58098      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58099      * specified, the column's index is used as an index into the Record's data Array.
58100      */
58101     /**
58102      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58103      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58104      */
58105     /**
58106      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58107      * Defaults to the value of the {@link #defaultSortable} property.
58108      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58109      */
58110     /**
58111      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58112      */
58113     /**
58114      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58115      */
58116     /**
58117      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58118      */
58119     /**
58120      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58121      */
58122     /**
58123      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58124      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58125      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58126      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58127      */
58128        /**
58129      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58130      */
58131     /**
58132      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58133      */
58134     /**
58135      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58136      */
58137     /**
58138      * @cfg {String} cursor (Optional)
58139      */
58140     /**
58141      * @cfg {String} tooltip (Optional)
58142      */
58143     /**
58144      * @cfg {Number} xs (Optional)
58145      */
58146     /**
58147      * @cfg {Number} sm (Optional)
58148      */
58149     /**
58150      * @cfg {Number} md (Optional)
58151      */
58152     /**
58153      * @cfg {Number} lg (Optional)
58154      */
58155     /**
58156      * Returns the id of the column at the specified index.
58157      * @param {Number} index The column index
58158      * @return {String} the id
58159      */
58160     getColumnId : function(index){
58161         return this.config[index].id;
58162     },
58163
58164     /**
58165      * Returns the column for a specified id.
58166      * @param {String} id The column id
58167      * @return {Object} the column
58168      */
58169     getColumnById : function(id){
58170         return this.lookup[id];
58171     },
58172
58173     
58174     /**
58175      * Returns the column for a specified dataIndex.
58176      * @param {String} dataIndex The column dataIndex
58177      * @return {Object|Boolean} the column or false if not found
58178      */
58179     getColumnByDataIndex: function(dataIndex){
58180         var index = this.findColumnIndex(dataIndex);
58181         return index > -1 ? this.config[index] : false;
58182     },
58183     
58184     /**
58185      * Returns the index for a specified column id.
58186      * @param {String} id The column id
58187      * @return {Number} the index, or -1 if not found
58188      */
58189     getIndexById : function(id){
58190         for(var i = 0, len = this.config.length; i < len; i++){
58191             if(this.config[i].id == id){
58192                 return i;
58193             }
58194         }
58195         return -1;
58196     },
58197     
58198     /**
58199      * Returns the index for a specified column dataIndex.
58200      * @param {String} dataIndex The column dataIndex
58201      * @return {Number} the index, or -1 if not found
58202      */
58203     
58204     findColumnIndex : function(dataIndex){
58205         for(var i = 0, len = this.config.length; i < len; i++){
58206             if(this.config[i].dataIndex == dataIndex){
58207                 return i;
58208             }
58209         }
58210         return -1;
58211     },
58212     
58213     
58214     moveColumn : function(oldIndex, newIndex){
58215         var c = this.config[oldIndex];
58216         this.config.splice(oldIndex, 1);
58217         this.config.splice(newIndex, 0, c);
58218         this.dataMap = null;
58219         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58220     },
58221
58222     isLocked : function(colIndex){
58223         return this.config[colIndex].locked === true;
58224     },
58225
58226     setLocked : function(colIndex, value, suppressEvent){
58227         if(this.isLocked(colIndex) == value){
58228             return;
58229         }
58230         this.config[colIndex].locked = value;
58231         if(!suppressEvent){
58232             this.fireEvent("columnlockchange", this, colIndex, value);
58233         }
58234     },
58235
58236     getTotalLockedWidth : function(){
58237         var totalWidth = 0;
58238         for(var i = 0; i < this.config.length; i++){
58239             if(this.isLocked(i) && !this.isHidden(i)){
58240                 this.totalWidth += this.getColumnWidth(i);
58241             }
58242         }
58243         return totalWidth;
58244     },
58245
58246     getLockedCount : function(){
58247         for(var i = 0, len = this.config.length; i < len; i++){
58248             if(!this.isLocked(i)){
58249                 return i;
58250             }
58251         }
58252         
58253         return this.config.length;
58254     },
58255
58256     /**
58257      * Returns the number of columns.
58258      * @return {Number}
58259      */
58260     getColumnCount : function(visibleOnly){
58261         if(visibleOnly === true){
58262             var c = 0;
58263             for(var i = 0, len = this.config.length; i < len; i++){
58264                 if(!this.isHidden(i)){
58265                     c++;
58266                 }
58267             }
58268             return c;
58269         }
58270         return this.config.length;
58271     },
58272
58273     /**
58274      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58275      * @param {Function} fn
58276      * @param {Object} scope (optional)
58277      * @return {Array} result
58278      */
58279     getColumnsBy : function(fn, scope){
58280         var r = [];
58281         for(var i = 0, len = this.config.length; i < len; i++){
58282             var c = this.config[i];
58283             if(fn.call(scope||this, c, i) === true){
58284                 r[r.length] = c;
58285             }
58286         }
58287         return r;
58288     },
58289
58290     /**
58291      * Returns true if the specified column is sortable.
58292      * @param {Number} col The column index
58293      * @return {Boolean}
58294      */
58295     isSortable : function(col){
58296         if(typeof this.config[col].sortable == "undefined"){
58297             return this.defaultSortable;
58298         }
58299         return this.config[col].sortable;
58300     },
58301
58302     /**
58303      * Returns the rendering (formatting) function defined for the column.
58304      * @param {Number} col The column index.
58305      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58306      */
58307     getRenderer : function(col){
58308         if(!this.config[col].renderer){
58309             return Roo.grid.ColumnModel.defaultRenderer;
58310         }
58311         return this.config[col].renderer;
58312     },
58313
58314     /**
58315      * Sets the rendering (formatting) function for a column.
58316      * @param {Number} col The column index
58317      * @param {Function} fn The function to use to process the cell's raw data
58318      * to return HTML markup for the grid view. The render function is called with
58319      * the following parameters:<ul>
58320      * <li>Data value.</li>
58321      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58322      * <li>css A CSS style string to apply to the table cell.</li>
58323      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58324      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58325      * <li>Row index</li>
58326      * <li>Column index</li>
58327      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58328      */
58329     setRenderer : function(col, fn){
58330         this.config[col].renderer = fn;
58331     },
58332
58333     /**
58334      * Returns the width for the specified column.
58335      * @param {Number} col The column index
58336      * @return {Number}
58337      */
58338     getColumnWidth : function(col){
58339         return this.config[col].width * 1 || this.defaultWidth;
58340     },
58341
58342     /**
58343      * Sets the width for a column.
58344      * @param {Number} col The column index
58345      * @param {Number} width The new width
58346      */
58347     setColumnWidth : function(col, width, suppressEvent){
58348         this.config[col].width = width;
58349         this.totalWidth = null;
58350         if(!suppressEvent){
58351              this.fireEvent("widthchange", this, col, width);
58352         }
58353     },
58354
58355     /**
58356      * Returns the total width of all columns.
58357      * @param {Boolean} includeHidden True to include hidden column widths
58358      * @return {Number}
58359      */
58360     getTotalWidth : function(includeHidden){
58361         if(!this.totalWidth){
58362             this.totalWidth = 0;
58363             for(var i = 0, len = this.config.length; i < len; i++){
58364                 if(includeHidden || !this.isHidden(i)){
58365                     this.totalWidth += this.getColumnWidth(i);
58366                 }
58367             }
58368         }
58369         return this.totalWidth;
58370     },
58371
58372     /**
58373      * Returns the header for the specified column.
58374      * @param {Number} col The column index
58375      * @return {String}
58376      */
58377     getColumnHeader : function(col){
58378         return this.config[col].header;
58379     },
58380
58381     /**
58382      * Sets the header for a column.
58383      * @param {Number} col The column index
58384      * @param {String} header The new header
58385      */
58386     setColumnHeader : function(col, header){
58387         this.config[col].header = header;
58388         this.fireEvent("headerchange", this, col, header);
58389     },
58390
58391     /**
58392      * Returns the tooltip for the specified column.
58393      * @param {Number} col The column index
58394      * @return {String}
58395      */
58396     getColumnTooltip : function(col){
58397             return this.config[col].tooltip;
58398     },
58399     /**
58400      * Sets the tooltip for a column.
58401      * @param {Number} col The column index
58402      * @param {String} tooltip The new tooltip
58403      */
58404     setColumnTooltip : function(col, tooltip){
58405             this.config[col].tooltip = tooltip;
58406     },
58407
58408     /**
58409      * Returns the dataIndex for the specified column.
58410      * @param {Number} col The column index
58411      * @return {Number}
58412      */
58413     getDataIndex : function(col){
58414         return this.config[col].dataIndex;
58415     },
58416
58417     /**
58418      * Sets the dataIndex for a column.
58419      * @param {Number} col The column index
58420      * @param {Number} dataIndex The new dataIndex
58421      */
58422     setDataIndex : function(col, dataIndex){
58423         this.config[col].dataIndex = dataIndex;
58424     },
58425
58426     
58427     
58428     /**
58429      * Returns true if the cell is editable.
58430      * @param {Number} colIndex The column index
58431      * @param {Number} rowIndex The row index - this is nto actually used..?
58432      * @return {Boolean}
58433      */
58434     isCellEditable : function(colIndex, rowIndex){
58435         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58436     },
58437
58438     /**
58439      * Returns the editor defined for the cell/column.
58440      * return false or null to disable editing.
58441      * @param {Number} colIndex The column index
58442      * @param {Number} rowIndex The row index
58443      * @return {Object}
58444      */
58445     getCellEditor : function(colIndex, rowIndex){
58446         return this.config[colIndex].editor;
58447     },
58448
58449     /**
58450      * Sets if a column is editable.
58451      * @param {Number} col The column index
58452      * @param {Boolean} editable True if the column is editable
58453      */
58454     setEditable : function(col, editable){
58455         this.config[col].editable = editable;
58456     },
58457
58458
58459     /**
58460      * Returns true if the column is hidden.
58461      * @param {Number} colIndex The column index
58462      * @return {Boolean}
58463      */
58464     isHidden : function(colIndex){
58465         return this.config[colIndex].hidden;
58466     },
58467
58468
58469     /**
58470      * Returns true if the column width cannot be changed
58471      */
58472     isFixed : function(colIndex){
58473         return this.config[colIndex].fixed;
58474     },
58475
58476     /**
58477      * Returns true if the column can be resized
58478      * @return {Boolean}
58479      */
58480     isResizable : function(colIndex){
58481         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58482     },
58483     /**
58484      * Sets if a column is hidden.
58485      * @param {Number} colIndex The column index
58486      * @param {Boolean} hidden True if the column is hidden
58487      */
58488     setHidden : function(colIndex, hidden){
58489         this.config[colIndex].hidden = hidden;
58490         this.totalWidth = null;
58491         this.fireEvent("hiddenchange", this, colIndex, hidden);
58492     },
58493
58494     /**
58495      * Sets the editor for a column.
58496      * @param {Number} col The column index
58497      * @param {Object} editor The editor object
58498      */
58499     setEditor : function(col, editor){
58500         this.config[col].editor = editor;
58501     }
58502 });
58503
58504 Roo.grid.ColumnModel.defaultRenderer = function(value)
58505 {
58506     if(typeof value == "object") {
58507         return value;
58508     }
58509         if(typeof value == "string" && value.length < 1){
58510             return "&#160;";
58511         }
58512     
58513         return String.format("{0}", value);
58514 };
58515
58516 // Alias for backwards compatibility
58517 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58518 /*
58519  * Based on:
58520  * Ext JS Library 1.1.1
58521  * Copyright(c) 2006-2007, Ext JS, LLC.
58522  *
58523  * Originally Released Under LGPL - original licence link has changed is not relivant.
58524  *
58525  * Fork - LGPL
58526  * <script type="text/javascript">
58527  */
58528
58529 /**
58530  * @class Roo.grid.AbstractSelectionModel
58531  * @extends Roo.util.Observable
58532  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58533  * implemented by descendant classes.  This class should not be directly instantiated.
58534  * @constructor
58535  */
58536 Roo.grid.AbstractSelectionModel = function(){
58537     this.locked = false;
58538     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58539 };
58540
58541 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58542     /** @ignore Called by the grid automatically. Do not call directly. */
58543     init : function(grid){
58544         this.grid = grid;
58545         this.initEvents();
58546     },
58547
58548     /**
58549      * Locks the selections.
58550      */
58551     lock : function(){
58552         this.locked = true;
58553     },
58554
58555     /**
58556      * Unlocks the selections.
58557      */
58558     unlock : function(){
58559         this.locked = false;
58560     },
58561
58562     /**
58563      * Returns true if the selections are locked.
58564      * @return {Boolean}
58565      */
58566     isLocked : function(){
58567         return this.locked;
58568     }
58569 });/*
58570  * Based on:
58571  * Ext JS Library 1.1.1
58572  * Copyright(c) 2006-2007, Ext JS, LLC.
58573  *
58574  * Originally Released Under LGPL - original licence link has changed is not relivant.
58575  *
58576  * Fork - LGPL
58577  * <script type="text/javascript">
58578  */
58579 /**
58580  * @extends Roo.grid.AbstractSelectionModel
58581  * @class Roo.grid.RowSelectionModel
58582  * The default SelectionModel used by {@link Roo.grid.Grid}.
58583  * It supports multiple selections and keyboard selection/navigation. 
58584  * @constructor
58585  * @param {Object} config
58586  */
58587 Roo.grid.RowSelectionModel = function(config){
58588     Roo.apply(this, config);
58589     this.selections = new Roo.util.MixedCollection(false, function(o){
58590         return o.id;
58591     });
58592
58593     this.last = false;
58594     this.lastActive = false;
58595
58596     this.addEvents({
58597         /**
58598              * @event selectionchange
58599              * Fires when the selection changes
58600              * @param {SelectionModel} this
58601              */
58602             "selectionchange" : true,
58603         /**
58604              * @event afterselectionchange
58605              * Fires after the selection changes (eg. by key press or clicking)
58606              * @param {SelectionModel} this
58607              */
58608             "afterselectionchange" : true,
58609         /**
58610              * @event beforerowselect
58611              * Fires when a row is selected being selected, return false to cancel.
58612              * @param {SelectionModel} this
58613              * @param {Number} rowIndex The selected index
58614              * @param {Boolean} keepExisting False if other selections will be cleared
58615              */
58616             "beforerowselect" : true,
58617         /**
58618              * @event rowselect
58619              * Fires when a row is selected.
58620              * @param {SelectionModel} this
58621              * @param {Number} rowIndex The selected index
58622              * @param {Roo.data.Record} r The record
58623              */
58624             "rowselect" : true,
58625         /**
58626              * @event rowdeselect
58627              * Fires when a row is deselected.
58628              * @param {SelectionModel} this
58629              * @param {Number} rowIndex The selected index
58630              */
58631         "rowdeselect" : true
58632     });
58633     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58634     this.locked = false;
58635 };
58636
58637 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58638     /**
58639      * @cfg {Boolean} singleSelect
58640      * True to allow selection of only one row at a time (defaults to false)
58641      */
58642     singleSelect : false,
58643
58644     // private
58645     initEvents : function(){
58646
58647         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58648             this.grid.on("mousedown", this.handleMouseDown, this);
58649         }else{ // allow click to work like normal
58650             this.grid.on("rowclick", this.handleDragableRowClick, this);
58651         }
58652
58653         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58654             "up" : function(e){
58655                 if(!e.shiftKey){
58656                     this.selectPrevious(e.shiftKey);
58657                 }else if(this.last !== false && this.lastActive !== false){
58658                     var last = this.last;
58659                     this.selectRange(this.last,  this.lastActive-1);
58660                     this.grid.getView().focusRow(this.lastActive);
58661                     if(last !== false){
58662                         this.last = last;
58663                     }
58664                 }else{
58665                     this.selectFirstRow();
58666                 }
58667                 this.fireEvent("afterselectionchange", this);
58668             },
58669             "down" : function(e){
58670                 if(!e.shiftKey){
58671                     this.selectNext(e.shiftKey);
58672                 }else if(this.last !== false && this.lastActive !== false){
58673                     var last = this.last;
58674                     this.selectRange(this.last,  this.lastActive+1);
58675                     this.grid.getView().focusRow(this.lastActive);
58676                     if(last !== false){
58677                         this.last = last;
58678                     }
58679                 }else{
58680                     this.selectFirstRow();
58681                 }
58682                 this.fireEvent("afterselectionchange", this);
58683             },
58684             scope: this
58685         });
58686
58687         var view = this.grid.view;
58688         view.on("refresh", this.onRefresh, this);
58689         view.on("rowupdated", this.onRowUpdated, this);
58690         view.on("rowremoved", this.onRemove, this);
58691     },
58692
58693     // private
58694     onRefresh : function(){
58695         var ds = this.grid.dataSource, i, v = this.grid.view;
58696         var s = this.selections;
58697         s.each(function(r){
58698             if((i = ds.indexOfId(r.id)) != -1){
58699                 v.onRowSelect(i);
58700                 s.add(ds.getAt(i)); // updating the selection relate data
58701             }else{
58702                 s.remove(r);
58703             }
58704         });
58705     },
58706
58707     // private
58708     onRemove : function(v, index, r){
58709         this.selections.remove(r);
58710     },
58711
58712     // private
58713     onRowUpdated : function(v, index, r){
58714         if(this.isSelected(r)){
58715             v.onRowSelect(index);
58716         }
58717     },
58718
58719     /**
58720      * Select records.
58721      * @param {Array} records The records to select
58722      * @param {Boolean} keepExisting (optional) True to keep existing selections
58723      */
58724     selectRecords : function(records, keepExisting){
58725         if(!keepExisting){
58726             this.clearSelections();
58727         }
58728         var ds = this.grid.dataSource;
58729         for(var i = 0, len = records.length; i < len; i++){
58730             this.selectRow(ds.indexOf(records[i]), true);
58731         }
58732     },
58733
58734     /**
58735      * Gets the number of selected rows.
58736      * @return {Number}
58737      */
58738     getCount : function(){
58739         return this.selections.length;
58740     },
58741
58742     /**
58743      * Selects the first row in the grid.
58744      */
58745     selectFirstRow : function(){
58746         this.selectRow(0);
58747     },
58748
58749     /**
58750      * Select the last row.
58751      * @param {Boolean} keepExisting (optional) True to keep existing selections
58752      */
58753     selectLastRow : function(keepExisting){
58754         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58755     },
58756
58757     /**
58758      * Selects the row immediately following the last selected row.
58759      * @param {Boolean} keepExisting (optional) True to keep existing selections
58760      */
58761     selectNext : function(keepExisting){
58762         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58763             this.selectRow(this.last+1, keepExisting);
58764             this.grid.getView().focusRow(this.last);
58765         }
58766     },
58767
58768     /**
58769      * Selects the row that precedes the last selected row.
58770      * @param {Boolean} keepExisting (optional) True to keep existing selections
58771      */
58772     selectPrevious : function(keepExisting){
58773         if(this.last){
58774             this.selectRow(this.last-1, keepExisting);
58775             this.grid.getView().focusRow(this.last);
58776         }
58777     },
58778
58779     /**
58780      * Returns the selected records
58781      * @return {Array} Array of selected records
58782      */
58783     getSelections : function(){
58784         return [].concat(this.selections.items);
58785     },
58786
58787     /**
58788      * Returns the first selected record.
58789      * @return {Record}
58790      */
58791     getSelected : function(){
58792         return this.selections.itemAt(0);
58793     },
58794
58795
58796     /**
58797      * Clears all selections.
58798      */
58799     clearSelections : function(fast){
58800         if(this.locked) {
58801             return;
58802         }
58803         if(fast !== true){
58804             var ds = this.grid.dataSource;
58805             var s = this.selections;
58806             s.each(function(r){
58807                 this.deselectRow(ds.indexOfId(r.id));
58808             }, this);
58809             s.clear();
58810         }else{
58811             this.selections.clear();
58812         }
58813         this.last = false;
58814     },
58815
58816
58817     /**
58818      * Selects all rows.
58819      */
58820     selectAll : function(){
58821         if(this.locked) {
58822             return;
58823         }
58824         this.selections.clear();
58825         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58826             this.selectRow(i, true);
58827         }
58828     },
58829
58830     /**
58831      * Returns True if there is a selection.
58832      * @return {Boolean}
58833      */
58834     hasSelection : function(){
58835         return this.selections.length > 0;
58836     },
58837
58838     /**
58839      * Returns True if the specified row is selected.
58840      * @param {Number/Record} record The record or index of the record to check
58841      * @return {Boolean}
58842      */
58843     isSelected : function(index){
58844         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58845         return (r && this.selections.key(r.id) ? true : false);
58846     },
58847
58848     /**
58849      * Returns True if the specified record id is selected.
58850      * @param {String} id The id of record to check
58851      * @return {Boolean}
58852      */
58853     isIdSelected : function(id){
58854         return (this.selections.key(id) ? true : false);
58855     },
58856
58857     // private
58858     handleMouseDown : function(e, t){
58859         var view = this.grid.getView(), rowIndex;
58860         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58861             return;
58862         };
58863         if(e.shiftKey && this.last !== false){
58864             var last = this.last;
58865             this.selectRange(last, rowIndex, e.ctrlKey);
58866             this.last = last; // reset the last
58867             view.focusRow(rowIndex);
58868         }else{
58869             var isSelected = this.isSelected(rowIndex);
58870             if(e.button !== 0 && isSelected){
58871                 view.focusRow(rowIndex);
58872             }else if(e.ctrlKey && isSelected){
58873                 this.deselectRow(rowIndex);
58874             }else if(!isSelected){
58875                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58876                 view.focusRow(rowIndex);
58877             }
58878         }
58879         this.fireEvent("afterselectionchange", this);
58880     },
58881     // private
58882     handleDragableRowClick :  function(grid, rowIndex, e) 
58883     {
58884         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58885             this.selectRow(rowIndex, false);
58886             grid.view.focusRow(rowIndex);
58887              this.fireEvent("afterselectionchange", this);
58888         }
58889     },
58890     
58891     /**
58892      * Selects multiple rows.
58893      * @param {Array} rows Array of the indexes of the row to select
58894      * @param {Boolean} keepExisting (optional) True to keep existing selections
58895      */
58896     selectRows : function(rows, keepExisting){
58897         if(!keepExisting){
58898             this.clearSelections();
58899         }
58900         for(var i = 0, len = rows.length; i < len; i++){
58901             this.selectRow(rows[i], true);
58902         }
58903     },
58904
58905     /**
58906      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58907      * @param {Number} startRow The index of the first row in the range
58908      * @param {Number} endRow The index of the last row in the range
58909      * @param {Boolean} keepExisting (optional) True to retain existing selections
58910      */
58911     selectRange : function(startRow, endRow, keepExisting){
58912         if(this.locked) {
58913             return;
58914         }
58915         if(!keepExisting){
58916             this.clearSelections();
58917         }
58918         if(startRow <= endRow){
58919             for(var i = startRow; i <= endRow; i++){
58920                 this.selectRow(i, true);
58921             }
58922         }else{
58923             for(var i = startRow; i >= endRow; i--){
58924                 this.selectRow(i, true);
58925             }
58926         }
58927     },
58928
58929     /**
58930      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58931      * @param {Number} startRow The index of the first row in the range
58932      * @param {Number} endRow The index of the last row in the range
58933      */
58934     deselectRange : function(startRow, endRow, preventViewNotify){
58935         if(this.locked) {
58936             return;
58937         }
58938         for(var i = startRow; i <= endRow; i++){
58939             this.deselectRow(i, preventViewNotify);
58940         }
58941     },
58942
58943     /**
58944      * Selects a row.
58945      * @param {Number} row The index of the row to select
58946      * @param {Boolean} keepExisting (optional) True to keep existing selections
58947      */
58948     selectRow : function(index, keepExisting, preventViewNotify){
58949         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58950             return;
58951         }
58952         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58953             if(!keepExisting || this.singleSelect){
58954                 this.clearSelections();
58955             }
58956             var r = this.grid.dataSource.getAt(index);
58957             this.selections.add(r);
58958             this.last = this.lastActive = index;
58959             if(!preventViewNotify){
58960                 this.grid.getView().onRowSelect(index);
58961             }
58962             this.fireEvent("rowselect", this, index, r);
58963             this.fireEvent("selectionchange", this);
58964         }
58965     },
58966
58967     /**
58968      * Deselects a row.
58969      * @param {Number} row The index of the row to deselect
58970      */
58971     deselectRow : function(index, preventViewNotify){
58972         if(this.locked) {
58973             return;
58974         }
58975         if(this.last == index){
58976             this.last = false;
58977         }
58978         if(this.lastActive == index){
58979             this.lastActive = false;
58980         }
58981         var r = this.grid.dataSource.getAt(index);
58982         this.selections.remove(r);
58983         if(!preventViewNotify){
58984             this.grid.getView().onRowDeselect(index);
58985         }
58986         this.fireEvent("rowdeselect", this, index);
58987         this.fireEvent("selectionchange", this);
58988     },
58989
58990     // private
58991     restoreLast : function(){
58992         if(this._last){
58993             this.last = this._last;
58994         }
58995     },
58996
58997     // private
58998     acceptsNav : function(row, col, cm){
58999         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59000     },
59001
59002     // private
59003     onEditorKey : function(field, e){
59004         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59005         if(k == e.TAB){
59006             e.stopEvent();
59007             ed.completeEdit();
59008             if(e.shiftKey){
59009                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59010             }else{
59011                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59012             }
59013         }else if(k == e.ENTER && !e.ctrlKey){
59014             e.stopEvent();
59015             ed.completeEdit();
59016             if(e.shiftKey){
59017                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59018             }else{
59019                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59020             }
59021         }else if(k == e.ESC){
59022             ed.cancelEdit();
59023         }
59024         if(newCell){
59025             g.startEditing(newCell[0], newCell[1]);
59026         }
59027     }
59028 });/*
59029  * Based on:
59030  * Ext JS Library 1.1.1
59031  * Copyright(c) 2006-2007, Ext JS, LLC.
59032  *
59033  * Originally Released Under LGPL - original licence link has changed is not relivant.
59034  *
59035  * Fork - LGPL
59036  * <script type="text/javascript">
59037  */
59038 /**
59039  * @class Roo.grid.CellSelectionModel
59040  * @extends Roo.grid.AbstractSelectionModel
59041  * This class provides the basic implementation for cell selection in a grid.
59042  * @constructor
59043  * @param {Object} config The object containing the configuration of this model.
59044  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59045  */
59046 Roo.grid.CellSelectionModel = function(config){
59047     Roo.apply(this, config);
59048
59049     this.selection = null;
59050
59051     this.addEvents({
59052         /**
59053              * @event beforerowselect
59054              * Fires before a cell is selected.
59055              * @param {SelectionModel} this
59056              * @param {Number} rowIndex The selected row index
59057              * @param {Number} colIndex The selected cell index
59058              */
59059             "beforecellselect" : true,
59060         /**
59061              * @event cellselect
59062              * Fires when a cell is selected.
59063              * @param {SelectionModel} this
59064              * @param {Number} rowIndex The selected row index
59065              * @param {Number} colIndex The selected cell index
59066              */
59067             "cellselect" : true,
59068         /**
59069              * @event selectionchange
59070              * Fires when the active selection changes.
59071              * @param {SelectionModel} this
59072              * @param {Object} selection null for no selection or an object (o) with two properties
59073                 <ul>
59074                 <li>o.record: the record object for the row the selection is in</li>
59075                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59076                 </ul>
59077              */
59078             "selectionchange" : true,
59079         /**
59080              * @event tabend
59081              * Fires when the tab (or enter) was pressed on the last editable cell
59082              * You can use this to trigger add new row.
59083              * @param {SelectionModel} this
59084              */
59085             "tabend" : true,
59086          /**
59087              * @event beforeeditnext
59088              * Fires before the next editable sell is made active
59089              * You can use this to skip to another cell or fire the tabend
59090              *    if you set cell to false
59091              * @param {Object} eventdata object : { cell : [ row, col ] } 
59092              */
59093             "beforeeditnext" : true
59094     });
59095     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59096 };
59097
59098 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59099     
59100     enter_is_tab: false,
59101
59102     /** @ignore */
59103     initEvents : function(){
59104         this.grid.on("mousedown", this.handleMouseDown, this);
59105         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59106         var view = this.grid.view;
59107         view.on("refresh", this.onViewChange, this);
59108         view.on("rowupdated", this.onRowUpdated, this);
59109         view.on("beforerowremoved", this.clearSelections, this);
59110         view.on("beforerowsinserted", this.clearSelections, this);
59111         if(this.grid.isEditor){
59112             this.grid.on("beforeedit", this.beforeEdit,  this);
59113         }
59114     },
59115
59116         //private
59117     beforeEdit : function(e){
59118         this.select(e.row, e.column, false, true, e.record);
59119     },
59120
59121         //private
59122     onRowUpdated : function(v, index, r){
59123         if(this.selection && this.selection.record == r){
59124             v.onCellSelect(index, this.selection.cell[1]);
59125         }
59126     },
59127
59128         //private
59129     onViewChange : function(){
59130         this.clearSelections(true);
59131     },
59132
59133         /**
59134          * Returns the currently selected cell,.
59135          * @return {Array} The selected cell (row, column) or null if none selected.
59136          */
59137     getSelectedCell : function(){
59138         return this.selection ? this.selection.cell : null;
59139     },
59140
59141     /**
59142      * Clears all selections.
59143      * @param {Boolean} true to prevent the gridview from being notified about the change.
59144      */
59145     clearSelections : function(preventNotify){
59146         var s = this.selection;
59147         if(s){
59148             if(preventNotify !== true){
59149                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59150             }
59151             this.selection = null;
59152             this.fireEvent("selectionchange", this, null);
59153         }
59154     },
59155
59156     /**
59157      * Returns true if there is a selection.
59158      * @return {Boolean}
59159      */
59160     hasSelection : function(){
59161         return this.selection ? true : false;
59162     },
59163
59164     /** @ignore */
59165     handleMouseDown : function(e, t){
59166         var v = this.grid.getView();
59167         if(this.isLocked()){
59168             return;
59169         };
59170         var row = v.findRowIndex(t);
59171         var cell = v.findCellIndex(t);
59172         if(row !== false && cell !== false){
59173             this.select(row, cell);
59174         }
59175     },
59176
59177     /**
59178      * Selects a cell.
59179      * @param {Number} rowIndex
59180      * @param {Number} collIndex
59181      */
59182     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59183         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59184             this.clearSelections();
59185             r = r || this.grid.dataSource.getAt(rowIndex);
59186             this.selection = {
59187                 record : r,
59188                 cell : [rowIndex, colIndex]
59189             };
59190             if(!preventViewNotify){
59191                 var v = this.grid.getView();
59192                 v.onCellSelect(rowIndex, colIndex);
59193                 if(preventFocus !== true){
59194                     v.focusCell(rowIndex, colIndex);
59195                 }
59196             }
59197             this.fireEvent("cellselect", this, rowIndex, colIndex);
59198             this.fireEvent("selectionchange", this, this.selection);
59199         }
59200     },
59201
59202         //private
59203     isSelectable : function(rowIndex, colIndex, cm){
59204         return !cm.isHidden(colIndex);
59205     },
59206
59207     /** @ignore */
59208     handleKeyDown : function(e){
59209         //Roo.log('Cell Sel Model handleKeyDown');
59210         if(!e.isNavKeyPress()){
59211             return;
59212         }
59213         var g = this.grid, s = this.selection;
59214         if(!s){
59215             e.stopEvent();
59216             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59217             if(cell){
59218                 this.select(cell[0], cell[1]);
59219             }
59220             return;
59221         }
59222         var sm = this;
59223         var walk = function(row, col, step){
59224             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59225         };
59226         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59227         var newCell;
59228
59229       
59230
59231         switch(k){
59232             case e.TAB:
59233                 // handled by onEditorKey
59234                 if (g.isEditor && g.editing) {
59235                     return;
59236                 }
59237                 if(e.shiftKey) {
59238                     newCell = walk(r, c-1, -1);
59239                 } else {
59240                     newCell = walk(r, c+1, 1);
59241                 }
59242                 break;
59243             
59244             case e.DOWN:
59245                newCell = walk(r+1, c, 1);
59246                 break;
59247             
59248             case e.UP:
59249                 newCell = walk(r-1, c, -1);
59250                 break;
59251             
59252             case e.RIGHT:
59253                 newCell = walk(r, c+1, 1);
59254                 break;
59255             
59256             case e.LEFT:
59257                 newCell = walk(r, c-1, -1);
59258                 break;
59259             
59260             case e.ENTER:
59261                 
59262                 if(g.isEditor && !g.editing){
59263                    g.startEditing(r, c);
59264                    e.stopEvent();
59265                    return;
59266                 }
59267                 
59268                 
59269              break;
59270         };
59271         if(newCell){
59272             this.select(newCell[0], newCell[1]);
59273             e.stopEvent();
59274             
59275         }
59276     },
59277
59278     acceptsNav : function(row, col, cm){
59279         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59280     },
59281     /**
59282      * Selects a cell.
59283      * @param {Number} field (not used) - as it's normally used as a listener
59284      * @param {Number} e - event - fake it by using
59285      *
59286      * var e = Roo.EventObjectImpl.prototype;
59287      * e.keyCode = e.TAB
59288      *
59289      * 
59290      */
59291     onEditorKey : function(field, e){
59292         
59293         var k = e.getKey(),
59294             newCell,
59295             g = this.grid,
59296             ed = g.activeEditor,
59297             forward = false;
59298         ///Roo.log('onEditorKey' + k);
59299         
59300         
59301         if (this.enter_is_tab && k == e.ENTER) {
59302             k = e.TAB;
59303         }
59304         
59305         if(k == e.TAB){
59306             if(e.shiftKey){
59307                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59308             }else{
59309                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59310                 forward = true;
59311             }
59312             
59313             e.stopEvent();
59314             
59315         } else if(k == e.ENTER &&  !e.ctrlKey){
59316             ed.completeEdit();
59317             e.stopEvent();
59318             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59319         
59320                 } else if(k == e.ESC){
59321             ed.cancelEdit();
59322         }
59323                 
59324         if (newCell) {
59325             var ecall = { cell : newCell, forward : forward };
59326             this.fireEvent('beforeeditnext', ecall );
59327             newCell = ecall.cell;
59328                         forward = ecall.forward;
59329         }
59330                 
59331         if(newCell){
59332             //Roo.log('next cell after edit');
59333             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59334         } else if (forward) {
59335             // tabbed past last
59336             this.fireEvent.defer(100, this, ['tabend',this]);
59337         }
59338     }
59339 });/*
59340  * Based on:
59341  * Ext JS Library 1.1.1
59342  * Copyright(c) 2006-2007, Ext JS, LLC.
59343  *
59344  * Originally Released Under LGPL - original licence link has changed is not relivant.
59345  *
59346  * Fork - LGPL
59347  * <script type="text/javascript">
59348  */
59349  
59350 /**
59351  * @class Roo.grid.EditorGrid
59352  * @extends Roo.grid.Grid
59353  * Class for creating and editable grid.
59354  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59355  * The container MUST have some type of size defined for the grid to fill. The container will be 
59356  * automatically set to position relative if it isn't already.
59357  * @param {Object} dataSource The data model to bind to
59358  * @param {Object} colModel The column model with info about this grid's columns
59359  */
59360 Roo.grid.EditorGrid = function(container, config){
59361     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59362     this.getGridEl().addClass("xedit-grid");
59363
59364     if(!this.selModel){
59365         this.selModel = new Roo.grid.CellSelectionModel();
59366     }
59367
59368     this.activeEditor = null;
59369
59370         this.addEvents({
59371             /**
59372              * @event beforeedit
59373              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59374              * <ul style="padding:5px;padding-left:16px;">
59375              * <li>grid - This grid</li>
59376              * <li>record - The record being edited</li>
59377              * <li>field - The field name being edited</li>
59378              * <li>value - The value for the field being edited.</li>
59379              * <li>row - The grid row index</li>
59380              * <li>column - The grid column index</li>
59381              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59382              * </ul>
59383              * @param {Object} e An edit event (see above for description)
59384              */
59385             "beforeedit" : true,
59386             /**
59387              * @event afteredit
59388              * Fires after a cell is edited. <br />
59389              * <ul style="padding:5px;padding-left:16px;">
59390              * <li>grid - This grid</li>
59391              * <li>record - The record being edited</li>
59392              * <li>field - The field name being edited</li>
59393              * <li>value - The value being set</li>
59394              * <li>originalValue - The original value for the field, before the edit.</li>
59395              * <li>row - The grid row index</li>
59396              * <li>column - The grid column index</li>
59397              * </ul>
59398              * @param {Object} e An edit event (see above for description)
59399              */
59400             "afteredit" : true,
59401             /**
59402              * @event validateedit
59403              * Fires after a cell is edited, but before the value is set in the record. 
59404          * You can use this to modify the value being set in the field, Return false
59405              * to cancel the change. The edit event object has the following properties <br />
59406              * <ul style="padding:5px;padding-left:16px;">
59407          * <li>editor - This editor</li>
59408              * <li>grid - This grid</li>
59409              * <li>record - The record being edited</li>
59410              * <li>field - The field name being edited</li>
59411              * <li>value - The value being set</li>
59412              * <li>originalValue - The original value for the field, before the edit.</li>
59413              * <li>row - The grid row index</li>
59414              * <li>column - The grid column index</li>
59415              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59416              * </ul>
59417              * @param {Object} e An edit event (see above for description)
59418              */
59419             "validateedit" : true
59420         });
59421     this.on("bodyscroll", this.stopEditing,  this);
59422     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59423 };
59424
59425 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59426     /**
59427      * @cfg {Number} clicksToEdit
59428      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59429      */
59430     clicksToEdit: 2,
59431
59432     // private
59433     isEditor : true,
59434     // private
59435     trackMouseOver: false, // causes very odd FF errors
59436
59437     onCellDblClick : function(g, row, col){
59438         this.startEditing(row, col);
59439     },
59440
59441     onEditComplete : function(ed, value, startValue){
59442         this.editing = false;
59443         this.activeEditor = null;
59444         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59445         var r = ed.record;
59446         var field = this.colModel.getDataIndex(ed.col);
59447         var e = {
59448             grid: this,
59449             record: r,
59450             field: field,
59451             originalValue: startValue,
59452             value: value,
59453             row: ed.row,
59454             column: ed.col,
59455             cancel:false,
59456             editor: ed
59457         };
59458         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59459         cell.show();
59460           
59461         if(String(value) !== String(startValue)){
59462             
59463             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59464                 r.set(field, e.value);
59465                 // if we are dealing with a combo box..
59466                 // then we also set the 'name' colum to be the displayField
59467                 if (ed.field.displayField && ed.field.name) {
59468                     r.set(ed.field.name, ed.field.el.dom.value);
59469                 }
59470                 
59471                 delete e.cancel; //?? why!!!
59472                 this.fireEvent("afteredit", e);
59473             }
59474         } else {
59475             this.fireEvent("afteredit", e); // always fire it!
59476         }
59477         this.view.focusCell(ed.row, ed.col);
59478     },
59479
59480     /**
59481      * Starts editing the specified for the specified row/column
59482      * @param {Number} rowIndex
59483      * @param {Number} colIndex
59484      */
59485     startEditing : function(row, col){
59486         this.stopEditing();
59487         if(this.colModel.isCellEditable(col, row)){
59488             this.view.ensureVisible(row, col, true);
59489           
59490             var r = this.dataSource.getAt(row);
59491             var field = this.colModel.getDataIndex(col);
59492             var cell = Roo.get(this.view.getCell(row,col));
59493             var e = {
59494                 grid: this,
59495                 record: r,
59496                 field: field,
59497                 value: r.data[field],
59498                 row: row,
59499                 column: col,
59500                 cancel:false 
59501             };
59502             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59503                 this.editing = true;
59504                 var ed = this.colModel.getCellEditor(col, row);
59505                 
59506                 if (!ed) {
59507                     return;
59508                 }
59509                 if(!ed.rendered){
59510                     ed.render(ed.parentEl || document.body);
59511                 }
59512                 ed.field.reset();
59513                
59514                 cell.hide();
59515                 
59516                 (function(){ // complex but required for focus issues in safari, ie and opera
59517                     ed.row = row;
59518                     ed.col = col;
59519                     ed.record = r;
59520                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59521                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59522                     this.activeEditor = ed;
59523                     var v = r.data[field];
59524                     ed.startEdit(this.view.getCell(row, col), v);
59525                     // combo's with 'displayField and name set
59526                     if (ed.field.displayField && ed.field.name) {
59527                         ed.field.el.dom.value = r.data[ed.field.name];
59528                     }
59529                     
59530                     
59531                 }).defer(50, this);
59532             }
59533         }
59534     },
59535         
59536     /**
59537      * Stops any active editing
59538      */
59539     stopEditing : function(){
59540         if(this.activeEditor){
59541             this.activeEditor.completeEdit();
59542         }
59543         this.activeEditor = null;
59544     },
59545         
59546          /**
59547      * Called to get grid's drag proxy text, by default returns this.ddText.
59548      * @return {String}
59549      */
59550     getDragDropText : function(){
59551         var count = this.selModel.getSelectedCell() ? 1 : 0;
59552         return String.format(this.ddText, count, count == 1 ? '' : 's');
59553     }
59554         
59555 });/*
59556  * Based on:
59557  * Ext JS Library 1.1.1
59558  * Copyright(c) 2006-2007, Ext JS, LLC.
59559  *
59560  * Originally Released Under LGPL - original licence link has changed is not relivant.
59561  *
59562  * Fork - LGPL
59563  * <script type="text/javascript">
59564  */
59565
59566 // private - not really -- you end up using it !
59567 // This is a support class used internally by the Grid components
59568
59569 /**
59570  * @class Roo.grid.GridEditor
59571  * @extends Roo.Editor
59572  * Class for creating and editable grid elements.
59573  * @param {Object} config any settings (must include field)
59574  */
59575 Roo.grid.GridEditor = function(field, config){
59576     if (!config && field.field) {
59577         config = field;
59578         field = Roo.factory(config.field, Roo.form);
59579     }
59580     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59581     field.monitorTab = false;
59582 };
59583
59584 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59585     
59586     /**
59587      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59588      */
59589     
59590     alignment: "tl-tl",
59591     autoSize: "width",
59592     hideEl : false,
59593     cls: "x-small-editor x-grid-editor",
59594     shim:false,
59595     shadow:"frame"
59596 });/*
59597  * Based on:
59598  * Ext JS Library 1.1.1
59599  * Copyright(c) 2006-2007, Ext JS, LLC.
59600  *
59601  * Originally Released Under LGPL - original licence link has changed is not relivant.
59602  *
59603  * Fork - LGPL
59604  * <script type="text/javascript">
59605  */
59606   
59607
59608   
59609 Roo.grid.PropertyRecord = Roo.data.Record.create([
59610     {name:'name',type:'string'},  'value'
59611 ]);
59612
59613
59614 Roo.grid.PropertyStore = function(grid, source){
59615     this.grid = grid;
59616     this.store = new Roo.data.Store({
59617         recordType : Roo.grid.PropertyRecord
59618     });
59619     this.store.on('update', this.onUpdate,  this);
59620     if(source){
59621         this.setSource(source);
59622     }
59623     Roo.grid.PropertyStore.superclass.constructor.call(this);
59624 };
59625
59626
59627
59628 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59629     setSource : function(o){
59630         this.source = o;
59631         this.store.removeAll();
59632         var data = [];
59633         for(var k in o){
59634             if(this.isEditableValue(o[k])){
59635                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59636             }
59637         }
59638         this.store.loadRecords({records: data}, {}, true);
59639     },
59640
59641     onUpdate : function(ds, record, type){
59642         if(type == Roo.data.Record.EDIT){
59643             var v = record.data['value'];
59644             var oldValue = record.modified['value'];
59645             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59646                 this.source[record.id] = v;
59647                 record.commit();
59648                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59649             }else{
59650                 record.reject();
59651             }
59652         }
59653     },
59654
59655     getProperty : function(row){
59656        return this.store.getAt(row);
59657     },
59658
59659     isEditableValue: function(val){
59660         if(val && val instanceof Date){
59661             return true;
59662         }else if(typeof val == 'object' || typeof val == 'function'){
59663             return false;
59664         }
59665         return true;
59666     },
59667
59668     setValue : function(prop, value){
59669         this.source[prop] = value;
59670         this.store.getById(prop).set('value', value);
59671     },
59672
59673     getSource : function(){
59674         return this.source;
59675     }
59676 });
59677
59678 Roo.grid.PropertyColumnModel = function(grid, store){
59679     this.grid = grid;
59680     var g = Roo.grid;
59681     g.PropertyColumnModel.superclass.constructor.call(this, [
59682         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59683         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59684     ]);
59685     this.store = store;
59686     this.bselect = Roo.DomHelper.append(document.body, {
59687         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59688             {tag: 'option', value: 'true', html: 'true'},
59689             {tag: 'option', value: 'false', html: 'false'}
59690         ]
59691     });
59692     Roo.id(this.bselect);
59693     var f = Roo.form;
59694     this.editors = {
59695         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59696         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59697         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59698         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59699         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59700     };
59701     this.renderCellDelegate = this.renderCell.createDelegate(this);
59702     this.renderPropDelegate = this.renderProp.createDelegate(this);
59703 };
59704
59705 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59706     
59707     
59708     nameText : 'Name',
59709     valueText : 'Value',
59710     
59711     dateFormat : 'm/j/Y',
59712     
59713     
59714     renderDate : function(dateVal){
59715         return dateVal.dateFormat(this.dateFormat);
59716     },
59717
59718     renderBool : function(bVal){
59719         return bVal ? 'true' : 'false';
59720     },
59721
59722     isCellEditable : function(colIndex, rowIndex){
59723         return colIndex == 1;
59724     },
59725
59726     getRenderer : function(col){
59727         return col == 1 ?
59728             this.renderCellDelegate : this.renderPropDelegate;
59729     },
59730
59731     renderProp : function(v){
59732         return this.getPropertyName(v);
59733     },
59734
59735     renderCell : function(val){
59736         var rv = val;
59737         if(val instanceof Date){
59738             rv = this.renderDate(val);
59739         }else if(typeof val == 'boolean'){
59740             rv = this.renderBool(val);
59741         }
59742         return Roo.util.Format.htmlEncode(rv);
59743     },
59744
59745     getPropertyName : function(name){
59746         var pn = this.grid.propertyNames;
59747         return pn && pn[name] ? pn[name] : name;
59748     },
59749
59750     getCellEditor : function(colIndex, rowIndex){
59751         var p = this.store.getProperty(rowIndex);
59752         var n = p.data['name'], val = p.data['value'];
59753         
59754         if(typeof(this.grid.customEditors[n]) == 'string'){
59755             return this.editors[this.grid.customEditors[n]];
59756         }
59757         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59758             return this.grid.customEditors[n];
59759         }
59760         if(val instanceof Date){
59761             return this.editors['date'];
59762         }else if(typeof val == 'number'){
59763             return this.editors['number'];
59764         }else if(typeof val == 'boolean'){
59765             return this.editors['boolean'];
59766         }else{
59767             return this.editors['string'];
59768         }
59769     }
59770 });
59771
59772 /**
59773  * @class Roo.grid.PropertyGrid
59774  * @extends Roo.grid.EditorGrid
59775  * This class represents the  interface of a component based property grid control.
59776  * <br><br>Usage:<pre><code>
59777  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59778       
59779  });
59780  // set any options
59781  grid.render();
59782  * </code></pre>
59783   
59784  * @constructor
59785  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59786  * The container MUST have some type of size defined for the grid to fill. The container will be
59787  * automatically set to position relative if it isn't already.
59788  * @param {Object} config A config object that sets properties on this grid.
59789  */
59790 Roo.grid.PropertyGrid = function(container, config){
59791     config = config || {};
59792     var store = new Roo.grid.PropertyStore(this);
59793     this.store = store;
59794     var cm = new Roo.grid.PropertyColumnModel(this, store);
59795     store.store.sort('name', 'ASC');
59796     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59797         ds: store.store,
59798         cm: cm,
59799         enableColLock:false,
59800         enableColumnMove:false,
59801         stripeRows:false,
59802         trackMouseOver: false,
59803         clicksToEdit:1
59804     }, config));
59805     this.getGridEl().addClass('x-props-grid');
59806     this.lastEditRow = null;
59807     this.on('columnresize', this.onColumnResize, this);
59808     this.addEvents({
59809          /**
59810              * @event beforepropertychange
59811              * Fires before a property changes (return false to stop?)
59812              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59813              * @param {String} id Record Id
59814              * @param {String} newval New Value
59815          * @param {String} oldval Old Value
59816              */
59817         "beforepropertychange": true,
59818         /**
59819              * @event propertychange
59820              * Fires after a property changes
59821              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59822              * @param {String} id Record Id
59823              * @param {String} newval New Value
59824          * @param {String} oldval Old Value
59825              */
59826         "propertychange": true
59827     });
59828     this.customEditors = this.customEditors || {};
59829 };
59830 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59831     
59832      /**
59833      * @cfg {Object} customEditors map of colnames=> custom editors.
59834      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59835      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59836      * false disables editing of the field.
59837          */
59838     
59839       /**
59840      * @cfg {Object} propertyNames map of property Names to their displayed value
59841          */
59842     
59843     render : function(){
59844         Roo.grid.PropertyGrid.superclass.render.call(this);
59845         this.autoSize.defer(100, this);
59846     },
59847
59848     autoSize : function(){
59849         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59850         if(this.view){
59851             this.view.fitColumns();
59852         }
59853     },
59854
59855     onColumnResize : function(){
59856         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59857         this.autoSize();
59858     },
59859     /**
59860      * Sets the data for the Grid
59861      * accepts a Key => Value object of all the elements avaiable.
59862      * @param {Object} data  to appear in grid.
59863      */
59864     setSource : function(source){
59865         this.store.setSource(source);
59866         //this.autoSize();
59867     },
59868     /**
59869      * Gets all the data from the grid.
59870      * @return {Object} data  data stored in grid
59871      */
59872     getSource : function(){
59873         return this.store.getSource();
59874     }
59875 });/*
59876   
59877  * Licence LGPL
59878  
59879  */
59880  
59881 /**
59882  * @class Roo.grid.Calendar
59883  * @extends Roo.util.Grid
59884  * This class extends the Grid to provide a calendar widget
59885  * <br><br>Usage:<pre><code>
59886  var grid = new Roo.grid.Calendar("my-container-id", {
59887      ds: myDataStore,
59888      cm: myColModel,
59889      selModel: mySelectionModel,
59890      autoSizeColumns: true,
59891      monitorWindowResize: false,
59892      trackMouseOver: true
59893      eventstore : real data store..
59894  });
59895  // set any options
59896  grid.render();
59897   
59898   * @constructor
59899  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59900  * The container MUST have some type of size defined for the grid to fill. The container will be
59901  * automatically set to position relative if it isn't already.
59902  * @param {Object} config A config object that sets properties on this grid.
59903  */
59904 Roo.grid.Calendar = function(container, config){
59905         // initialize the container
59906         this.container = Roo.get(container);
59907         this.container.update("");
59908         this.container.setStyle("overflow", "hidden");
59909     this.container.addClass('x-grid-container');
59910
59911     this.id = this.container.id;
59912
59913     Roo.apply(this, config);
59914     // check and correct shorthanded configs
59915     
59916     var rows = [];
59917     var d =1;
59918     for (var r = 0;r < 6;r++) {
59919         
59920         rows[r]=[];
59921         for (var c =0;c < 7;c++) {
59922             rows[r][c]= '';
59923         }
59924     }
59925     if (this.eventStore) {
59926         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59927         this.eventStore.on('load',this.onLoad, this);
59928         this.eventStore.on('beforeload',this.clearEvents, this);
59929          
59930     }
59931     
59932     this.dataSource = new Roo.data.Store({
59933             proxy: new Roo.data.MemoryProxy(rows),
59934             reader: new Roo.data.ArrayReader({}, [
59935                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59936     });
59937
59938     this.dataSource.load();
59939     this.ds = this.dataSource;
59940     this.ds.xmodule = this.xmodule || false;
59941     
59942     
59943     var cellRender = function(v,x,r)
59944     {
59945         return String.format(
59946             '<div class="fc-day  fc-widget-content"><div>' +
59947                 '<div class="fc-event-container"></div>' +
59948                 '<div class="fc-day-number">{0}</div>'+
59949                 
59950                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59951             '</div></div>', v);
59952     
59953     }
59954     
59955     
59956     this.colModel = new Roo.grid.ColumnModel( [
59957         {
59958             xtype: 'ColumnModel',
59959             xns: Roo.grid,
59960             dataIndex : 'weekday0',
59961             header : 'Sunday',
59962             renderer : cellRender
59963         },
59964         {
59965             xtype: 'ColumnModel',
59966             xns: Roo.grid,
59967             dataIndex : 'weekday1',
59968             header : 'Monday',
59969             renderer : cellRender
59970         },
59971         {
59972             xtype: 'ColumnModel',
59973             xns: Roo.grid,
59974             dataIndex : 'weekday2',
59975             header : 'Tuesday',
59976             renderer : cellRender
59977         },
59978         {
59979             xtype: 'ColumnModel',
59980             xns: Roo.grid,
59981             dataIndex : 'weekday3',
59982             header : 'Wednesday',
59983             renderer : cellRender
59984         },
59985         {
59986             xtype: 'ColumnModel',
59987             xns: Roo.grid,
59988             dataIndex : 'weekday4',
59989             header : 'Thursday',
59990             renderer : cellRender
59991         },
59992         {
59993             xtype: 'ColumnModel',
59994             xns: Roo.grid,
59995             dataIndex : 'weekday5',
59996             header : 'Friday',
59997             renderer : cellRender
59998         },
59999         {
60000             xtype: 'ColumnModel',
60001             xns: Roo.grid,
60002             dataIndex : 'weekday6',
60003             header : 'Saturday',
60004             renderer : cellRender
60005         }
60006     ]);
60007     this.cm = this.colModel;
60008     this.cm.xmodule = this.xmodule || false;
60009  
60010         
60011           
60012     //this.selModel = new Roo.grid.CellSelectionModel();
60013     //this.sm = this.selModel;
60014     //this.selModel.init(this);
60015     
60016     
60017     if(this.width){
60018         this.container.setWidth(this.width);
60019     }
60020
60021     if(this.height){
60022         this.container.setHeight(this.height);
60023     }
60024     /** @private */
60025         this.addEvents({
60026         // raw events
60027         /**
60028          * @event click
60029          * The raw click event for the entire grid.
60030          * @param {Roo.EventObject} e
60031          */
60032         "click" : true,
60033         /**
60034          * @event dblclick
60035          * The raw dblclick event for the entire grid.
60036          * @param {Roo.EventObject} e
60037          */
60038         "dblclick" : true,
60039         /**
60040          * @event contextmenu
60041          * The raw contextmenu event for the entire grid.
60042          * @param {Roo.EventObject} e
60043          */
60044         "contextmenu" : true,
60045         /**
60046          * @event mousedown
60047          * The raw mousedown event for the entire grid.
60048          * @param {Roo.EventObject} e
60049          */
60050         "mousedown" : true,
60051         /**
60052          * @event mouseup
60053          * The raw mouseup event for the entire grid.
60054          * @param {Roo.EventObject} e
60055          */
60056         "mouseup" : true,
60057         /**
60058          * @event mouseover
60059          * The raw mouseover event for the entire grid.
60060          * @param {Roo.EventObject} e
60061          */
60062         "mouseover" : true,
60063         /**
60064          * @event mouseout
60065          * The raw mouseout event for the entire grid.
60066          * @param {Roo.EventObject} e
60067          */
60068         "mouseout" : true,
60069         /**
60070          * @event keypress
60071          * The raw keypress event for the entire grid.
60072          * @param {Roo.EventObject} e
60073          */
60074         "keypress" : true,
60075         /**
60076          * @event keydown
60077          * The raw keydown event for the entire grid.
60078          * @param {Roo.EventObject} e
60079          */
60080         "keydown" : true,
60081
60082         // custom events
60083
60084         /**
60085          * @event cellclick
60086          * Fires when a cell is clicked
60087          * @param {Grid} this
60088          * @param {Number} rowIndex
60089          * @param {Number} columnIndex
60090          * @param {Roo.EventObject} e
60091          */
60092         "cellclick" : true,
60093         /**
60094          * @event celldblclick
60095          * Fires when a cell is double clicked
60096          * @param {Grid} this
60097          * @param {Number} rowIndex
60098          * @param {Number} columnIndex
60099          * @param {Roo.EventObject} e
60100          */
60101         "celldblclick" : true,
60102         /**
60103          * @event rowclick
60104          * Fires when a row is clicked
60105          * @param {Grid} this
60106          * @param {Number} rowIndex
60107          * @param {Roo.EventObject} e
60108          */
60109         "rowclick" : true,
60110         /**
60111          * @event rowdblclick
60112          * Fires when a row is double clicked
60113          * @param {Grid} this
60114          * @param {Number} rowIndex
60115          * @param {Roo.EventObject} e
60116          */
60117         "rowdblclick" : true,
60118         /**
60119          * @event headerclick
60120          * Fires when a header is clicked
60121          * @param {Grid} this
60122          * @param {Number} columnIndex
60123          * @param {Roo.EventObject} e
60124          */
60125         "headerclick" : true,
60126         /**
60127          * @event headerdblclick
60128          * Fires when a header cell is double clicked
60129          * @param {Grid} this
60130          * @param {Number} columnIndex
60131          * @param {Roo.EventObject} e
60132          */
60133         "headerdblclick" : true,
60134         /**
60135          * @event rowcontextmenu
60136          * Fires when a row is right clicked
60137          * @param {Grid} this
60138          * @param {Number} rowIndex
60139          * @param {Roo.EventObject} e
60140          */
60141         "rowcontextmenu" : true,
60142         /**
60143          * @event cellcontextmenu
60144          * Fires when a cell is right clicked
60145          * @param {Grid} this
60146          * @param {Number} rowIndex
60147          * @param {Number} cellIndex
60148          * @param {Roo.EventObject} e
60149          */
60150          "cellcontextmenu" : true,
60151         /**
60152          * @event headercontextmenu
60153          * Fires when a header is right clicked
60154          * @param {Grid} this
60155          * @param {Number} columnIndex
60156          * @param {Roo.EventObject} e
60157          */
60158         "headercontextmenu" : true,
60159         /**
60160          * @event bodyscroll
60161          * Fires when the body element is scrolled
60162          * @param {Number} scrollLeft
60163          * @param {Number} scrollTop
60164          */
60165         "bodyscroll" : true,
60166         /**
60167          * @event columnresize
60168          * Fires when the user resizes a column
60169          * @param {Number} columnIndex
60170          * @param {Number} newSize
60171          */
60172         "columnresize" : true,
60173         /**
60174          * @event columnmove
60175          * Fires when the user moves a column
60176          * @param {Number} oldIndex
60177          * @param {Number} newIndex
60178          */
60179         "columnmove" : true,
60180         /**
60181          * @event startdrag
60182          * Fires when row(s) start being dragged
60183          * @param {Grid} this
60184          * @param {Roo.GridDD} dd The drag drop object
60185          * @param {event} e The raw browser event
60186          */
60187         "startdrag" : true,
60188         /**
60189          * @event enddrag
60190          * Fires when a drag operation is complete
60191          * @param {Grid} this
60192          * @param {Roo.GridDD} dd The drag drop object
60193          * @param {event} e The raw browser event
60194          */
60195         "enddrag" : true,
60196         /**
60197          * @event dragdrop
60198          * Fires when dragged row(s) are dropped on a valid DD target
60199          * @param {Grid} this
60200          * @param {Roo.GridDD} dd The drag drop object
60201          * @param {String} targetId The target drag drop object
60202          * @param {event} e The raw browser event
60203          */
60204         "dragdrop" : true,
60205         /**
60206          * @event dragover
60207          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60208          * @param {Grid} this
60209          * @param {Roo.GridDD} dd The drag drop object
60210          * @param {String} targetId The target drag drop object
60211          * @param {event} e The raw browser event
60212          */
60213         "dragover" : true,
60214         /**
60215          * @event dragenter
60216          *  Fires when the dragged row(s) first cross another DD target while being dragged
60217          * @param {Grid} this
60218          * @param {Roo.GridDD} dd The drag drop object
60219          * @param {String} targetId The target drag drop object
60220          * @param {event} e The raw browser event
60221          */
60222         "dragenter" : true,
60223         /**
60224          * @event dragout
60225          * Fires when the dragged row(s) leave another DD target while being dragged
60226          * @param {Grid} this
60227          * @param {Roo.GridDD} dd The drag drop object
60228          * @param {String} targetId The target drag drop object
60229          * @param {event} e The raw browser event
60230          */
60231         "dragout" : true,
60232         /**
60233          * @event rowclass
60234          * Fires when a row is rendered, so you can change add a style to it.
60235          * @param {GridView} gridview   The grid view
60236          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60237          */
60238         'rowclass' : true,
60239
60240         /**
60241          * @event render
60242          * Fires when the grid is rendered
60243          * @param {Grid} grid
60244          */
60245         'render' : true,
60246             /**
60247              * @event select
60248              * Fires when a date is selected
60249              * @param {DatePicker} this
60250              * @param {Date} date The selected date
60251              */
60252         'select': true,
60253         /**
60254              * @event monthchange
60255              * Fires when the displayed month changes 
60256              * @param {DatePicker} this
60257              * @param {Date} date The selected month
60258              */
60259         'monthchange': true,
60260         /**
60261              * @event evententer
60262              * Fires when mouse over an event
60263              * @param {Calendar} this
60264              * @param {event} Event
60265              */
60266         'evententer': true,
60267         /**
60268              * @event eventleave
60269              * Fires when the mouse leaves an
60270              * @param {Calendar} this
60271              * @param {event}
60272              */
60273         'eventleave': true,
60274         /**
60275              * @event eventclick
60276              * Fires when the mouse click an
60277              * @param {Calendar} this
60278              * @param {event}
60279              */
60280         'eventclick': true,
60281         /**
60282              * @event eventrender
60283              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60284              * @param {Calendar} this
60285              * @param {data} data to be modified
60286              */
60287         'eventrender': true
60288         
60289     });
60290
60291     Roo.grid.Grid.superclass.constructor.call(this);
60292     this.on('render', function() {
60293         this.view.el.addClass('x-grid-cal'); 
60294         
60295         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60296
60297     },this);
60298     
60299     if (!Roo.grid.Calendar.style) {
60300         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60301             
60302             
60303             '.x-grid-cal .x-grid-col' :  {
60304                 height: 'auto !important',
60305                 'vertical-align': 'top'
60306             },
60307             '.x-grid-cal  .fc-event-hori' : {
60308                 height: '14px'
60309             }
60310              
60311             
60312         }, Roo.id());
60313     }
60314
60315     
60316     
60317 };
60318 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60319     /**
60320      * @cfg {Store} eventStore The store that loads events.
60321      */
60322     eventStore : 25,
60323
60324      
60325     activeDate : false,
60326     startDay : 0,
60327     autoWidth : true,
60328     monitorWindowResize : false,
60329
60330     
60331     resizeColumns : function() {
60332         var col = (this.view.el.getWidth() / 7) - 3;
60333         // loop through cols, and setWidth
60334         for(var i =0 ; i < 7 ; i++){
60335             this.cm.setColumnWidth(i, col);
60336         }
60337     },
60338      setDate :function(date) {
60339         
60340         Roo.log('setDate?');
60341         
60342         this.resizeColumns();
60343         var vd = this.activeDate;
60344         this.activeDate = date;
60345 //        if(vd && this.el){
60346 //            var t = date.getTime();
60347 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60348 //                Roo.log('using add remove');
60349 //                
60350 //                this.fireEvent('monthchange', this, date);
60351 //                
60352 //                this.cells.removeClass("fc-state-highlight");
60353 //                this.cells.each(function(c){
60354 //                   if(c.dateValue == t){
60355 //                       c.addClass("fc-state-highlight");
60356 //                       setTimeout(function(){
60357 //                            try{c.dom.firstChild.focus();}catch(e){}
60358 //                       }, 50);
60359 //                       return false;
60360 //                   }
60361 //                   return true;
60362 //                });
60363 //                return;
60364 //            }
60365 //        }
60366         
60367         var days = date.getDaysInMonth();
60368         
60369         var firstOfMonth = date.getFirstDateOfMonth();
60370         var startingPos = firstOfMonth.getDay()-this.startDay;
60371         
60372         if(startingPos < this.startDay){
60373             startingPos += 7;
60374         }
60375         
60376         var pm = date.add(Date.MONTH, -1);
60377         var prevStart = pm.getDaysInMonth()-startingPos;
60378 //        
60379         
60380         
60381         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60382         
60383         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60384         //this.cells.addClassOnOver('fc-state-hover');
60385         
60386         var cells = this.cells.elements;
60387         var textEls = this.textNodes;
60388         
60389         //Roo.each(cells, function(cell){
60390         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60391         //});
60392         
60393         days += startingPos;
60394
60395         // convert everything to numbers so it's fast
60396         var day = 86400000;
60397         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60398         //Roo.log(d);
60399         //Roo.log(pm);
60400         //Roo.log(prevStart);
60401         
60402         var today = new Date().clearTime().getTime();
60403         var sel = date.clearTime().getTime();
60404         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60405         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60406         var ddMatch = this.disabledDatesRE;
60407         var ddText = this.disabledDatesText;
60408         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60409         var ddaysText = this.disabledDaysText;
60410         var format = this.format;
60411         
60412         var setCellClass = function(cal, cell){
60413             
60414             //Roo.log('set Cell Class');
60415             cell.title = "";
60416             var t = d.getTime();
60417             
60418             //Roo.log(d);
60419             
60420             
60421             cell.dateValue = t;
60422             if(t == today){
60423                 cell.className += " fc-today";
60424                 cell.className += " fc-state-highlight";
60425                 cell.title = cal.todayText;
60426             }
60427             if(t == sel){
60428                 // disable highlight in other month..
60429                 cell.className += " fc-state-highlight";
60430                 
60431             }
60432             // disabling
60433             if(t < min) {
60434                 //cell.className = " fc-state-disabled";
60435                 cell.title = cal.minText;
60436                 return;
60437             }
60438             if(t > max) {
60439                 //cell.className = " fc-state-disabled";
60440                 cell.title = cal.maxText;
60441                 return;
60442             }
60443             if(ddays){
60444                 if(ddays.indexOf(d.getDay()) != -1){
60445                     // cell.title = ddaysText;
60446                    // cell.className = " fc-state-disabled";
60447                 }
60448             }
60449             if(ddMatch && format){
60450                 var fvalue = d.dateFormat(format);
60451                 if(ddMatch.test(fvalue)){
60452                     cell.title = ddText.replace("%0", fvalue);
60453                    cell.className = " fc-state-disabled";
60454                 }
60455             }
60456             
60457             if (!cell.initialClassName) {
60458                 cell.initialClassName = cell.dom.className;
60459             }
60460             
60461             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60462         };
60463
60464         var i = 0;
60465         
60466         for(; i < startingPos; i++) {
60467             cells[i].dayName =  (++prevStart);
60468             Roo.log(textEls[i]);
60469             d.setDate(d.getDate()+1);
60470             
60471             //cells[i].className = "fc-past fc-other-month";
60472             setCellClass(this, cells[i]);
60473         }
60474         
60475         var intDay = 0;
60476         
60477         for(; i < days; i++){
60478             intDay = i - startingPos + 1;
60479             cells[i].dayName =  (intDay);
60480             d.setDate(d.getDate()+1);
60481             
60482             cells[i].className = ''; // "x-date-active";
60483             setCellClass(this, cells[i]);
60484         }
60485         var extraDays = 0;
60486         
60487         for(; i < 42; i++) {
60488             //textEls[i].innerHTML = (++extraDays);
60489             
60490             d.setDate(d.getDate()+1);
60491             cells[i].dayName = (++extraDays);
60492             cells[i].className = "fc-future fc-other-month";
60493             setCellClass(this, cells[i]);
60494         }
60495         
60496         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60497         
60498         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60499         
60500         // this will cause all the cells to mis
60501         var rows= [];
60502         var i =0;
60503         for (var r = 0;r < 6;r++) {
60504             for (var c =0;c < 7;c++) {
60505                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60506             }    
60507         }
60508         
60509         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60510         for(i=0;i<cells.length;i++) {
60511             
60512             this.cells.elements[i].dayName = cells[i].dayName ;
60513             this.cells.elements[i].className = cells[i].className;
60514             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60515             this.cells.elements[i].title = cells[i].title ;
60516             this.cells.elements[i].dateValue = cells[i].dateValue ;
60517         }
60518         
60519         
60520         
60521         
60522         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60523         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60524         
60525         ////if(totalRows != 6){
60526             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60527            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60528        // }
60529         
60530         this.fireEvent('monthchange', this, date);
60531         
60532         
60533     },
60534  /**
60535      * Returns the grid's SelectionModel.
60536      * @return {SelectionModel}
60537      */
60538     getSelectionModel : function(){
60539         if(!this.selModel){
60540             this.selModel = new Roo.grid.CellSelectionModel();
60541         }
60542         return this.selModel;
60543     },
60544
60545     load: function() {
60546         this.eventStore.load()
60547         
60548         
60549         
60550     },
60551     
60552     findCell : function(dt) {
60553         dt = dt.clearTime().getTime();
60554         var ret = false;
60555         this.cells.each(function(c){
60556             //Roo.log("check " +c.dateValue + '?=' + dt);
60557             if(c.dateValue == dt){
60558                 ret = c;
60559                 return false;
60560             }
60561             return true;
60562         });
60563         
60564         return ret;
60565     },
60566     
60567     findCells : function(rec) {
60568         var s = rec.data.start_dt.clone().clearTime().getTime();
60569        // Roo.log(s);
60570         var e= rec.data.end_dt.clone().clearTime().getTime();
60571        // Roo.log(e);
60572         var ret = [];
60573         this.cells.each(function(c){
60574              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60575             
60576             if(c.dateValue > e){
60577                 return ;
60578             }
60579             if(c.dateValue < s){
60580                 return ;
60581             }
60582             ret.push(c);
60583         });
60584         
60585         return ret;    
60586     },
60587     
60588     findBestRow: function(cells)
60589     {
60590         var ret = 0;
60591         
60592         for (var i =0 ; i < cells.length;i++) {
60593             ret  = Math.max(cells[i].rows || 0,ret);
60594         }
60595         return ret;
60596         
60597     },
60598     
60599     
60600     addItem : function(rec)
60601     {
60602         // look for vertical location slot in
60603         var cells = this.findCells(rec);
60604         
60605         rec.row = this.findBestRow(cells);
60606         
60607         // work out the location.
60608         
60609         var crow = false;
60610         var rows = [];
60611         for(var i =0; i < cells.length; i++) {
60612             if (!crow) {
60613                 crow = {
60614                     start : cells[i],
60615                     end :  cells[i]
60616                 };
60617                 continue;
60618             }
60619             if (crow.start.getY() == cells[i].getY()) {
60620                 // on same row.
60621                 crow.end = cells[i];
60622                 continue;
60623             }
60624             // different row.
60625             rows.push(crow);
60626             crow = {
60627                 start: cells[i],
60628                 end : cells[i]
60629             };
60630             
60631         }
60632         
60633         rows.push(crow);
60634         rec.els = [];
60635         rec.rows = rows;
60636         rec.cells = cells;
60637         for (var i = 0; i < cells.length;i++) {
60638             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60639             
60640         }
60641         
60642         
60643     },
60644     
60645     clearEvents: function() {
60646         
60647         if (!this.eventStore.getCount()) {
60648             return;
60649         }
60650         // reset number of rows in cells.
60651         Roo.each(this.cells.elements, function(c){
60652             c.rows = 0;
60653         });
60654         
60655         this.eventStore.each(function(e) {
60656             this.clearEvent(e);
60657         },this);
60658         
60659     },
60660     
60661     clearEvent : function(ev)
60662     {
60663         if (ev.els) {
60664             Roo.each(ev.els, function(el) {
60665                 el.un('mouseenter' ,this.onEventEnter, this);
60666                 el.un('mouseleave' ,this.onEventLeave, this);
60667                 el.remove();
60668             },this);
60669             ev.els = [];
60670         }
60671     },
60672     
60673     
60674     renderEvent : function(ev,ctr) {
60675         if (!ctr) {
60676              ctr = this.view.el.select('.fc-event-container',true).first();
60677         }
60678         
60679          
60680         this.clearEvent(ev);
60681             //code
60682        
60683         
60684         
60685         ev.els = [];
60686         var cells = ev.cells;
60687         var rows = ev.rows;
60688         this.fireEvent('eventrender', this, ev);
60689         
60690         for(var i =0; i < rows.length; i++) {
60691             
60692             cls = '';
60693             if (i == 0) {
60694                 cls += ' fc-event-start';
60695             }
60696             if ((i+1) == rows.length) {
60697                 cls += ' fc-event-end';
60698             }
60699             
60700             //Roo.log(ev.data);
60701             // how many rows should it span..
60702             var cg = this.eventTmpl.append(ctr,Roo.apply({
60703                 fccls : cls
60704                 
60705             }, ev.data) , true);
60706             
60707             
60708             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60709             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60710             cg.on('click', this.onEventClick, this, ev);
60711             
60712             ev.els.push(cg);
60713             
60714             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60715             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60716             //Roo.log(cg);
60717              
60718             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60719             cg.setWidth(ebox.right - sbox.x -2);
60720         }
60721     },
60722     
60723     renderEvents: function()
60724     {   
60725         // first make sure there is enough space..
60726         
60727         if (!this.eventTmpl) {
60728             this.eventTmpl = new Roo.Template(
60729                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60730                     '<div class="fc-event-inner">' +
60731                         '<span class="fc-event-time">{time}</span>' +
60732                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60733                     '</div>' +
60734                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60735                 '</div>'
60736             );
60737                 
60738         }
60739                
60740         
60741         
60742         this.cells.each(function(c) {
60743             //Roo.log(c.select('.fc-day-content div',true).first());
60744             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60745         });
60746         
60747         var ctr = this.view.el.select('.fc-event-container',true).first();
60748         
60749         var cls;
60750         this.eventStore.each(function(ev){
60751             
60752             this.renderEvent(ev);
60753              
60754              
60755         }, this);
60756         this.view.layout();
60757         
60758     },
60759     
60760     onEventEnter: function (e, el,event,d) {
60761         this.fireEvent('evententer', this, el, event);
60762     },
60763     
60764     onEventLeave: function (e, el,event,d) {
60765         this.fireEvent('eventleave', this, el, event);
60766     },
60767     
60768     onEventClick: function (e, el,event,d) {
60769         this.fireEvent('eventclick', this, el, event);
60770     },
60771     
60772     onMonthChange: function () {
60773         this.store.load();
60774     },
60775     
60776     onLoad: function () {
60777         
60778         //Roo.log('calendar onload');
60779 //         
60780         if(this.eventStore.getCount() > 0){
60781             
60782            
60783             
60784             this.eventStore.each(function(d){
60785                 
60786                 
60787                 // FIXME..
60788                 var add =   d.data;
60789                 if (typeof(add.end_dt) == 'undefined')  {
60790                     Roo.log("Missing End time in calendar data: ");
60791                     Roo.log(d);
60792                     return;
60793                 }
60794                 if (typeof(add.start_dt) == 'undefined')  {
60795                     Roo.log("Missing Start time in calendar data: ");
60796                     Roo.log(d);
60797                     return;
60798                 }
60799                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60800                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60801                 add.id = add.id || d.id;
60802                 add.title = add.title || '??';
60803                 
60804                 this.addItem(d);
60805                 
60806              
60807             },this);
60808         }
60809         
60810         this.renderEvents();
60811     }
60812     
60813
60814 });
60815 /*
60816  grid : {
60817                 xtype: 'Grid',
60818                 xns: Roo.grid,
60819                 listeners : {
60820                     render : function ()
60821                     {
60822                         _this.grid = this;
60823                         
60824                         if (!this.view.el.hasClass('course-timesheet')) {
60825                             this.view.el.addClass('course-timesheet');
60826                         }
60827                         if (this.tsStyle) {
60828                             this.ds.load({});
60829                             return; 
60830                         }
60831                         Roo.log('width');
60832                         Roo.log(_this.grid.view.el.getWidth());
60833                         
60834                         
60835                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60836                             '.course-timesheet .x-grid-row' : {
60837                                 height: '80px'
60838                             },
60839                             '.x-grid-row td' : {
60840                                 'vertical-align' : 0
60841                             },
60842                             '.course-edit-link' : {
60843                                 'color' : 'blue',
60844                                 'text-overflow' : 'ellipsis',
60845                                 'overflow' : 'hidden',
60846                                 'white-space' : 'nowrap',
60847                                 'cursor' : 'pointer'
60848                             },
60849                             '.sub-link' : {
60850                                 'color' : 'green'
60851                             },
60852                             '.de-act-sup-link' : {
60853                                 'color' : 'purple',
60854                                 'text-decoration' : 'line-through'
60855                             },
60856                             '.de-act-link' : {
60857                                 'color' : 'red',
60858                                 'text-decoration' : 'line-through'
60859                             },
60860                             '.course-timesheet .course-highlight' : {
60861                                 'border-top-style': 'dashed !important',
60862                                 'border-bottom-bottom': 'dashed !important'
60863                             },
60864                             '.course-timesheet .course-item' : {
60865                                 'font-family'   : 'tahoma, arial, helvetica',
60866                                 'font-size'     : '11px',
60867                                 'overflow'      : 'hidden',
60868                                 'padding-left'  : '10px',
60869                                 'padding-right' : '10px',
60870                                 'padding-top' : '10px' 
60871                             }
60872                             
60873                         }, Roo.id());
60874                                 this.ds.load({});
60875                     }
60876                 },
60877                 autoWidth : true,
60878                 monitorWindowResize : false,
60879                 cellrenderer : function(v,x,r)
60880                 {
60881                     return v;
60882                 },
60883                 sm : {
60884                     xtype: 'CellSelectionModel',
60885                     xns: Roo.grid
60886                 },
60887                 dataSource : {
60888                     xtype: 'Store',
60889                     xns: Roo.data,
60890                     listeners : {
60891                         beforeload : function (_self, options)
60892                         {
60893                             options.params = options.params || {};
60894                             options.params._month = _this.monthField.getValue();
60895                             options.params.limit = 9999;
60896                             options.params['sort'] = 'when_dt';    
60897                             options.params['dir'] = 'ASC';    
60898                             this.proxy.loadResponse = this.loadResponse;
60899                             Roo.log("load?");
60900                             //this.addColumns();
60901                         },
60902                         load : function (_self, records, options)
60903                         {
60904                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60905                                 // if you click on the translation.. you can edit it...
60906                                 var el = Roo.get(this);
60907                                 var id = el.dom.getAttribute('data-id');
60908                                 var d = el.dom.getAttribute('data-date');
60909                                 var t = el.dom.getAttribute('data-time');
60910                                 //var id = this.child('span').dom.textContent;
60911                                 
60912                                 //Roo.log(this);
60913                                 Pman.Dialog.CourseCalendar.show({
60914                                     id : id,
60915                                     when_d : d,
60916                                     when_t : t,
60917                                     productitem_active : id ? 1 : 0
60918                                 }, function() {
60919                                     _this.grid.ds.load({});
60920                                 });
60921                            
60922                            });
60923                            
60924                            _this.panel.fireEvent('resize', [ '', '' ]);
60925                         }
60926                     },
60927                     loadResponse : function(o, success, response){
60928                             // this is overridden on before load..
60929                             
60930                             Roo.log("our code?");       
60931                             //Roo.log(success);
60932                             //Roo.log(response)
60933                             delete this.activeRequest;
60934                             if(!success){
60935                                 this.fireEvent("loadexception", this, o, response);
60936                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60937                                 return;
60938                             }
60939                             var result;
60940                             try {
60941                                 result = o.reader.read(response);
60942                             }catch(e){
60943                                 Roo.log("load exception?");
60944                                 this.fireEvent("loadexception", this, o, response, e);
60945                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60946                                 return;
60947                             }
60948                             Roo.log("ready...");        
60949                             // loop through result.records;
60950                             // and set this.tdate[date] = [] << array of records..
60951                             _this.tdata  = {};
60952                             Roo.each(result.records, function(r){
60953                                 //Roo.log(r.data);
60954                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60955                                     _this.tdata[r.data.when_dt.format('j')] = [];
60956                                 }
60957                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60958                             });
60959                             
60960                             //Roo.log(_this.tdata);
60961                             
60962                             result.records = [];
60963                             result.totalRecords = 6;
60964                     
60965                             // let's generate some duumy records for the rows.
60966                             //var st = _this.dateField.getValue();
60967                             
60968                             // work out monday..
60969                             //st = st.add(Date.DAY, -1 * st.format('w'));
60970                             
60971                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60972                             
60973                             var firstOfMonth = date.getFirstDayOfMonth();
60974                             var days = date.getDaysInMonth();
60975                             var d = 1;
60976                             var firstAdded = false;
60977                             for (var i = 0; i < result.totalRecords ; i++) {
60978                                 //var d= st.add(Date.DAY, i);
60979                                 var row = {};
60980                                 var added = 0;
60981                                 for(var w = 0 ; w < 7 ; w++){
60982                                     if(!firstAdded && firstOfMonth != w){
60983                                         continue;
60984                                     }
60985                                     if(d > days){
60986                                         continue;
60987                                     }
60988                                     firstAdded = true;
60989                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60990                                     row['weekday'+w] = String.format(
60991                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60992                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60993                                                     d,
60994                                                     date.format('Y-m-')+dd
60995                                                 );
60996                                     added++;
60997                                     if(typeof(_this.tdata[d]) != 'undefined'){
60998                                         Roo.each(_this.tdata[d], function(r){
60999                                             var is_sub = '';
61000                                             var deactive = '';
61001                                             var id = r.id;
61002                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61003                                             if(r.parent_id*1>0){
61004                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61005                                                 id = r.parent_id;
61006                                             }
61007                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61008                                                 deactive = 'de-act-link';
61009                                             }
61010                                             
61011                                             row['weekday'+w] += String.format(
61012                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61013                                                     id, //0
61014                                                     r.product_id_name, //1
61015                                                     r.when_dt.format('h:ia'), //2
61016                                                     is_sub, //3
61017                                                     deactive, //4
61018                                                     desc // 5
61019                                             );
61020                                         });
61021                                     }
61022                                     d++;
61023                                 }
61024                                 
61025                                 // only do this if something added..
61026                                 if(added > 0){ 
61027                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61028                                 }
61029                                 
61030                                 
61031                                 // push it twice. (second one with an hour..
61032                                 
61033                             }
61034                             //Roo.log(result);
61035                             this.fireEvent("load", this, o, o.request.arg);
61036                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61037                         },
61038                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61039                     proxy : {
61040                         xtype: 'HttpProxy',
61041                         xns: Roo.data,
61042                         method : 'GET',
61043                         url : baseURL + '/Roo/Shop_course.php'
61044                     },
61045                     reader : {
61046                         xtype: 'JsonReader',
61047                         xns: Roo.data,
61048                         id : 'id',
61049                         fields : [
61050                             {
61051                                 'name': 'id',
61052                                 'type': 'int'
61053                             },
61054                             {
61055                                 'name': 'when_dt',
61056                                 'type': 'string'
61057                             },
61058                             {
61059                                 'name': 'end_dt',
61060                                 'type': 'string'
61061                             },
61062                             {
61063                                 'name': 'parent_id',
61064                                 'type': 'int'
61065                             },
61066                             {
61067                                 'name': 'product_id',
61068                                 'type': 'int'
61069                             },
61070                             {
61071                                 'name': 'productitem_id',
61072                                 'type': 'int'
61073                             },
61074                             {
61075                                 'name': 'guid',
61076                                 'type': 'int'
61077                             }
61078                         ]
61079                     }
61080                 },
61081                 toolbar : {
61082                     xtype: 'Toolbar',
61083                     xns: Roo,
61084                     items : [
61085                         {
61086                             xtype: 'Button',
61087                             xns: Roo.Toolbar,
61088                             listeners : {
61089                                 click : function (_self, e)
61090                                 {
61091                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61092                                     sd.setMonth(sd.getMonth()-1);
61093                                     _this.monthField.setValue(sd.format('Y-m-d'));
61094                                     _this.grid.ds.load({});
61095                                 }
61096                             },
61097                             text : "Back"
61098                         },
61099                         {
61100                             xtype: 'Separator',
61101                             xns: Roo.Toolbar
61102                         },
61103                         {
61104                             xtype: 'MonthField',
61105                             xns: Roo.form,
61106                             listeners : {
61107                                 render : function (_self)
61108                                 {
61109                                     _this.monthField = _self;
61110                                    // _this.monthField.set  today
61111                                 },
61112                                 select : function (combo, date)
61113                                 {
61114                                     _this.grid.ds.load({});
61115                                 }
61116                             },
61117                             value : (function() { return new Date(); })()
61118                         },
61119                         {
61120                             xtype: 'Separator',
61121                             xns: Roo.Toolbar
61122                         },
61123                         {
61124                             xtype: 'TextItem',
61125                             xns: Roo.Toolbar,
61126                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61127                         },
61128                         {
61129                             xtype: 'Fill',
61130                             xns: Roo.Toolbar
61131                         },
61132                         {
61133                             xtype: 'Button',
61134                             xns: Roo.Toolbar,
61135                             listeners : {
61136                                 click : function (_self, e)
61137                                 {
61138                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61139                                     sd.setMonth(sd.getMonth()+1);
61140                                     _this.monthField.setValue(sd.format('Y-m-d'));
61141                                     _this.grid.ds.load({});
61142                                 }
61143                             },
61144                             text : "Next"
61145                         }
61146                     ]
61147                 },
61148                  
61149             }
61150         };
61151         
61152         *//*
61153  * Based on:
61154  * Ext JS Library 1.1.1
61155  * Copyright(c) 2006-2007, Ext JS, LLC.
61156  *
61157  * Originally Released Under LGPL - original licence link has changed is not relivant.
61158  *
61159  * Fork - LGPL
61160  * <script type="text/javascript">
61161  */
61162  
61163 /**
61164  * @class Roo.LoadMask
61165  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61166  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61167  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61168  * element's UpdateManager load indicator and will be destroyed after the initial load.
61169  * @constructor
61170  * Create a new LoadMask
61171  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61172  * @param {Object} config The config object
61173  */
61174 Roo.LoadMask = function(el, config){
61175     this.el = Roo.get(el);
61176     Roo.apply(this, config);
61177     if(this.store){
61178         this.store.on('beforeload', this.onBeforeLoad, this);
61179         this.store.on('load', this.onLoad, this);
61180         this.store.on('loadexception', this.onLoadException, this);
61181         this.removeMask = false;
61182     }else{
61183         var um = this.el.getUpdateManager();
61184         um.showLoadIndicator = false; // disable the default indicator
61185         um.on('beforeupdate', this.onBeforeLoad, this);
61186         um.on('update', this.onLoad, this);
61187         um.on('failure', this.onLoad, this);
61188         this.removeMask = true;
61189     }
61190 };
61191
61192 Roo.LoadMask.prototype = {
61193     /**
61194      * @cfg {Boolean} removeMask
61195      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61196      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61197      */
61198     /**
61199      * @cfg {String} msg
61200      * The text to display in a centered loading message box (defaults to 'Loading...')
61201      */
61202     msg : 'Loading...',
61203     /**
61204      * @cfg {String} msgCls
61205      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61206      */
61207     msgCls : 'x-mask-loading',
61208
61209     /**
61210      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61211      * @type Boolean
61212      */
61213     disabled: false,
61214
61215     /**
61216      * Disables the mask to prevent it from being displayed
61217      */
61218     disable : function(){
61219        this.disabled = true;
61220     },
61221
61222     /**
61223      * Enables the mask so that it can be displayed
61224      */
61225     enable : function(){
61226         this.disabled = false;
61227     },
61228     
61229     onLoadException : function()
61230     {
61231         Roo.log(arguments);
61232         
61233         if (typeof(arguments[3]) != 'undefined') {
61234             Roo.MessageBox.alert("Error loading",arguments[3]);
61235         } 
61236         /*
61237         try {
61238             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61239                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61240             }   
61241         } catch(e) {
61242             
61243         }
61244         */
61245     
61246         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61247     },
61248     // private
61249     onLoad : function()
61250     {
61251         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61252     },
61253
61254     // private
61255     onBeforeLoad : function(){
61256         if(!this.disabled){
61257             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61258         }
61259     },
61260
61261     // private
61262     destroy : function(){
61263         if(this.store){
61264             this.store.un('beforeload', this.onBeforeLoad, this);
61265             this.store.un('load', this.onLoad, this);
61266             this.store.un('loadexception', this.onLoadException, this);
61267         }else{
61268             var um = this.el.getUpdateManager();
61269             um.un('beforeupdate', this.onBeforeLoad, this);
61270             um.un('update', this.onLoad, this);
61271             um.un('failure', this.onLoad, this);
61272         }
61273     }
61274 };/*
61275  * Based on:
61276  * Ext JS Library 1.1.1
61277  * Copyright(c) 2006-2007, Ext JS, LLC.
61278  *
61279  * Originally Released Under LGPL - original licence link has changed is not relivant.
61280  *
61281  * Fork - LGPL
61282  * <script type="text/javascript">
61283  */
61284
61285
61286 /**
61287  * @class Roo.XTemplate
61288  * @extends Roo.Template
61289  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61290 <pre><code>
61291 var t = new Roo.XTemplate(
61292         '&lt;select name="{name}"&gt;',
61293                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61294         '&lt;/select&gt;'
61295 );
61296  
61297 // then append, applying the master template values
61298  </code></pre>
61299  *
61300  * Supported features:
61301  *
61302  *  Tags:
61303
61304 <pre><code>
61305       {a_variable} - output encoded.
61306       {a_variable.format:("Y-m-d")} - call a method on the variable
61307       {a_variable:raw} - unencoded output
61308       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61309       {a_variable:this.method_on_template(...)} - call a method on the template object.
61310  
61311 </code></pre>
61312  *  The tpl tag:
61313 <pre><code>
61314         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61315         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61316         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61317         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61318   
61319         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61320         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61321 </code></pre>
61322  *      
61323  */
61324 Roo.XTemplate = function()
61325 {
61326     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61327     if (this.html) {
61328         this.compile();
61329     }
61330 };
61331
61332
61333 Roo.extend(Roo.XTemplate, Roo.Template, {
61334
61335     /**
61336      * The various sub templates
61337      */
61338     tpls : false,
61339     /**
61340      *
61341      * basic tag replacing syntax
61342      * WORD:WORD()
61343      *
61344      * // you can fake an object call by doing this
61345      *  x.t:(test,tesT) 
61346      * 
61347      */
61348     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61349
61350     /**
61351      * compile the template
61352      *
61353      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61354      *
61355      */
61356     compile: function()
61357     {
61358         var s = this.html;
61359      
61360         s = ['<tpl>', s, '</tpl>'].join('');
61361     
61362         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61363             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61364             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61365             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61366             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61367             m,
61368             id     = 0,
61369             tpls   = [];
61370     
61371         while(true == !!(m = s.match(re))){
61372             var forMatch   = m[0].match(nameRe),
61373                 ifMatch   = m[0].match(ifRe),
61374                 execMatch   = m[0].match(execRe),
61375                 namedMatch   = m[0].match(namedRe),
61376                 
61377                 exp  = null, 
61378                 fn   = null,
61379                 exec = null,
61380                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61381                 
61382             if (ifMatch) {
61383                 // if - puts fn into test..
61384                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61385                 if(exp){
61386                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61387                 }
61388             }
61389             
61390             if (execMatch) {
61391                 // exec - calls a function... returns empty if true is  returned.
61392                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61393                 if(exp){
61394                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61395                 }
61396             }
61397             
61398             
61399             if (name) {
61400                 // for = 
61401                 switch(name){
61402                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61403                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61404                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61405                 }
61406             }
61407             var uid = namedMatch ? namedMatch[1] : id;
61408             
61409             
61410             tpls.push({
61411                 id:     namedMatch ? namedMatch[1] : id,
61412                 target: name,
61413                 exec:   exec,
61414                 test:   fn,
61415                 body:   m[1] || ''
61416             });
61417             if (namedMatch) {
61418                 s = s.replace(m[0], '');
61419             } else { 
61420                 s = s.replace(m[0], '{xtpl'+ id + '}');
61421             }
61422             ++id;
61423         }
61424         this.tpls = [];
61425         for(var i = tpls.length-1; i >= 0; --i){
61426             this.compileTpl(tpls[i]);
61427             this.tpls[tpls[i].id] = tpls[i];
61428         }
61429         this.master = tpls[tpls.length-1];
61430         return this;
61431     },
61432     /**
61433      * same as applyTemplate, except it's done to one of the subTemplates
61434      * when using named templates, you can do:
61435      *
61436      * var str = pl.applySubTemplate('your-name', values);
61437      *
61438      * 
61439      * @param {Number} id of the template
61440      * @param {Object} values to apply to template
61441      * @param {Object} parent (normaly the instance of this object)
61442      */
61443     applySubTemplate : function(id, values, parent)
61444     {
61445         
61446         
61447         var t = this.tpls[id];
61448         
61449         
61450         try { 
61451             if(t.test && !t.test.call(this, values, parent)){
61452                 return '';
61453             }
61454         } catch(e) {
61455             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61456             Roo.log(e.toString());
61457             Roo.log(t.test);
61458             return ''
61459         }
61460         try { 
61461             
61462             if(t.exec && t.exec.call(this, values, parent)){
61463                 return '';
61464             }
61465         } catch(e) {
61466             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61467             Roo.log(e.toString());
61468             Roo.log(t.exec);
61469             return ''
61470         }
61471         try {
61472             var vs = t.target ? t.target.call(this, values, parent) : values;
61473             parent = t.target ? values : parent;
61474             if(t.target && vs instanceof Array){
61475                 var buf = [];
61476                 for(var i = 0, len = vs.length; i < len; i++){
61477                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61478                 }
61479                 return buf.join('');
61480             }
61481             return t.compiled.call(this, vs, parent);
61482         } catch (e) {
61483             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61484             Roo.log(e.toString());
61485             Roo.log(t.compiled);
61486             return '';
61487         }
61488     },
61489
61490     compileTpl : function(tpl)
61491     {
61492         var fm = Roo.util.Format;
61493         var useF = this.disableFormats !== true;
61494         var sep = Roo.isGecko ? "+" : ",";
61495         var undef = function(str) {
61496             Roo.log("Property not found :"  + str);
61497             return '';
61498         };
61499         
61500         var fn = function(m, name, format, args)
61501         {
61502             //Roo.log(arguments);
61503             args = args ? args.replace(/\\'/g,"'") : args;
61504             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61505             if (typeof(format) == 'undefined') {
61506                 format= 'htmlEncode';
61507             }
61508             if (format == 'raw' ) {
61509                 format = false;
61510             }
61511             
61512             if(name.substr(0, 4) == 'xtpl'){
61513                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61514             }
61515             
61516             // build an array of options to determine if value is undefined..
61517             
61518             // basically get 'xxxx.yyyy' then do
61519             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61520             //    (function () { Roo.log("Property not found"); return ''; })() :
61521             //    ......
61522             
61523             var udef_ar = [];
61524             var lookfor = '';
61525             Roo.each(name.split('.'), function(st) {
61526                 lookfor += (lookfor.length ? '.': '') + st;
61527                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61528             });
61529             
61530             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61531             
61532             
61533             if(format && useF){
61534                 
61535                 args = args ? ',' + args : "";
61536                  
61537                 if(format.substr(0, 5) != "this."){
61538                     format = "fm." + format + '(';
61539                 }else{
61540                     format = 'this.call("'+ format.substr(5) + '", ';
61541                     args = ", values";
61542                 }
61543                 
61544                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61545             }
61546              
61547             if (args.length) {
61548                 // called with xxyx.yuu:(test,test)
61549                 // change to ()
61550                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61551             }
61552             // raw.. - :raw modifier..
61553             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61554             
61555         };
61556         var body;
61557         // branched to use + in gecko and [].join() in others
61558         if(Roo.isGecko){
61559             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61560                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61561                     "';};};";
61562         }else{
61563             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61564             body.push(tpl.body.replace(/(\r\n|\n)/g,
61565                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61566             body.push("'].join('');};};");
61567             body = body.join('');
61568         }
61569         
61570         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61571        
61572         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61573         eval(body);
61574         
61575         return this;
61576     },
61577
61578     applyTemplate : function(values){
61579         return this.master.compiled.call(this, values, {});
61580         //var s = this.subs;
61581     },
61582
61583     apply : function(){
61584         return this.applyTemplate.apply(this, arguments);
61585     }
61586
61587  });
61588
61589 Roo.XTemplate.from = function(el){
61590     el = Roo.getDom(el);
61591     return new Roo.XTemplate(el.value || el.innerHTML);
61592 };