commit
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             console.log(s);
346             
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
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         try {
4671            
4672             if(this.compiled){
4673                 return this.compiled(values);
4674             }
4675             var useF = this.disableFormats !== true;
4676             var fm = Roo.util.Format, tpl = this;
4677             var fn = function(m, name, format, args){
4678                 if(format && useF){
4679                     if(format.substr(0, 5) == "this."){
4680                         return tpl.call(format.substr(5), values[name], values);
4681                     }else{
4682                         if(args){
4683                             // quoted values are required for strings in compiled templates, 
4684                             // but for non compiled we need to strip them
4685                             // quoted reversed for jsmin
4686                             var re = /^\s*['"](.*)["']\s*$/;
4687                             args = args.split(',');
4688                             for(var i = 0, len = args.length; i < len; i++){
4689                                 args[i] = args[i].replace(re, "$1");
4690                             }
4691                             args = [values[name]].concat(args);
4692                         }else{
4693                             args = [values[name]];
4694                         }
4695                         return fm[format].apply(fm, args);
4696                     }
4697                 }else{
4698                     return values[name] !== undefined ? values[name] : "";
4699                 }
4700             };
4701             return this.html.replace(this.re, fn);
4702         } catch (e) {
4703             Roo.log(e);
4704             throw e;
4705         }
4706          
4707     },
4708     
4709     loading : false,
4710       
4711     load : function ()
4712     {
4713          
4714         if (this.loading) {
4715             return;
4716         }
4717         var _t = this;
4718         
4719         this.loading = true;
4720         this.compiled = false;
4721         
4722         var cx = new Roo.data.Connection();
4723         cx.request({
4724             url : this.url,
4725             method : 'GET',
4726             success : function (response) {
4727                 _t.loading = false;
4728                 _t.html = response.responseText;
4729                 _t.url = false;
4730                 _t.compile();
4731              },
4732             failure : function(response) {
4733                 Roo.log("Template failed to load from " + _t.url);
4734                 _t.loading = false;
4735             }
4736         });
4737     },
4738
4739     /**
4740      * Sets the HTML used as the template and optionally compiles it.
4741      * @param {String} html
4742      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4743      * @return {Roo.Template} this
4744      */
4745     set : function(html, compile){
4746         this.html = html;
4747         this.compiled = null;
4748         if(compile){
4749             this.compile();
4750         }
4751         return this;
4752     },
4753     
4754     /**
4755      * True to disable format functions (defaults to false)
4756      * @type Boolean
4757      */
4758     disableFormats : false,
4759     
4760     /**
4761     * The regular expression used to match template variables 
4762     * @type RegExp
4763     * @property 
4764     */
4765     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4766     
4767     /**
4768      * Compiles the template into an internal function, eliminating the RegEx overhead.
4769      * @return {Roo.Template} this
4770      */
4771     compile : function(){
4772         var fm = Roo.util.Format;
4773         var useF = this.disableFormats !== true;
4774         var sep = Roo.isGecko ? "+" : ",";
4775         var fn = function(m, name, format, args){
4776             if(format && useF){
4777                 args = args ? ',' + args : "";
4778                 if(format.substr(0, 5) != "this."){
4779                     format = "fm." + format + '(';
4780                 }else{
4781                     format = 'this.call("'+ format.substr(5) + '", ';
4782                     args = ", values";
4783                 }
4784             }else{
4785                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4786             }
4787             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4788         };
4789         var body;
4790         // branched to use + in gecko and [].join() in others
4791         if(Roo.isGecko){
4792             body = "this.compiled = function(values){ return '" +
4793                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4794                     "';};";
4795         }else{
4796             body = ["this.compiled = function(values){ return ['"];
4797             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4798             body.push("'].join('');};");
4799             body = body.join('');
4800         }
4801         /**
4802          * eval:var:values
4803          * eval:var:fm
4804          */
4805         eval(body);
4806         return this;
4807     },
4808     
4809     // private function used to call members
4810     call : function(fnName, value, allValues){
4811         return this[fnName](value, allValues);
4812     },
4813     
4814     /**
4815      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4816      * @param {String/HTMLElement/Roo.Element} el The context element
4817      * @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'})
4818      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4819      * @return {HTMLElement/Roo.Element} The new node or Element
4820      */
4821     insertFirst: function(el, values, returnElement){
4822         return this.doInsert('afterBegin', el, values, returnElement);
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and inserts the new node(s) before el.
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @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'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     insertBefore: function(el, values, returnElement){
4833         return this.doInsert('beforeBegin', el, values, returnElement);
4834     },
4835
4836     /**
4837      * Applies the supplied values to the template and inserts the new node(s) after el.
4838      * @param {String/HTMLElement/Roo.Element} el The context element
4839      * @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'})
4840      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4841      * @return {HTMLElement/Roo.Element} The new node or Element
4842      */
4843     insertAfter : function(el, values, returnElement){
4844         return this.doInsert('afterEnd', el, values, returnElement);
4845     },
4846     
4847     /**
4848      * Applies the supplied values to the template and appends the new node(s) to el.
4849      * @param {String/HTMLElement/Roo.Element} el The context element
4850      * @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'})
4851      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4852      * @return {HTMLElement/Roo.Element} The new node or Element
4853      */
4854     append : function(el, values, returnElement){
4855         return this.doInsert('beforeEnd', el, values, returnElement);
4856     },
4857
4858     doInsert : function(where, el, values, returnEl){
4859         el = Roo.getDom(el);
4860         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4861         return returnEl ? Roo.get(newNode, true) : newNode;
4862     },
4863
4864     /**
4865      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4866      * @param {String/HTMLElement/Roo.Element} el The context element
4867      * @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'})
4868      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4869      * @return {HTMLElement/Roo.Element} The new node or Element
4870      */
4871     overwrite : function(el, values, returnElement){
4872         el = Roo.getDom(el);
4873         el.innerHTML = this.applyTemplate(values);
4874         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4875     }
4876 };
4877 /**
4878  * Alias for {@link #applyTemplate}
4879  * @method
4880  */
4881 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4882
4883 // backwards compat
4884 Roo.DomHelper.Template = Roo.Template;
4885
4886 /**
4887  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4888  * @param {String/HTMLElement} el A DOM element or its id
4889  * @returns {Roo.Template} The created template
4890  * @static
4891  */
4892 Roo.Template.from = function(el){
4893     el = Roo.getDom(el);
4894     return new Roo.Template(el.value || el.innerHTML);
4895 };/*
4896  * Based on:
4897  * Ext JS Library 1.1.1
4898  * Copyright(c) 2006-2007, Ext JS, LLC.
4899  *
4900  * Originally Released Under LGPL - original licence link has changed is not relivant.
4901  *
4902  * Fork - LGPL
4903  * <script type="text/javascript">
4904  */
4905  
4906
4907 /*
4908  * This is code is also distributed under MIT license for use
4909  * with jQuery and prototype JavaScript libraries.
4910  */
4911 /**
4912  * @class Roo.DomQuery
4913 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).
4914 <p>
4915 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>
4916
4917 <p>
4918 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.
4919 </p>
4920 <h4>Element Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>*</b> any element</li>
4923     <li> <b>E</b> an element with the tag E</li>
4924     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4925     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4926     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4927     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4928 </ul>
4929 <h4>Attribute Selectors:</h4>
4930 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4931 <ul class="list">
4932     <li> <b>E[foo]</b> has an attribute "foo"</li>
4933     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4934     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4935     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4936     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4937     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4938     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4939 </ul>
4940 <h4>Pseudo Classes:</h4>
4941 <ul class="list">
4942     <li> <b>E:first-child</b> E is the first child of its parent</li>
4943     <li> <b>E:last-child</b> E is the last child of its parent</li>
4944     <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>
4945     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4946     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4947     <li> <b>E:only-child</b> E is the only child of its parent</li>
4948     <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>
4949     <li> <b>E:first</b> the first E in the resultset</li>
4950     <li> <b>E:last</b> the last E in the resultset</li>
4951     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4952     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4953     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4954     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4955     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4956     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4957     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4958     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4959     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4960 </ul>
4961 <h4>CSS Value Selectors:</h4>
4962 <ul class="list">
4963     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4964     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4965     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4966     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4967     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4968     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4969 </ul>
4970  * @singleton
4971  */
4972 Roo.DomQuery = function(){
4973     var cache = {}, simpleCache = {}, valueCache = {};
4974     var nonSpace = /\S/;
4975     var trimRe = /^\s+|\s+$/g;
4976     var tplRe = /\{(\d+)\}/g;
4977     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4978     var tagTokenRe = /^(#)?([\w-\*]+)/;
4979     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4980
4981     function child(p, index){
4982         var i = 0;
4983         var n = p.firstChild;
4984         while(n){
4985             if(n.nodeType == 1){
4986                if(++i == index){
4987                    return n;
4988                }
4989             }
4990             n = n.nextSibling;
4991         }
4992         return null;
4993     };
4994
4995     function next(n){
4996         while((n = n.nextSibling) && n.nodeType != 1);
4997         return n;
4998     };
4999
5000     function prev(n){
5001         while((n = n.previousSibling) && n.nodeType != 1);
5002         return n;
5003     };
5004
5005     function children(d){
5006         var n = d.firstChild, ni = -1;
5007             while(n){
5008                 var nx = n.nextSibling;
5009                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5010                     d.removeChild(n);
5011                 }else{
5012                     n.nodeIndex = ++ni;
5013                 }
5014                 n = nx;
5015             }
5016             return this;
5017         };
5018
5019     function byClassName(c, a, v){
5020         if(!v){
5021             return c;
5022         }
5023         var r = [], ri = -1, cn;
5024         for(var i = 0, ci; ci = c[i]; i++){
5025             if((' '+ci.className+' ').indexOf(v) != -1){
5026                 r[++ri] = ci;
5027             }
5028         }
5029         return r;
5030     };
5031
5032     function attrValue(n, attr){
5033         if(!n.tagName && typeof n.length != "undefined"){
5034             n = n[0];
5035         }
5036         if(!n){
5037             return null;
5038         }
5039         if(attr == "for"){
5040             return n.htmlFor;
5041         }
5042         if(attr == "class" || attr == "className"){
5043             return n.className;
5044         }
5045         return n.getAttribute(attr) || n[attr];
5046
5047     };
5048
5049     function getNodes(ns, mode, tagName){
5050         var result = [], ri = -1, cs;
5051         if(!ns){
5052             return result;
5053         }
5054         tagName = tagName || "*";
5055         if(typeof ns.getElementsByTagName != "undefined"){
5056             ns = [ns];
5057         }
5058         if(!mode){
5059             for(var i = 0, ni; ni = ns[i]; i++){
5060                 cs = ni.getElementsByTagName(tagName);
5061                 for(var j = 0, ci; ci = cs[j]; j++){
5062                     result[++ri] = ci;
5063                 }
5064             }
5065         }else if(mode == "/" || mode == ">"){
5066             var utag = tagName.toUpperCase();
5067             for(var i = 0, ni, cn; ni = ns[i]; i++){
5068                 cn = ni.children || ni.childNodes;
5069                 for(var j = 0, cj; cj = cn[j]; j++){
5070                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5071                         result[++ri] = cj;
5072                     }
5073                 }
5074             }
5075         }else if(mode == "+"){
5076             var utag = tagName.toUpperCase();
5077             for(var i = 0, n; n = ns[i]; i++){
5078                 while((n = n.nextSibling) && n.nodeType != 1);
5079                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5080                     result[++ri] = n;
5081                 }
5082             }
5083         }else if(mode == "~"){
5084             for(var i = 0, n; n = ns[i]; i++){
5085                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5086                 if(n){
5087                     result[++ri] = n;
5088                 }
5089             }
5090         }
5091         return result;
5092     };
5093
5094     function concat(a, b){
5095         if(b.slice){
5096             return a.concat(b);
5097         }
5098         for(var i = 0, l = b.length; i < l; i++){
5099             a[a.length] = b[i];
5100         }
5101         return a;
5102     }
5103
5104     function byTag(cs, tagName){
5105         if(cs.tagName || cs == document){
5106             cs = [cs];
5107         }
5108         if(!tagName){
5109             return cs;
5110         }
5111         var r = [], ri = -1;
5112         tagName = tagName.toLowerCase();
5113         for(var i = 0, ci; ci = cs[i]; i++){
5114             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byId(cs, attr, id){
5122         if(cs.tagName || cs == document){
5123             cs = [cs];
5124         }
5125         if(!id){
5126             return cs;
5127         }
5128         var r = [], ri = -1;
5129         for(var i = 0,ci; ci = cs[i]; i++){
5130             if(ci && ci.id == id){
5131                 r[++ri] = ci;
5132                 return r;
5133             }
5134         }
5135         return r;
5136     };
5137
5138     function byAttribute(cs, attr, value, op, custom){
5139         var r = [], ri = -1, st = custom=="{";
5140         var f = Roo.DomQuery.operators[op];
5141         for(var i = 0, ci; ci = cs[i]; i++){
5142             var a;
5143             if(st){
5144                 a = Roo.DomQuery.getStyle(ci, attr);
5145             }
5146             else if(attr == "class" || attr == "className"){
5147                 a = ci.className;
5148             }else if(attr == "for"){
5149                 a = ci.htmlFor;
5150             }else if(attr == "href"){
5151                 a = ci.getAttribute("href", 2);
5152             }else{
5153                 a = ci.getAttribute(attr);
5154             }
5155             if((f && f(a, value)) || (!f && a)){
5156                 r[++ri] = ci;
5157             }
5158         }
5159         return r;
5160     };
5161
5162     function byPseudo(cs, name, value){
5163         return Roo.DomQuery.pseudos[name](cs, value);
5164     };
5165
5166     // This is for IE MSXML which does not support expandos.
5167     // IE runs the same speed using setAttribute, however FF slows way down
5168     // and Safari completely fails so they need to continue to use expandos.
5169     var isIE = window.ActiveXObject ? true : false;
5170
5171     // this eval is stop the compressor from
5172     // renaming the variable to something shorter
5173     
5174     /** eval:var:batch */
5175     var batch = 30803; 
5176
5177     var key = 30803;
5178
5179     function nodupIEXml(cs){
5180         var d = ++key;
5181         cs[0].setAttribute("_nodup", d);
5182         var r = [cs[0]];
5183         for(var i = 1, len = cs.length; i < len; i++){
5184             var c = cs[i];
5185             if(!c.getAttribute("_nodup") != d){
5186                 c.setAttribute("_nodup", d);
5187                 r[r.length] = c;
5188             }
5189         }
5190         for(var i = 0, len = cs.length; i < len; i++){
5191             cs[i].removeAttribute("_nodup");
5192         }
5193         return r;
5194     }
5195
5196     function nodup(cs){
5197         if(!cs){
5198             return [];
5199         }
5200         var len = cs.length, c, i, r = cs, cj, ri = -1;
5201         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5202             return cs;
5203         }
5204         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5205             return nodupIEXml(cs);
5206         }
5207         var d = ++key;
5208         cs[0]._nodup = d;
5209         for(i = 1; c = cs[i]; i++){
5210             if(c._nodup != d){
5211                 c._nodup = d;
5212             }else{
5213                 r = [];
5214                 for(var j = 0; j < i; j++){
5215                     r[++ri] = cs[j];
5216                 }
5217                 for(j = i+1; cj = cs[j]; j++){
5218                     if(cj._nodup != d){
5219                         cj._nodup = d;
5220                         r[++ri] = cj;
5221                     }
5222                 }
5223                 return r;
5224             }
5225         }
5226         return r;
5227     }
5228
5229     function quickDiffIEXml(c1, c2){
5230         var d = ++key;
5231         for(var i = 0, len = c1.length; i < len; i++){
5232             c1[i].setAttribute("_qdiff", d);
5233         }
5234         var r = [];
5235         for(var i = 0, len = c2.length; i < len; i++){
5236             if(c2[i].getAttribute("_qdiff") != d){
5237                 r[r.length] = c2[i];
5238             }
5239         }
5240         for(var i = 0, len = c1.length; i < len; i++){
5241            c1[i].removeAttribute("_qdiff");
5242         }
5243         return r;
5244     }
5245
5246     function quickDiff(c1, c2){
5247         var len1 = c1.length;
5248         if(!len1){
5249             return c2;
5250         }
5251         if(isIE && c1[0].selectSingleNode){
5252             return quickDiffIEXml(c1, c2);
5253         }
5254         var d = ++key;
5255         for(var i = 0; i < len1; i++){
5256             c1[i]._qdiff = d;
5257         }
5258         var r = [];
5259         for(var i = 0, len = c2.length; i < len; i++){
5260             if(c2[i]._qdiff != d){
5261                 r[r.length] = c2[i];
5262             }
5263         }
5264         return r;
5265     }
5266
5267     function quickId(ns, mode, root, id){
5268         if(ns == root){
5269            var d = root.ownerDocument || root;
5270            return d.getElementById(id);
5271         }
5272         ns = getNodes(ns, mode, "*");
5273         return byId(ns, null, id);
5274     }
5275
5276     return {
5277         getStyle : function(el, name){
5278             return Roo.fly(el).getStyle(name);
5279         },
5280         /**
5281          * Compiles a selector/xpath query into a reusable function. The returned function
5282          * takes one parameter "root" (optional), which is the context node from where the query should start.
5283          * @param {String} selector The selector/xpath query
5284          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5285          * @return {Function}
5286          */
5287         compile : function(path, type){
5288             type = type || "select";
5289             
5290             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5291             var q = path, mode, lq;
5292             var tk = Roo.DomQuery.matchers;
5293             var tklen = tk.length;
5294             var mm;
5295
5296             // accept leading mode switch
5297             var lmode = q.match(modeRe);
5298             if(lmode && lmode[1]){
5299                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5300                 q = q.replace(lmode[1], "");
5301             }
5302             // strip leading slashes
5303             while(path.substr(0, 1)=="/"){
5304                 path = path.substr(1);
5305             }
5306
5307             while(q && lq != q){
5308                 lq = q;
5309                 var tm = q.match(tagTokenRe);
5310                 if(type == "select"){
5311                     if(tm){
5312                         if(tm[1] == "#"){
5313                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5314                         }else{
5315                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5316                         }
5317                         q = q.replace(tm[0], "");
5318                     }else if(q.substr(0, 1) != '@'){
5319                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5320                     }
5321                 }else{
5322                     if(tm){
5323                         if(tm[1] == "#"){
5324                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5325                         }else{
5326                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5327                         }
5328                         q = q.replace(tm[0], "");
5329                     }
5330                 }
5331                 while(!(mm = q.match(modeRe))){
5332                     var matched = false;
5333                     for(var j = 0; j < tklen; j++){
5334                         var t = tk[j];
5335                         var m = q.match(t.re);
5336                         if(m){
5337                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5338                                                     return m[i];
5339                                                 });
5340                             q = q.replace(m[0], "");
5341                             matched = true;
5342                             break;
5343                         }
5344                     }
5345                     // prevent infinite loop on bad selector
5346                     if(!matched){
5347                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5348                     }
5349                 }
5350                 if(mm[1]){
5351                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5352                     q = q.replace(mm[1], "");
5353                 }
5354             }
5355             fn[fn.length] = "return nodup(n);\n}";
5356             
5357              /** 
5358               * list of variables that need from compression as they are used by eval.
5359              *  eval:var:batch 
5360              *  eval:var:nodup
5361              *  eval:var:byTag
5362              *  eval:var:ById
5363              *  eval:var:getNodes
5364              *  eval:var:quickId
5365              *  eval:var:mode
5366              *  eval:var:root
5367              *  eval:var:n
5368              *  eval:var:byClassName
5369              *  eval:var:byPseudo
5370              *  eval:var:byAttribute
5371              *  eval:var:attrValue
5372              * 
5373              **/ 
5374             eval(fn.join(""));
5375             return f;
5376         },
5377
5378         /**
5379          * Selects a group of elements.
5380          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5381          * @param {Node} root (optional) The start of the query (defaults to document).
5382          * @return {Array}
5383          */
5384         select : function(path, root, type){
5385             if(!root || root == document){
5386                 root = document;
5387             }
5388             if(typeof root == "string"){
5389                 root = document.getElementById(root);
5390             }
5391             var paths = path.split(",");
5392             var results = [];
5393             for(var i = 0, len = paths.length; i < len; i++){
5394                 var p = paths[i].replace(trimRe, "");
5395                 if(!cache[p]){
5396                     cache[p] = Roo.DomQuery.compile(p);
5397                     if(!cache[p]){
5398                         throw p + " is not a valid selector";
5399                     }
5400                 }
5401                 var result = cache[p](root);
5402                 if(result && result != document){
5403                     results = results.concat(result);
5404                 }
5405             }
5406             if(paths.length > 1){
5407                 return nodup(results);
5408             }
5409             return results;
5410         },
5411
5412         /**
5413          * Selects a single element.
5414          * @param {String} selector The selector/xpath query
5415          * @param {Node} root (optional) The start of the query (defaults to document).
5416          * @return {Element}
5417          */
5418         selectNode : function(path, root){
5419             return Roo.DomQuery.select(path, root)[0];
5420         },
5421
5422         /**
5423          * Selects the value of a node, optionally replacing null with the defaultValue.
5424          * @param {String} selector The selector/xpath query
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @param {String} defaultValue
5427          */
5428         selectValue : function(path, root, defaultValue){
5429             path = path.replace(trimRe, "");
5430             if(!valueCache[path]){
5431                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5432             }
5433             var n = valueCache[path](root);
5434             n = n[0] ? n[0] : n;
5435             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5436             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5437         },
5438
5439         /**
5440          * Selects the value of a node, parsing integers and floats.
5441          * @param {String} selector The selector/xpath query
5442          * @param {Node} root (optional) The start of the query (defaults to document).
5443          * @param {Number} defaultValue
5444          * @return {Number}
5445          */
5446         selectNumber : function(path, root, defaultValue){
5447             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5448             return parseFloat(v);
5449         },
5450
5451         /**
5452          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5453          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5454          * @param {String} selector The simple selector to test
5455          * @return {Boolean}
5456          */
5457         is : function(el, ss){
5458             if(typeof el == "string"){
5459                 el = document.getElementById(el);
5460             }
5461             var isArray = (el instanceof Array);
5462             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5463             return isArray ? (result.length == el.length) : (result.length > 0);
5464         },
5465
5466         /**
5467          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5468          * @param {Array} el An array of elements to filter
5469          * @param {String} selector The simple selector to test
5470          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5471          * the selector instead of the ones that match
5472          * @return {Array}
5473          */
5474         filter : function(els, ss, nonMatches){
5475             ss = ss.replace(trimRe, "");
5476             if(!simpleCache[ss]){
5477                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5478             }
5479             var result = simpleCache[ss](els);
5480             return nonMatches ? quickDiff(result, els) : result;
5481         },
5482
5483         /**
5484          * Collection of matching regular expressions and code snippets.
5485          */
5486         matchers : [{
5487                 re: /^\.([\w-]+)/,
5488                 select: 'n = byClassName(n, null, " {1} ");'
5489             }, {
5490                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5491                 select: 'n = byPseudo(n, "{1}", "{2}");'
5492             },{
5493                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5494                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5495             }, {
5496                 re: /^#([\w-]+)/,
5497                 select: 'n = byId(n, null, "{1}");'
5498             },{
5499                 re: /^@([\w-]+)/,
5500                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5501             }
5502         ],
5503
5504         /**
5505          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5506          * 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;.
5507          */
5508         operators : {
5509             "=" : function(a, v){
5510                 return a == v;
5511             },
5512             "!=" : function(a, v){
5513                 return a != v;
5514             },
5515             "^=" : function(a, v){
5516                 return a && a.substr(0, v.length) == v;
5517             },
5518             "$=" : function(a, v){
5519                 return a && a.substr(a.length-v.length) == v;
5520             },
5521             "*=" : function(a, v){
5522                 return a && a.indexOf(v) !== -1;
5523             },
5524             "%=" : function(a, v){
5525                 return (a % v) == 0;
5526             },
5527             "|=" : function(a, v){
5528                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5529             },
5530             "~=" : function(a, v){
5531                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5532             }
5533         },
5534
5535         /**
5536          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5537          * and the argument (if any) supplied in the selector.
5538          */
5539         pseudos : {
5540             "first-child" : function(c){
5541                 var r = [], ri = -1, n;
5542                 for(var i = 0, ci; ci = n = c[i]; i++){
5543                     while((n = n.previousSibling) && n.nodeType != 1);
5544                     if(!n){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "last-child" : function(c){
5552                 var r = [], ri = -1, n;
5553                 for(var i = 0, ci; ci = n = c[i]; i++){
5554                     while((n = n.nextSibling) && n.nodeType != 1);
5555                     if(!n){
5556                         r[++ri] = ci;
5557                     }
5558                 }
5559                 return r;
5560             },
5561
5562             "nth-child" : function(c, a) {
5563                 var r = [], ri = -1;
5564                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5565                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5566                 for(var i = 0, n; n = c[i]; i++){
5567                     var pn = n.parentNode;
5568                     if (batch != pn._batch) {
5569                         var j = 0;
5570                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5571                             if(cn.nodeType == 1){
5572                                cn.nodeIndex = ++j;
5573                             }
5574                         }
5575                         pn._batch = batch;
5576                     }
5577                     if (f == 1) {
5578                         if (l == 0 || n.nodeIndex == l){
5579                             r[++ri] = n;
5580                         }
5581                     } else if ((n.nodeIndex + l) % f == 0){
5582                         r[++ri] = n;
5583                     }
5584                 }
5585
5586                 return r;
5587             },
5588
5589             "only-child" : function(c){
5590                 var r = [], ri = -1;;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     if(!prev(ci) && !next(ci)){
5593                         r[++ri] = ci;
5594                     }
5595                 }
5596                 return r;
5597             },
5598
5599             "empty" : function(c){
5600                 var r = [], ri = -1;
5601                 for(var i = 0, ci; ci = c[i]; i++){
5602                     var cns = ci.childNodes, j = 0, cn, empty = true;
5603                     while(cn = cns[j]){
5604                         ++j;
5605                         if(cn.nodeType == 1 || cn.nodeType == 3){
5606                             empty = false;
5607                             break;
5608                         }
5609                     }
5610                     if(empty){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "contains" : function(c, v){
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5621                         r[++ri] = ci;
5622                     }
5623                 }
5624                 return r;
5625             },
5626
5627             "nodeValue" : function(c, v){
5628                 var r = [], ri = -1;
5629                 for(var i = 0, ci; ci = c[i]; i++){
5630                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5631                         r[++ri] = ci;
5632                     }
5633                 }
5634                 return r;
5635             },
5636
5637             "checked" : function(c){
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     if(ci.checked == true){
5641                         r[++ri] = ci;
5642                     }
5643                 }
5644                 return r;
5645             },
5646
5647             "not" : function(c, ss){
5648                 return Roo.DomQuery.filter(c, ss, true);
5649             },
5650
5651             "odd" : function(c){
5652                 return this["nth-child"](c, "odd");
5653             },
5654
5655             "even" : function(c){
5656                 return this["nth-child"](c, "even");
5657             },
5658
5659             "nth" : function(c, a){
5660                 return c[a-1] || [];
5661             },
5662
5663             "first" : function(c){
5664                 return c[0] || [];
5665             },
5666
5667             "last" : function(c){
5668                 return c[c.length-1] || [];
5669             },
5670
5671             "has" : function(c, ss){
5672                 var s = Roo.DomQuery.select;
5673                 var r = [], ri = -1;
5674                 for(var i = 0, ci; ci = c[i]; i++){
5675                     if(s(ss, ci).length > 0){
5676                         r[++ri] = ci;
5677                     }
5678                 }
5679                 return r;
5680             },
5681
5682             "next" : function(c, ss){
5683                 var is = Roo.DomQuery.is;
5684                 var r = [], ri = -1;
5685                 for(var i = 0, ci; ci = c[i]; i++){
5686                     var n = next(ci);
5687                     if(n && is(n, ss)){
5688                         r[++ri] = ci;
5689                     }
5690                 }
5691                 return r;
5692             },
5693
5694             "prev" : function(c, ss){
5695                 var is = Roo.DomQuery.is;
5696                 var r = [], ri = -1;
5697                 for(var i = 0, ci; ci = c[i]; i++){
5698                     var n = prev(ci);
5699                     if(n && is(n, ss)){
5700                         r[++ri] = ci;
5701                     }
5702                 }
5703                 return r;
5704             }
5705         }
5706     };
5707 }();
5708
5709 /**
5710  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5711  * @param {String} path The selector/xpath query
5712  * @param {Node} root (optional) The start of the query (defaults to document).
5713  * @return {Array}
5714  * @member Roo
5715  * @method query
5716  */
5717 Roo.query = Roo.DomQuery.select;
5718 /*
5719  * Based on:
5720  * Ext JS Library 1.1.1
5721  * Copyright(c) 2006-2007, Ext JS, LLC.
5722  *
5723  * Originally Released Under LGPL - original licence link has changed is not relivant.
5724  *
5725  * Fork - LGPL
5726  * <script type="text/javascript">
5727  */
5728
5729 /**
5730  * @class Roo.util.Observable
5731  * Base class that provides a common interface for publishing events. Subclasses are expected to
5732  * to have a property "events" with all the events defined.<br>
5733  * For example:
5734  * <pre><code>
5735  Employee = function(name){
5736     this.name = name;
5737     this.addEvents({
5738         "fired" : true,
5739         "quit" : true
5740     });
5741  }
5742  Roo.extend(Employee, Roo.util.Observable);
5743 </code></pre>
5744  * @param {Object} config properties to use (incuding events / listeners)
5745  */
5746
5747 Roo.util.Observable = function(cfg){
5748     
5749     cfg = cfg|| {};
5750     this.addEvents(cfg.events || {});
5751     if (cfg.events) {
5752         delete cfg.events; // make sure
5753     }
5754      
5755     Roo.apply(this, cfg);
5756     
5757     if(this.listeners){
5758         this.on(this.listeners);
5759         delete this.listeners;
5760     }
5761 };
5762 Roo.util.Observable.prototype = {
5763     /** 
5764  * @cfg {Object} listeners  list of events and functions to call for this object, 
5765  * For example :
5766  * <pre><code>
5767     listeners :  { 
5768        'click' : function(e) {
5769            ..... 
5770         } ,
5771         .... 
5772     } 
5773   </code></pre>
5774  */
5775     
5776     
5777     /**
5778      * Fires the specified event with the passed parameters (minus the event name).
5779      * @param {String} eventName
5780      * @param {Object...} args Variable number of parameters are passed to handlers
5781      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5782      */
5783     fireEvent : function(){
5784         var ce = this.events[arguments[0].toLowerCase()];
5785         if(typeof ce == "object"){
5786             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5787         }else{
5788             return true;
5789         }
5790     },
5791
5792     // private
5793     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5794
5795     /**
5796      * Appends an event handler to this component
5797      * @param {String}   eventName The type of event to listen for
5798      * @param {Function} handler The method the event invokes
5799      * @param {Object}   scope (optional) The scope in which to execute the handler
5800      * function. The handler function's "this" context.
5801      * @param {Object}   options (optional) An object containing handler configuration
5802      * properties. This may contain any of the following properties:<ul>
5803      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5804      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5805      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5806      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5807      * by the specified number of milliseconds. If the event fires again within that time, the original
5808      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5809      * </ul><br>
5810      * <p>
5811      * <b>Combining Options</b><br>
5812      * Using the options argument, it is possible to combine different types of listeners:<br>
5813      * <br>
5814      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5815                 <pre><code>
5816                 el.on('click', this.onClick, this, {
5817                         single: true,
5818                 delay: 100,
5819                 forumId: 4
5820                 });
5821                 </code></pre>
5822      * <p>
5823      * <b>Attaching multiple handlers in 1 call</b><br>
5824      * The method also allows for a single argument to be passed which is a config object containing properties
5825      * which specify multiple handlers.
5826      * <pre><code>
5827                 el.on({
5828                         'click': {
5829                         fn: this.onClick,
5830                         scope: this,
5831                         delay: 100
5832                 }, 
5833                 'mouseover': {
5834                         fn: this.onMouseOver,
5835                         scope: this
5836                 },
5837                 'mouseout': {
5838                         fn: this.onMouseOut,
5839                         scope: this
5840                 }
5841                 });
5842                 </code></pre>
5843      * <p>
5844      * Or a shorthand syntax which passes the same scope object to all handlers:
5845         <pre><code>
5846                 el.on({
5847                         'click': this.onClick,
5848                 'mouseover': this.onMouseOver,
5849                 'mouseout': this.onMouseOut,
5850                 scope: this
5851                 });
5852                 </code></pre>
5853      */
5854     addListener : function(eventName, fn, scope, o){
5855         if(typeof eventName == "object"){
5856             o = eventName;
5857             for(var e in o){
5858                 if(this.filterOptRe.test(e)){
5859                     continue;
5860                 }
5861                 if(typeof o[e] == "function"){
5862                     // shared options
5863                     this.addListener(e, o[e], o.scope,  o);
5864                 }else{
5865                     // individual options
5866                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5867                 }
5868             }
5869             return;
5870         }
5871         o = (!o || typeof o == "boolean") ? {} : o;
5872         eventName = eventName.toLowerCase();
5873         var ce = this.events[eventName] || true;
5874         if(typeof ce == "boolean"){
5875             ce = new Roo.util.Event(this, eventName);
5876             this.events[eventName] = ce;
5877         }
5878         ce.addListener(fn, scope, o);
5879     },
5880
5881     /**
5882      * Removes a listener
5883      * @param {String}   eventName     The type of event to listen for
5884      * @param {Function} handler        The handler to remove
5885      * @param {Object}   scope  (optional) The scope (this object) for the handler
5886      */
5887     removeListener : function(eventName, fn, scope){
5888         var ce = this.events[eventName.toLowerCase()];
5889         if(typeof ce == "object"){
5890             ce.removeListener(fn, scope);
5891         }
5892     },
5893
5894     /**
5895      * Removes all listeners for this object
5896      */
5897     purgeListeners : function(){
5898         for(var evt in this.events){
5899             if(typeof this.events[evt] == "object"){
5900                  this.events[evt].clearListeners();
5901             }
5902         }
5903     },
5904
5905     relayEvents : function(o, events){
5906         var createHandler = function(ename){
5907             return function(){
5908                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5909             };
5910         };
5911         for(var i = 0, len = events.length; i < len; i++){
5912             var ename = events[i];
5913             if(!this.events[ename]){ this.events[ename] = true; };
5914             o.on(ename, createHandler(ename), this);
5915         }
5916     },
5917
5918     /**
5919      * Used to define events on this Observable
5920      * @param {Object} object The object with the events defined
5921      */
5922     addEvents : function(o){
5923         if(!this.events){
5924             this.events = {};
5925         }
5926         Roo.applyIf(this.events, o);
5927     },
5928
5929     /**
5930      * Checks to see if this object has any listeners for a specified event
5931      * @param {String} eventName The name of the event to check for
5932      * @return {Boolean} True if the event is being listened for, else false
5933      */
5934     hasListener : function(eventName){
5935         var e = this.events[eventName];
5936         return typeof e == "object" && e.listeners.length > 0;
5937     }
5938 };
5939 /**
5940  * Appends an event handler to this element (shorthand for addListener)
5941  * @param {String}   eventName     The type of event to listen for
5942  * @param {Function} handler        The method the event invokes
5943  * @param {Object}   scope (optional) The scope in which to execute the handler
5944  * function. The handler function's "this" context.
5945  * @param {Object}   options  (optional)
5946  * @method
5947  */
5948 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5949 /**
5950  * Removes a listener (shorthand for removeListener)
5951  * @param {String}   eventName     The type of event to listen for
5952  * @param {Function} handler        The handler to remove
5953  * @param {Object}   scope  (optional) The scope (this object) for the handler
5954  * @method
5955  */
5956 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5957
5958 /**
5959  * Starts capture on the specified Observable. All events will be passed
5960  * to the supplied function with the event name + standard signature of the event
5961  * <b>before</b> the event is fired. If the supplied function returns false,
5962  * the event will not fire.
5963  * @param {Observable} o The Observable to capture
5964  * @param {Function} fn The function to call
5965  * @param {Object} scope (optional) The scope (this object) for the fn
5966  * @static
5967  */
5968 Roo.util.Observable.capture = function(o, fn, scope){
5969     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5970 };
5971
5972 /**
5973  * Removes <b>all</b> added captures from the Observable.
5974  * @param {Observable} o The Observable to release
5975  * @static
5976  */
5977 Roo.util.Observable.releaseCapture = function(o){
5978     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5979 };
5980
5981 (function(){
5982
5983     var createBuffered = function(h, o, scope){
5984         var task = new Roo.util.DelayedTask();
5985         return function(){
5986             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5987         };
5988     };
5989
5990     var createSingle = function(h, e, fn, scope){
5991         return function(){
5992             e.removeListener(fn, scope);
5993             return h.apply(scope, arguments);
5994         };
5995     };
5996
5997     var createDelayed = function(h, o, scope){
5998         return function(){
5999             var args = Array.prototype.slice.call(arguments, 0);
6000             setTimeout(function(){
6001                 h.apply(scope, args);
6002             }, o.delay || 10);
6003         };
6004     };
6005
6006     Roo.util.Event = function(obj, name){
6007         this.name = name;
6008         this.obj = obj;
6009         this.listeners = [];
6010     };
6011
6012     Roo.util.Event.prototype = {
6013         addListener : function(fn, scope, options){
6014             var o = options || {};
6015             scope = scope || this.obj;
6016             if(!this.isListening(fn, scope)){
6017                 var l = {fn: fn, scope: scope, options: o};
6018                 var h = fn;
6019                 if(o.delay){
6020                     h = createDelayed(h, o, scope);
6021                 }
6022                 if(o.single){
6023                     h = createSingle(h, this, fn, scope);
6024                 }
6025                 if(o.buffer){
6026                     h = createBuffered(h, o, scope);
6027                 }
6028                 l.fireFn = h;
6029                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6030                     this.listeners.push(l);
6031                 }else{
6032                     this.listeners = this.listeners.slice(0);
6033                     this.listeners.push(l);
6034                 }
6035             }
6036         },
6037
6038         findListener : function(fn, scope){
6039             scope = scope || this.obj;
6040             var ls = this.listeners;
6041             for(var i = 0, len = ls.length; i < len; i++){
6042                 var l = ls[i];
6043                 if(l.fn == fn && l.scope == scope){
6044                     return i;
6045                 }
6046             }
6047             return -1;
6048         },
6049
6050         isListening : function(fn, scope){
6051             return this.findListener(fn, scope) != -1;
6052         },
6053
6054         removeListener : function(fn, scope){
6055             var index;
6056             if((index = this.findListener(fn, scope)) != -1){
6057                 if(!this.firing){
6058                     this.listeners.splice(index, 1);
6059                 }else{
6060                     this.listeners = this.listeners.slice(0);
6061                     this.listeners.splice(index, 1);
6062                 }
6063                 return true;
6064             }
6065             return false;
6066         },
6067
6068         clearListeners : function(){
6069             this.listeners = [];
6070         },
6071
6072         fire : function(){
6073             var ls = this.listeners, scope, len = ls.length;
6074             if(len > 0){
6075                 this.firing = true;
6076                 var args = Array.prototype.slice.call(arguments, 0);
6077                 for(var i = 0; i < len; i++){
6078                     var l = ls[i];
6079                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6080                         this.firing = false;
6081                         return false;
6082                     }
6083                 }
6084                 this.firing = false;
6085             }
6086             return true;
6087         }
6088     };
6089 })();/*
6090  * RooJS Library 
6091  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6092  *
6093  * Licence LGPL 
6094  *
6095  */
6096  
6097 /**
6098  * @class Roo.Document
6099  * @extends Roo.util.Observable
6100  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6101  * 
6102  * @param {Object} config the methods and properties of the 'base' class for the application.
6103  * 
6104  *  Generic Page handler - implement this to start your app..
6105  * 
6106  * eg.
6107  *  MyProject = new Roo.Document({
6108         events : {
6109             'load' : true // your events..
6110         },
6111         listeners : {
6112             'ready' : function() {
6113                 // fired on Roo.onReady()
6114             }
6115         }
6116  * 
6117  */
6118 Roo.Document = function(cfg) {
6119      
6120     this.addEvents({ 
6121         'ready' : true
6122     });
6123     Roo.util.Observable.call(this,cfg);
6124     
6125     var _this = this;
6126     
6127     Roo.onReady(function() {
6128         _this.fireEvent('ready');
6129     },null,false);
6130     
6131     
6132 }
6133
6134 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6135  * Based on:
6136  * Ext JS Library 1.1.1
6137  * Copyright(c) 2006-2007, Ext JS, LLC.
6138  *
6139  * Originally Released Under LGPL - original licence link has changed is not relivant.
6140  *
6141  * Fork - LGPL
6142  * <script type="text/javascript">
6143  */
6144
6145 /**
6146  * @class Roo.EventManager
6147  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6148  * several useful events directly.
6149  * See {@link Roo.EventObject} for more details on normalized event objects.
6150  * @singleton
6151  */
6152 Roo.EventManager = function(){
6153     var docReadyEvent, docReadyProcId, docReadyState = false;
6154     var resizeEvent, resizeTask, textEvent, textSize;
6155     var E = Roo.lib.Event;
6156     var D = Roo.lib.Dom;
6157
6158     
6159     
6160
6161     var fireDocReady = function(){
6162         if(!docReadyState){
6163             docReadyState = true;
6164             Roo.isReady = true;
6165             if(docReadyProcId){
6166                 clearInterval(docReadyProcId);
6167             }
6168             if(Roo.isGecko || Roo.isOpera) {
6169                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6170             }
6171             if(Roo.isIE){
6172                 var defer = document.getElementById("ie-deferred-loader");
6173                 if(defer){
6174                     defer.onreadystatechange = null;
6175                     defer.parentNode.removeChild(defer);
6176                 }
6177             }
6178             if(docReadyEvent){
6179                 docReadyEvent.fire();
6180                 docReadyEvent.clearListeners();
6181             }
6182         }
6183     };
6184     
6185     var initDocReady = function(){
6186         docReadyEvent = new Roo.util.Event();
6187         if(Roo.isGecko || Roo.isOpera) {
6188             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6189         }else if(Roo.isIE){
6190             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6191             var defer = document.getElementById("ie-deferred-loader");
6192             defer.onreadystatechange = function(){
6193                 if(this.readyState == "complete"){
6194                     fireDocReady();
6195                 }
6196             };
6197         }else if(Roo.isSafari){ 
6198             docReadyProcId = setInterval(function(){
6199                 var rs = document.readyState;
6200                 if(rs == "complete") {
6201                     fireDocReady();     
6202                  }
6203             }, 10);
6204         }
6205         // no matter what, make sure it fires on load
6206         E.on(window, "load", fireDocReady);
6207     };
6208
6209     var createBuffered = function(h, o){
6210         var task = new Roo.util.DelayedTask(h);
6211         return function(e){
6212             // create new event object impl so new events don't wipe out properties
6213             e = new Roo.EventObjectImpl(e);
6214             task.delay(o.buffer, h, null, [e]);
6215         };
6216     };
6217
6218     var createSingle = function(h, el, ename, fn){
6219         return function(e){
6220             Roo.EventManager.removeListener(el, ename, fn);
6221             h(e);
6222         };
6223     };
6224
6225     var createDelayed = function(h, o){
6226         return function(e){
6227             // create new event object impl so new events don't wipe out properties
6228             e = new Roo.EventObjectImpl(e);
6229             setTimeout(function(){
6230                 h(e);
6231             }, o.delay || 10);
6232         };
6233     };
6234     var transitionEndVal = false;
6235     
6236     var transitionEnd = function()
6237     {
6238         if (transitionEndVal) {
6239             return transitionEndVal;
6240         }
6241         var el = document.createElement('div');
6242
6243         var transEndEventNames = {
6244             WebkitTransition : 'webkitTransitionEnd',
6245             MozTransition    : 'transitionend',
6246             OTransition      : 'oTransitionEnd otransitionend',
6247             transition       : 'transitionend'
6248         };
6249     
6250         for (var name in transEndEventNames) {
6251             if (el.style[name] !== undefined) {
6252                 transitionEndVal = transEndEventNames[name];
6253                 return  transitionEndVal ;
6254             }
6255         }
6256     }
6257     
6258
6259     var listen = function(element, ename, opt, fn, scope){
6260         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6261         fn = fn || o.fn; scope = scope || o.scope;
6262         var el = Roo.getDom(element);
6263         
6264         
6265         if(!el){
6266             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6267         }
6268         
6269         if (ename == 'transitionend') {
6270             ename = transitionEnd();
6271         }
6272         var h = function(e){
6273             e = Roo.EventObject.setEvent(e);
6274             var t;
6275             if(o.delegate){
6276                 t = e.getTarget(o.delegate, el);
6277                 if(!t){
6278                     return;
6279                 }
6280             }else{
6281                 t = e.target;
6282             }
6283             if(o.stopEvent === true){
6284                 e.stopEvent();
6285             }
6286             if(o.preventDefault === true){
6287                e.preventDefault();
6288             }
6289             if(o.stopPropagation === true){
6290                 e.stopPropagation();
6291             }
6292
6293             if(o.normalized === false){
6294                 e = e.browserEvent;
6295             }
6296
6297             fn.call(scope || el, e, t, o);
6298         };
6299         if(o.delay){
6300             h = createDelayed(h, o);
6301         }
6302         if(o.single){
6303             h = createSingle(h, el, ename, fn);
6304         }
6305         if(o.buffer){
6306             h = createBuffered(h, o);
6307         }
6308         
6309         fn._handlers = fn._handlers || [];
6310         
6311         
6312         fn._handlers.push([Roo.id(el), ename, h]);
6313         
6314         
6315          
6316         E.on(el, ename, h);
6317         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6318             el.addEventListener("DOMMouseScroll", h, false);
6319             E.on(window, 'unload', function(){
6320                 el.removeEventListener("DOMMouseScroll", h, false);
6321             });
6322         }
6323         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6324             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6325         }
6326         return h;
6327     };
6328
6329     var stopListening = function(el, ename, fn){
6330         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6331         if(hds){
6332             for(var i = 0, len = hds.length; i < len; i++){
6333                 var h = hds[i];
6334                 if(h[0] == id && h[1] == ename){
6335                     hd = h[2];
6336                     hds.splice(i, 1);
6337                     break;
6338                 }
6339             }
6340         }
6341         E.un(el, ename, hd);
6342         el = Roo.getDom(el);
6343         if(ename == "mousewheel" && el.addEventListener){
6344             el.removeEventListener("DOMMouseScroll", hd, false);
6345         }
6346         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6347             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6348         }
6349     };
6350
6351     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6352     
6353     var pub = {
6354         
6355         
6356         /** 
6357          * Fix for doc tools
6358          * @scope Roo.EventManager
6359          */
6360         
6361         
6362         /** 
6363          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6364          * object with a Roo.EventObject
6365          * @param {Function} fn        The method the event invokes
6366          * @param {Object}   scope    An object that becomes the scope of the handler
6367          * @param {boolean}  override If true, the obj passed in becomes
6368          *                             the execution scope of the listener
6369          * @return {Function} The wrapped function
6370          * @deprecated
6371          */
6372         wrap : function(fn, scope, override){
6373             return function(e){
6374                 Roo.EventObject.setEvent(e);
6375                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6376             };
6377         },
6378         
6379         /**
6380      * Appends an event handler to an element (shorthand for addListener)
6381      * @param {String/HTMLElement}   element        The html element or id to assign the
6382      * @param {String}   eventName The type of event to listen for
6383      * @param {Function} handler The method the event invokes
6384      * @param {Object}   scope (optional) The scope in which to execute the handler
6385      * function. The handler function's "this" context.
6386      * @param {Object}   options (optional) An object containing handler configuration
6387      * properties. This may contain any of the following properties:<ul>
6388      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6389      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6390      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6391      * <li>preventDefault {Boolean} True to prevent the default action</li>
6392      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6393      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6394      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6395      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6396      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6397      * by the specified number of milliseconds. If the event fires again within that time, the original
6398      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6399      * </ul><br>
6400      * <p>
6401      * <b>Combining Options</b><br>
6402      * Using the options argument, it is possible to combine different types of listeners:<br>
6403      * <br>
6404      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6405      * Code:<pre><code>
6406 el.on('click', this.onClick, this, {
6407     single: true,
6408     delay: 100,
6409     stopEvent : true,
6410     forumId: 4
6411 });</code></pre>
6412      * <p>
6413      * <b>Attaching multiple handlers in 1 call</b><br>
6414       * The method also allows for a single argument to be passed which is a config object containing properties
6415      * which specify multiple handlers.
6416      * <p>
6417      * Code:<pre><code>
6418 el.on({
6419     'click' : {
6420         fn: this.onClick
6421         scope: this,
6422         delay: 100
6423     },
6424     'mouseover' : {
6425         fn: this.onMouseOver
6426         scope: this
6427     },
6428     'mouseout' : {
6429         fn: this.onMouseOut
6430         scope: this
6431     }
6432 });</code></pre>
6433      * <p>
6434      * Or a shorthand syntax:<br>
6435      * Code:<pre><code>
6436 el.on({
6437     'click' : this.onClick,
6438     'mouseover' : this.onMouseOver,
6439     'mouseout' : this.onMouseOut
6440     scope: this
6441 });</code></pre>
6442      */
6443         addListener : function(element, eventName, fn, scope, options){
6444             if(typeof eventName == "object"){
6445                 var o = eventName;
6446                 for(var e in o){
6447                     if(propRe.test(e)){
6448                         continue;
6449                     }
6450                     if(typeof o[e] == "function"){
6451                         // shared options
6452                         listen(element, e, o, o[e], o.scope);
6453                     }else{
6454                         // individual options
6455                         listen(element, e, o[e]);
6456                     }
6457                 }
6458                 return;
6459             }
6460             return listen(element, eventName, options, fn, scope);
6461         },
6462         
6463         /**
6464          * Removes an event handler
6465          *
6466          * @param {String/HTMLElement}   element        The id or html element to remove the 
6467          *                             event from
6468          * @param {String}   eventName     The type of event
6469          * @param {Function} fn
6470          * @return {Boolean} True if a listener was actually removed
6471          */
6472         removeListener : function(element, eventName, fn){
6473             return stopListening(element, eventName, fn);
6474         },
6475         
6476         /**
6477          * Fires when the document is ready (before onload and before images are loaded). Can be 
6478          * accessed shorthanded Roo.onReady().
6479          * @param {Function} fn        The method the event invokes
6480          * @param {Object}   scope    An  object that becomes the scope of the handler
6481          * @param {boolean}  options
6482          */
6483         onDocumentReady : function(fn, scope, options){
6484             if(docReadyState){ // if it already fired
6485                 docReadyEvent.addListener(fn, scope, options);
6486                 docReadyEvent.fire();
6487                 docReadyEvent.clearListeners();
6488                 return;
6489             }
6490             if(!docReadyEvent){
6491                 initDocReady();
6492             }
6493             docReadyEvent.addListener(fn, scope, options);
6494         },
6495         
6496         /**
6497          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6498          * @param {Function} fn        The method the event invokes
6499          * @param {Object}   scope    An object that becomes the scope of the handler
6500          * @param {boolean}  options
6501          */
6502         onWindowResize : function(fn, scope, options){
6503             if(!resizeEvent){
6504                 resizeEvent = new Roo.util.Event();
6505                 resizeTask = new Roo.util.DelayedTask(function(){
6506                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6507                 });
6508                 E.on(window, "resize", function(){
6509                     if(Roo.isIE){
6510                         resizeTask.delay(50);
6511                     }else{
6512                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6513                     }
6514                 });
6515             }
6516             resizeEvent.addListener(fn, scope, options);
6517         },
6518
6519         /**
6520          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6521          * @param {Function} fn        The method the event invokes
6522          * @param {Object}   scope    An object that becomes the scope of the handler
6523          * @param {boolean}  options
6524          */
6525         onTextResize : function(fn, scope, options){
6526             if(!textEvent){
6527                 textEvent = new Roo.util.Event();
6528                 var textEl = new Roo.Element(document.createElement('div'));
6529                 textEl.dom.className = 'x-text-resize';
6530                 textEl.dom.innerHTML = 'X';
6531                 textEl.appendTo(document.body);
6532                 textSize = textEl.dom.offsetHeight;
6533                 setInterval(function(){
6534                     if(textEl.dom.offsetHeight != textSize){
6535                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6536                     }
6537                 }, this.textResizeInterval);
6538             }
6539             textEvent.addListener(fn, scope, options);
6540         },
6541
6542         /**
6543          * Removes the passed window resize listener.
6544          * @param {Function} fn        The method the event invokes
6545          * @param {Object}   scope    The scope of handler
6546          */
6547         removeResizeListener : function(fn, scope){
6548             if(resizeEvent){
6549                 resizeEvent.removeListener(fn, scope);
6550             }
6551         },
6552
6553         // private
6554         fireResize : function(){
6555             if(resizeEvent){
6556                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6557             }   
6558         },
6559         /**
6560          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6561          */
6562         ieDeferSrc : false,
6563         /**
6564          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6565          */
6566         textResizeInterval : 50
6567     };
6568     
6569     /**
6570      * Fix for doc tools
6571      * @scopeAlias pub=Roo.EventManager
6572      */
6573     
6574      /**
6575      * Appends an event handler to an element (shorthand for addListener)
6576      * @param {String/HTMLElement}   element        The html element or id to assign the
6577      * @param {String}   eventName The type of event to listen for
6578      * @param {Function} handler The method the event invokes
6579      * @param {Object}   scope (optional) The scope in which to execute the handler
6580      * function. The handler function's "this" context.
6581      * @param {Object}   options (optional) An object containing handler configuration
6582      * properties. This may contain any of the following properties:<ul>
6583      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6584      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6585      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6586      * <li>preventDefault {Boolean} True to prevent the default action</li>
6587      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6588      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6589      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6590      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6591      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6592      * by the specified number of milliseconds. If the event fires again within that time, the original
6593      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6594      * </ul><br>
6595      * <p>
6596      * <b>Combining Options</b><br>
6597      * Using the options argument, it is possible to combine different types of listeners:<br>
6598      * <br>
6599      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6600      * Code:<pre><code>
6601 el.on('click', this.onClick, this, {
6602     single: true,
6603     delay: 100,
6604     stopEvent : true,
6605     forumId: 4
6606 });</code></pre>
6607      * <p>
6608      * <b>Attaching multiple handlers in 1 call</b><br>
6609       * The method also allows for a single argument to be passed which is a config object containing properties
6610      * which specify multiple handlers.
6611      * <p>
6612      * Code:<pre><code>
6613 el.on({
6614     'click' : {
6615         fn: this.onClick
6616         scope: this,
6617         delay: 100
6618     },
6619     'mouseover' : {
6620         fn: this.onMouseOver
6621         scope: this
6622     },
6623     'mouseout' : {
6624         fn: this.onMouseOut
6625         scope: this
6626     }
6627 });</code></pre>
6628      * <p>
6629      * Or a shorthand syntax:<br>
6630      * Code:<pre><code>
6631 el.on({
6632     'click' : this.onClick,
6633     'mouseover' : this.onMouseOver,
6634     'mouseout' : this.onMouseOut
6635     scope: this
6636 });</code></pre>
6637      */
6638     pub.on = pub.addListener;
6639     pub.un = pub.removeListener;
6640
6641     pub.stoppedMouseDownEvent = new Roo.util.Event();
6642     return pub;
6643 }();
6644 /**
6645   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6646   * @param {Function} fn        The method the event invokes
6647   * @param {Object}   scope    An  object that becomes the scope of the handler
6648   * @param {boolean}  override If true, the obj passed in becomes
6649   *                             the execution scope of the listener
6650   * @member Roo
6651   * @method onReady
6652  */
6653 Roo.onReady = Roo.EventManager.onDocumentReady;
6654
6655 Roo.onReady(function(){
6656     var bd = Roo.get(document.body);
6657     if(!bd){ return; }
6658
6659     var cls = [
6660             Roo.isIE ? "roo-ie"
6661             : Roo.isIE11 ? "roo-ie11"
6662             : Roo.isEdge ? "roo-edge"
6663             : Roo.isGecko ? "roo-gecko"
6664             : Roo.isOpera ? "roo-opera"
6665             : Roo.isSafari ? "roo-safari" : ""];
6666
6667     if(Roo.isMac){
6668         cls.push("roo-mac");
6669     }
6670     if(Roo.isLinux){
6671         cls.push("roo-linux");
6672     }
6673     if(Roo.isIOS){
6674         cls.push("roo-ios");
6675     }
6676     if(Roo.isTouch){
6677         cls.push("roo-touch");
6678     }
6679     if(Roo.isBorderBox){
6680         cls.push('roo-border-box');
6681     }
6682     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6683         var p = bd.dom.parentNode;
6684         if(p){
6685             p.className += ' roo-strict';
6686         }
6687     }
6688     bd.addClass(cls.join(' '));
6689 });
6690
6691 /**
6692  * @class Roo.EventObject
6693  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6694  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6695  * Example:
6696  * <pre><code>
6697  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6698     e.preventDefault();
6699     var target = e.getTarget();
6700     ...
6701  }
6702  var myDiv = Roo.get("myDiv");
6703  myDiv.on("click", handleClick);
6704  //or
6705  Roo.EventManager.on("myDiv", 'click', handleClick);
6706  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6707  </code></pre>
6708  * @singleton
6709  */
6710 Roo.EventObject = function(){
6711     
6712     var E = Roo.lib.Event;
6713     
6714     // safari keypress events for special keys return bad keycodes
6715     var safariKeys = {
6716         63234 : 37, // left
6717         63235 : 39, // right
6718         63232 : 38, // up
6719         63233 : 40, // down
6720         63276 : 33, // page up
6721         63277 : 34, // page down
6722         63272 : 46, // delete
6723         63273 : 36, // home
6724         63275 : 35  // end
6725     };
6726
6727     // normalize button clicks
6728     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6729                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6730
6731     Roo.EventObjectImpl = function(e){
6732         if(e){
6733             this.setEvent(e.browserEvent || e);
6734         }
6735     };
6736     Roo.EventObjectImpl.prototype = {
6737         /**
6738          * Used to fix doc tools.
6739          * @scope Roo.EventObject.prototype
6740          */
6741             
6742
6743         
6744         
6745         /** The normal browser event */
6746         browserEvent : null,
6747         /** The button pressed in a mouse event */
6748         button : -1,
6749         /** True if the shift key was down during the event */
6750         shiftKey : false,
6751         /** True if the control key was down during the event */
6752         ctrlKey : false,
6753         /** True if the alt key was down during the event */
6754         altKey : false,
6755
6756         /** Key constant 
6757         * @type Number */
6758         BACKSPACE : 8,
6759         /** Key constant 
6760         * @type Number */
6761         TAB : 9,
6762         /** Key constant 
6763         * @type Number */
6764         RETURN : 13,
6765         /** Key constant 
6766         * @type Number */
6767         ENTER : 13,
6768         /** Key constant 
6769         * @type Number */
6770         SHIFT : 16,
6771         /** Key constant 
6772         * @type Number */
6773         CONTROL : 17,
6774         /** Key constant 
6775         * @type Number */
6776         ESC : 27,
6777         /** Key constant 
6778         * @type Number */
6779         SPACE : 32,
6780         /** Key constant 
6781         * @type Number */
6782         PAGEUP : 33,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEDOWN : 34,
6786         /** Key constant 
6787         * @type Number */
6788         END : 35,
6789         /** Key constant 
6790         * @type Number */
6791         HOME : 36,
6792         /** Key constant 
6793         * @type Number */
6794         LEFT : 37,
6795         /** Key constant 
6796         * @type Number */
6797         UP : 38,
6798         /** Key constant 
6799         * @type Number */
6800         RIGHT : 39,
6801         /** Key constant 
6802         * @type Number */
6803         DOWN : 40,
6804         /** Key constant 
6805         * @type Number */
6806         DELETE : 46,
6807         /** Key constant 
6808         * @type Number */
6809         F5 : 116,
6810
6811            /** @private */
6812         setEvent : function(e){
6813             if(e == this || (e && e.browserEvent)){ // already wrapped
6814                 return e;
6815             }
6816             this.browserEvent = e;
6817             if(e){
6818                 // normalize buttons
6819                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6820                 if(e.type == 'click' && this.button == -1){
6821                     this.button = 0;
6822                 }
6823                 this.type = e.type;
6824                 this.shiftKey = e.shiftKey;
6825                 // mac metaKey behaves like ctrlKey
6826                 this.ctrlKey = e.ctrlKey || e.metaKey;
6827                 this.altKey = e.altKey;
6828                 // in getKey these will be normalized for the mac
6829                 this.keyCode = e.keyCode;
6830                 // keyup warnings on firefox.
6831                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6832                 // cache the target for the delayed and or buffered events
6833                 this.target = E.getTarget(e);
6834                 // same for XY
6835                 this.xy = E.getXY(e);
6836             }else{
6837                 this.button = -1;
6838                 this.shiftKey = false;
6839                 this.ctrlKey = false;
6840                 this.altKey = false;
6841                 this.keyCode = 0;
6842                 this.charCode =0;
6843                 this.target = null;
6844                 this.xy = [0, 0];
6845             }
6846             return this;
6847         },
6848
6849         /**
6850          * Stop the event (preventDefault and stopPropagation)
6851          */
6852         stopEvent : function(){
6853             if(this.browserEvent){
6854                 if(this.browserEvent.type == 'mousedown'){
6855                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6856                 }
6857                 E.stopEvent(this.browserEvent);
6858             }
6859         },
6860
6861         /**
6862          * Prevents the browsers default handling of the event.
6863          */
6864         preventDefault : function(){
6865             if(this.browserEvent){
6866                 E.preventDefault(this.browserEvent);
6867             }
6868         },
6869
6870         /** @private */
6871         isNavKeyPress : function(){
6872             var k = this.keyCode;
6873             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6874             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6875         },
6876
6877         isSpecialKey : function(){
6878             var k = this.keyCode;
6879             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6880             (k == 16) || (k == 17) ||
6881             (k >= 18 && k <= 20) ||
6882             (k >= 33 && k <= 35) ||
6883             (k >= 36 && k <= 39) ||
6884             (k >= 44 && k <= 45);
6885         },
6886         /**
6887          * Cancels bubbling of the event.
6888          */
6889         stopPropagation : function(){
6890             if(this.browserEvent){
6891                 if(this.type == 'mousedown'){
6892                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6893                 }
6894                 E.stopPropagation(this.browserEvent);
6895             }
6896         },
6897
6898         /**
6899          * Gets the key code for the event.
6900          * @return {Number}
6901          */
6902         getCharCode : function(){
6903             return this.charCode || this.keyCode;
6904         },
6905
6906         /**
6907          * Returns a normalized keyCode for the event.
6908          * @return {Number} The key code
6909          */
6910         getKey : function(){
6911             var k = this.keyCode || this.charCode;
6912             return Roo.isSafari ? (safariKeys[k] || k) : k;
6913         },
6914
6915         /**
6916          * Gets the x coordinate of the event.
6917          * @return {Number}
6918          */
6919         getPageX : function(){
6920             return this.xy[0];
6921         },
6922
6923         /**
6924          * Gets the y coordinate of the event.
6925          * @return {Number}
6926          */
6927         getPageY : function(){
6928             return this.xy[1];
6929         },
6930
6931         /**
6932          * Gets the time of the event.
6933          * @return {Number}
6934          */
6935         getTime : function(){
6936             if(this.browserEvent){
6937                 return E.getTime(this.browserEvent);
6938             }
6939             return null;
6940         },
6941
6942         /**
6943          * Gets the page coordinates of the event.
6944          * @return {Array} The xy values like [x, y]
6945          */
6946         getXY : function(){
6947             return this.xy;
6948         },
6949
6950         /**
6951          * Gets the target for the event.
6952          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6953          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6954                 search as a number or element (defaults to 10 || document.body)
6955          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6956          * @return {HTMLelement}
6957          */
6958         getTarget : function(selector, maxDepth, returnEl){
6959             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6960         },
6961         /**
6962          * Gets the related target.
6963          * @return {HTMLElement}
6964          */
6965         getRelatedTarget : function(){
6966             if(this.browserEvent){
6967                 return E.getRelatedTarget(this.browserEvent);
6968             }
6969             return null;
6970         },
6971
6972         /**
6973          * Normalizes mouse wheel delta across browsers
6974          * @return {Number} The delta
6975          */
6976         getWheelDelta : function(){
6977             var e = this.browserEvent;
6978             var delta = 0;
6979             if(e.wheelDelta){ /* IE/Opera. */
6980                 delta = e.wheelDelta/120;
6981             }else if(e.detail){ /* Mozilla case. */
6982                 delta = -e.detail/3;
6983             }
6984             return delta;
6985         },
6986
6987         /**
6988          * Returns true if the control, meta, shift or alt key was pressed during this event.
6989          * @return {Boolean}
6990          */
6991         hasModifier : function(){
6992             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6993         },
6994
6995         /**
6996          * Returns true if the target of this event equals el or is a child of el
6997          * @param {String/HTMLElement/Element} el
6998          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6999          * @return {Boolean}
7000          */
7001         within : function(el, related){
7002             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7003             return t && Roo.fly(el).contains(t);
7004         },
7005
7006         getPoint : function(){
7007             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7008         }
7009     };
7010
7011     return new Roo.EventObjectImpl();
7012 }();
7013             
7014     /*
7015  * Based on:
7016  * Ext JS Library 1.1.1
7017  * Copyright(c) 2006-2007, Ext JS, LLC.
7018  *
7019  * Originally Released Under LGPL - original licence link has changed is not relivant.
7020  *
7021  * Fork - LGPL
7022  * <script type="text/javascript">
7023  */
7024
7025  
7026 // was in Composite Element!??!?!
7027  
7028 (function(){
7029     var D = Roo.lib.Dom;
7030     var E = Roo.lib.Event;
7031     var A = Roo.lib.Anim;
7032
7033     // local style camelizing for speed
7034     var propCache = {};
7035     var camelRe = /(-[a-z])/gi;
7036     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7037     var view = document.defaultView;
7038
7039 /**
7040  * @class Roo.Element
7041  * Represents an Element in the DOM.<br><br>
7042  * Usage:<br>
7043 <pre><code>
7044 var el = Roo.get("my-div");
7045
7046 // or with getEl
7047 var el = getEl("my-div");
7048
7049 // or with a DOM element
7050 var el = Roo.get(myDivElement);
7051 </code></pre>
7052  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7053  * each call instead of constructing a new one.<br><br>
7054  * <b>Animations</b><br />
7055  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7056  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7057 <pre>
7058 Option    Default   Description
7059 --------- --------  ---------------------------------------------
7060 duration  .35       The duration of the animation in seconds
7061 easing    easeOut   The YUI easing method
7062 callback  none      A function to execute when the anim completes
7063 scope     this      The scope (this) of the callback function
7064 </pre>
7065 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7066 * manipulate the animation. Here's an example:
7067 <pre><code>
7068 var el = Roo.get("my-div");
7069
7070 // no animation
7071 el.setWidth(100);
7072
7073 // default animation
7074 el.setWidth(100, true);
7075
7076 // animation with some options set
7077 el.setWidth(100, {
7078     duration: 1,
7079     callback: this.foo,
7080     scope: this
7081 });
7082
7083 // using the "anim" property to get the Anim object
7084 var opt = {
7085     duration: 1,
7086     callback: this.foo,
7087     scope: this
7088 };
7089 el.setWidth(100, opt);
7090 ...
7091 if(opt.anim.isAnimated()){
7092     opt.anim.stop();
7093 }
7094 </code></pre>
7095 * <b> Composite (Collections of) Elements</b><br />
7096  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7097  * @constructor Create a new Element directly.
7098  * @param {String/HTMLElement} element
7099  * @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).
7100  */
7101     Roo.Element = function(element, forceNew){
7102         var dom = typeof element == "string" ?
7103                 document.getElementById(element) : element;
7104         if(!dom){ // invalid id/element
7105             return null;
7106         }
7107         var id = dom.id;
7108         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7109             return Roo.Element.cache[id];
7110         }
7111
7112         /**
7113          * The DOM element
7114          * @type HTMLElement
7115          */
7116         this.dom = dom;
7117
7118         /**
7119          * The DOM element ID
7120          * @type String
7121          */
7122         this.id = id || Roo.id(dom);
7123     };
7124
7125     var El = Roo.Element;
7126
7127     El.prototype = {
7128         /**
7129          * The element's default display mode  (defaults to "")
7130          * @type String
7131          */
7132         originalDisplay : "",
7133
7134         visibilityMode : 1,
7135         /**
7136          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7137          * @type String
7138          */
7139         defaultUnit : "px",
7140         
7141         /**
7142          * Sets the element's visibility mode. When setVisible() is called it
7143          * will use this to determine whether to set the visibility or the display property.
7144          * @param visMode Element.VISIBILITY or Element.DISPLAY
7145          * @return {Roo.Element} this
7146          */
7147         setVisibilityMode : function(visMode){
7148             this.visibilityMode = visMode;
7149             return this;
7150         },
7151         /**
7152          * Convenience method for setVisibilityMode(Element.DISPLAY)
7153          * @param {String} display (optional) What to set display to when visible
7154          * @return {Roo.Element} this
7155          */
7156         enableDisplayMode : function(display){
7157             this.setVisibilityMode(El.DISPLAY);
7158             if(typeof display != "undefined") { this.originalDisplay = display; }
7159             return this;
7160         },
7161
7162         /**
7163          * 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)
7164          * @param {String} selector The simple selector to test
7165          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7166                 search as a number or element (defaults to 10 || document.body)
7167          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7168          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7169          */
7170         findParent : function(simpleSelector, maxDepth, returnEl){
7171             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7172             maxDepth = maxDepth || 50;
7173             if(typeof maxDepth != "number"){
7174                 stopEl = Roo.getDom(maxDepth);
7175                 maxDepth = 10;
7176             }
7177             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7178                 if(dq.is(p, simpleSelector)){
7179                     return returnEl ? Roo.get(p) : p;
7180                 }
7181                 depth++;
7182                 p = p.parentNode;
7183             }
7184             return null;
7185         },
7186
7187
7188         /**
7189          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7190          * @param {String} selector The simple selector to test
7191          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7192                 search as a number or element (defaults to 10 || document.body)
7193          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7194          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7195          */
7196         findParentNode : function(simpleSelector, maxDepth, returnEl){
7197             var p = Roo.fly(this.dom.parentNode, '_internal');
7198             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7199         },
7200         
7201         /**
7202          * Looks at  the scrollable parent element
7203          */
7204         findScrollableParent : function()
7205         {
7206             var overflowRegex = /(auto|scroll)/;
7207             
7208             if(this.getStyle('position') === 'fixed'){
7209                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7210             }
7211             
7212             var excludeStaticParent = this.getStyle('position') === "absolute";
7213             
7214             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7215                 
7216                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7217                     continue;
7218                 }
7219                 
7220                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7221                     return parent;
7222                 }
7223                 
7224                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7225                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7226                 }
7227             }
7228             
7229             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7230         },
7231
7232         /**
7233          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7234          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7235          * @param {String} selector The simple selector to test
7236          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7237                 search as a number or element (defaults to 10 || document.body)
7238          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7239          */
7240         up : function(simpleSelector, maxDepth){
7241             return this.findParentNode(simpleSelector, maxDepth, true);
7242         },
7243
7244
7245
7246         /**
7247          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7248          * @param {String} selector The simple selector to test
7249          * @return {Boolean} True if this element matches the selector, else false
7250          */
7251         is : function(simpleSelector){
7252             return Roo.DomQuery.is(this.dom, simpleSelector);
7253         },
7254
7255         /**
7256          * Perform animation on this element.
7257          * @param {Object} args The YUI animation control args
7258          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7259          * @param {Function} onComplete (optional) Function to call when animation completes
7260          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7261          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7262          * @return {Roo.Element} this
7263          */
7264         animate : function(args, duration, onComplete, easing, animType){
7265             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7266             return this;
7267         },
7268
7269         /*
7270          * @private Internal animation call
7271          */
7272         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7273             animType = animType || 'run';
7274             opt = opt || {};
7275             var anim = Roo.lib.Anim[animType](
7276                 this.dom, args,
7277                 (opt.duration || defaultDur) || .35,
7278                 (opt.easing || defaultEase) || 'easeOut',
7279                 function(){
7280                     Roo.callback(cb, this);
7281                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7282                 },
7283                 this
7284             );
7285             opt.anim = anim;
7286             return anim;
7287         },
7288
7289         // private legacy anim prep
7290         preanim : function(a, i){
7291             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7292         },
7293
7294         /**
7295          * Removes worthless text nodes
7296          * @param {Boolean} forceReclean (optional) By default the element
7297          * keeps track if it has been cleaned already so
7298          * you can call this over and over. However, if you update the element and
7299          * need to force a reclean, you can pass true.
7300          */
7301         clean : function(forceReclean){
7302             if(this.isCleaned && forceReclean !== true){
7303                 return this;
7304             }
7305             var ns = /\S/;
7306             var d = this.dom, n = d.firstChild, ni = -1;
7307             while(n){
7308                 var nx = n.nextSibling;
7309                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7310                     d.removeChild(n);
7311                 }else{
7312                     n.nodeIndex = ++ni;
7313                 }
7314                 n = nx;
7315             }
7316             this.isCleaned = true;
7317             return this;
7318         },
7319
7320         // private
7321         calcOffsetsTo : function(el){
7322             el = Roo.get(el);
7323             var d = el.dom;
7324             var restorePos = false;
7325             if(el.getStyle('position') == 'static'){
7326                 el.position('relative');
7327                 restorePos = true;
7328             }
7329             var x = 0, y =0;
7330             var op = this.dom;
7331             while(op && op != d && op.tagName != 'HTML'){
7332                 x+= op.offsetLeft;
7333                 y+= op.offsetTop;
7334                 op = op.offsetParent;
7335             }
7336             if(restorePos){
7337                 el.position('static');
7338             }
7339             return [x, y];
7340         },
7341
7342         /**
7343          * Scrolls this element into view within the passed container.
7344          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7345          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7346          * @return {Roo.Element} this
7347          */
7348         scrollIntoView : function(container, hscroll){
7349             var c = Roo.getDom(container) || document.body;
7350             var el = this.dom;
7351
7352             var o = this.calcOffsetsTo(c),
7353                 l = o[0],
7354                 t = o[1],
7355                 b = t+el.offsetHeight,
7356                 r = l+el.offsetWidth;
7357
7358             var ch = c.clientHeight;
7359             var ct = parseInt(c.scrollTop, 10);
7360             var cl = parseInt(c.scrollLeft, 10);
7361             var cb = ct + ch;
7362             var cr = cl + c.clientWidth;
7363
7364             if(t < ct){
7365                 c.scrollTop = t;
7366             }else if(b > cb){
7367                 c.scrollTop = b-ch;
7368             }
7369
7370             if(hscroll !== false){
7371                 if(l < cl){
7372                     c.scrollLeft = l;
7373                 }else if(r > cr){
7374                     c.scrollLeft = r-c.clientWidth;
7375                 }
7376             }
7377             return this;
7378         },
7379
7380         // private
7381         scrollChildIntoView : function(child, hscroll){
7382             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7383         },
7384
7385         /**
7386          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7387          * the new height may not be available immediately.
7388          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7389          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7390          * @param {Function} onComplete (optional) Function to call when animation completes
7391          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7392          * @return {Roo.Element} this
7393          */
7394         autoHeight : function(animate, duration, onComplete, easing){
7395             var oldHeight = this.getHeight();
7396             this.clip();
7397             this.setHeight(1); // force clipping
7398             setTimeout(function(){
7399                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7400                 if(!animate){
7401                     this.setHeight(height);
7402                     this.unclip();
7403                     if(typeof onComplete == "function"){
7404                         onComplete();
7405                     }
7406                 }else{
7407                     this.setHeight(oldHeight); // restore original height
7408                     this.setHeight(height, animate, duration, function(){
7409                         this.unclip();
7410                         if(typeof onComplete == "function") { onComplete(); }
7411                     }.createDelegate(this), easing);
7412                 }
7413             }.createDelegate(this), 0);
7414             return this;
7415         },
7416
7417         /**
7418          * Returns true if this element is an ancestor of the passed element
7419          * @param {HTMLElement/String} el The element to check
7420          * @return {Boolean} True if this element is an ancestor of el, else false
7421          */
7422         contains : function(el){
7423             if(!el){return false;}
7424             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7425         },
7426
7427         /**
7428          * Checks whether the element is currently visible using both visibility and display properties.
7429          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7430          * @return {Boolean} True if the element is currently visible, else false
7431          */
7432         isVisible : function(deep) {
7433             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7434             if(deep !== true || !vis){
7435                 return vis;
7436             }
7437             var p = this.dom.parentNode;
7438             while(p && p.tagName.toLowerCase() != "body"){
7439                 if(!Roo.fly(p, '_isVisible').isVisible()){
7440                     return false;
7441                 }
7442                 p = p.parentNode;
7443             }
7444             return true;
7445         },
7446
7447         /**
7448          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7449          * @param {String} selector The CSS selector
7450          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7451          * @return {CompositeElement/CompositeElementLite} The composite element
7452          */
7453         select : function(selector, unique){
7454             return El.select(selector, unique, this.dom);
7455         },
7456
7457         /**
7458          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7459          * @param {String} selector The CSS selector
7460          * @return {Array} An array of the matched nodes
7461          */
7462         query : function(selector, unique){
7463             return Roo.DomQuery.select(selector, this.dom);
7464         },
7465
7466         /**
7467          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7468          * @param {String} selector The CSS selector
7469          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7470          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7471          */
7472         child : function(selector, returnDom){
7473             var n = Roo.DomQuery.selectNode(selector, this.dom);
7474             return returnDom ? n : Roo.get(n);
7475         },
7476
7477         /**
7478          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7479          * @param {String} selector The CSS selector
7480          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7481          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7482          */
7483         down : function(selector, returnDom){
7484             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7485             return returnDom ? n : Roo.get(n);
7486         },
7487
7488         /**
7489          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7490          * @param {String} group The group the DD object is member of
7491          * @param {Object} config The DD config object
7492          * @param {Object} overrides An object containing methods to override/implement on the DD object
7493          * @return {Roo.dd.DD} The DD object
7494          */
7495         initDD : function(group, config, overrides){
7496             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7497             return Roo.apply(dd, overrides);
7498         },
7499
7500         /**
7501          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7502          * @param {String} group The group the DDProxy object is member of
7503          * @param {Object} config The DDProxy config object
7504          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7505          * @return {Roo.dd.DDProxy} The DDProxy object
7506          */
7507         initDDProxy : function(group, config, overrides){
7508             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7509             return Roo.apply(dd, overrides);
7510         },
7511
7512         /**
7513          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7514          * @param {String} group The group the DDTarget object is member of
7515          * @param {Object} config The DDTarget config object
7516          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7517          * @return {Roo.dd.DDTarget} The DDTarget object
7518          */
7519         initDDTarget : function(group, config, overrides){
7520             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7521             return Roo.apply(dd, overrides);
7522         },
7523
7524         /**
7525          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7526          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7527          * @param {Boolean} visible Whether the element is visible
7528          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7529          * @return {Roo.Element} this
7530          */
7531          setVisible : function(visible, animate){
7532             if(!animate || !A){
7533                 if(this.visibilityMode == El.DISPLAY){
7534                     this.setDisplayed(visible);
7535                 }else{
7536                     this.fixDisplay();
7537                     this.dom.style.visibility = visible ? "visible" : "hidden";
7538                 }
7539             }else{
7540                 // closure for composites
7541                 var dom = this.dom;
7542                 var visMode = this.visibilityMode;
7543                 if(visible){
7544                     this.setOpacity(.01);
7545                     this.setVisible(true);
7546                 }
7547                 this.anim({opacity: { to: (visible?1:0) }},
7548                       this.preanim(arguments, 1),
7549                       null, .35, 'easeIn', function(){
7550                          if(!visible){
7551                              if(visMode == El.DISPLAY){
7552                                  dom.style.display = "none";
7553                              }else{
7554                                  dom.style.visibility = "hidden";
7555                              }
7556                              Roo.get(dom).setOpacity(1);
7557                          }
7558                      });
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Returns true if display is not "none"
7565          * @return {Boolean}
7566          */
7567         isDisplayed : function() {
7568             return this.getStyle("display") != "none";
7569         },
7570
7571         /**
7572          * Toggles the element's visibility or display, depending on visibility mode.
7573          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7574          * @return {Roo.Element} this
7575          */
7576         toggle : function(animate){
7577             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7583          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7584          * @return {Roo.Element} this
7585          */
7586         setDisplayed : function(value) {
7587             if(typeof value == "boolean"){
7588                value = value ? this.originalDisplay : "none";
7589             }
7590             this.setStyle("display", value);
7591             return this;
7592         },
7593
7594         /**
7595          * Tries to focus the element. Any exceptions are caught and ignored.
7596          * @return {Roo.Element} this
7597          */
7598         focus : function() {
7599             try{
7600                 this.dom.focus();
7601             }catch(e){}
7602             return this;
7603         },
7604
7605         /**
7606          * Tries to blur the element. Any exceptions are caught and ignored.
7607          * @return {Roo.Element} this
7608          */
7609         blur : function() {
7610             try{
7611                 this.dom.blur();
7612             }catch(e){}
7613             return this;
7614         },
7615
7616         /**
7617          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7618          * @param {String/Array} className The CSS class to add, or an array of classes
7619          * @return {Roo.Element} this
7620          */
7621         addClass : function(className){
7622             if(className instanceof Array){
7623                 for(var i = 0, len = className.length; i < len; i++) {
7624                     this.addClass(className[i]);
7625                 }
7626             }else{
7627                 if(className && !this.hasClass(className)){
7628                     this.dom.className = this.dom.className + " " + className;
7629                 }
7630             }
7631             return this;
7632         },
7633
7634         /**
7635          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7636          * @param {String/Array} className The CSS class to add, or an array of classes
7637          * @return {Roo.Element} this
7638          */
7639         radioClass : function(className){
7640             var siblings = this.dom.parentNode.childNodes;
7641             for(var i = 0; i < siblings.length; i++) {
7642                 var s = siblings[i];
7643                 if(s.nodeType == 1){
7644                     Roo.get(s).removeClass(className);
7645                 }
7646             }
7647             this.addClass(className);
7648             return this;
7649         },
7650
7651         /**
7652          * Removes one or more CSS classes from the element.
7653          * @param {String/Array} className The CSS class to remove, or an array of classes
7654          * @return {Roo.Element} this
7655          */
7656         removeClass : function(className){
7657             if(!className || !this.dom.className){
7658                 return this;
7659             }
7660             if(className instanceof Array){
7661                 for(var i = 0, len = className.length; i < len; i++) {
7662                     this.removeClass(className[i]);
7663                 }
7664             }else{
7665                 if(this.hasClass(className)){
7666                     var re = this.classReCache[className];
7667                     if (!re) {
7668                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7669                        this.classReCache[className] = re;
7670                     }
7671                     this.dom.className =
7672                         this.dom.className.replace(re, " ");
7673                 }
7674             }
7675             return this;
7676         },
7677
7678         // private
7679         classReCache: {},
7680
7681         /**
7682          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7683          * @param {String} className The CSS class to toggle
7684          * @return {Roo.Element} this
7685          */
7686         toggleClass : function(className){
7687             if(this.hasClass(className)){
7688                 this.removeClass(className);
7689             }else{
7690                 this.addClass(className);
7691             }
7692             return this;
7693         },
7694
7695         /**
7696          * Checks if the specified CSS class exists on this element's DOM node.
7697          * @param {String} className The CSS class to check for
7698          * @return {Boolean} True if the class exists, else false
7699          */
7700         hasClass : function(className){
7701             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7702         },
7703
7704         /**
7705          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7706          * @param {String} oldClassName The CSS class to replace
7707          * @param {String} newClassName The replacement CSS class
7708          * @return {Roo.Element} this
7709          */
7710         replaceClass : function(oldClassName, newClassName){
7711             this.removeClass(oldClassName);
7712             this.addClass(newClassName);
7713             return this;
7714         },
7715
7716         /**
7717          * Returns an object with properties matching the styles requested.
7718          * For example, el.getStyles('color', 'font-size', 'width') might return
7719          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7720          * @param {String} style1 A style name
7721          * @param {String} style2 A style name
7722          * @param {String} etc.
7723          * @return {Object} The style object
7724          */
7725         getStyles : function(){
7726             var a = arguments, len = a.length, r = {};
7727             for(var i = 0; i < len; i++){
7728                 r[a[i]] = this.getStyle(a[i]);
7729             }
7730             return r;
7731         },
7732
7733         /**
7734          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7735          * @param {String} property The style property whose value is returned.
7736          * @return {String} The current value of the style property for this element.
7737          */
7738         getStyle : function(){
7739             return view && view.getComputedStyle ?
7740                 function(prop){
7741                     var el = this.dom, v, cs, camel;
7742                     if(prop == 'float'){
7743                         prop = "cssFloat";
7744                     }
7745                     if(el.style && (v = el.style[prop])){
7746                         return v;
7747                     }
7748                     if(cs = view.getComputedStyle(el, "")){
7749                         if(!(camel = propCache[prop])){
7750                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7751                         }
7752                         return cs[camel];
7753                     }
7754                     return null;
7755                 } :
7756                 function(prop){
7757                     var el = this.dom, v, cs, camel;
7758                     if(prop == 'opacity'){
7759                         if(typeof el.style.filter == 'string'){
7760                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7761                             if(m){
7762                                 var fv = parseFloat(m[1]);
7763                                 if(!isNaN(fv)){
7764                                     return fv ? fv / 100 : 0;
7765                                 }
7766                             }
7767                         }
7768                         return 1;
7769                     }else if(prop == 'float'){
7770                         prop = "styleFloat";
7771                     }
7772                     if(!(camel = propCache[prop])){
7773                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7774                     }
7775                     if(v = el.style[camel]){
7776                         return v;
7777                     }
7778                     if(cs = el.currentStyle){
7779                         return cs[camel];
7780                     }
7781                     return null;
7782                 };
7783         }(),
7784
7785         /**
7786          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7787          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7788          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7789          * @return {Roo.Element} this
7790          */
7791         setStyle : function(prop, value){
7792             if(typeof prop == "string"){
7793                 
7794                 if (prop == 'float') {
7795                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7796                     return this;
7797                 }
7798                 
7799                 var camel;
7800                 if(!(camel = propCache[prop])){
7801                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7802                 }
7803                 
7804                 if(camel == 'opacity') {
7805                     this.setOpacity(value);
7806                 }else{
7807                     this.dom.style[camel] = value;
7808                 }
7809             }else{
7810                 for(var style in prop){
7811                     if(typeof prop[style] != "function"){
7812                        this.setStyle(style, prop[style]);
7813                     }
7814                 }
7815             }
7816             return this;
7817         },
7818
7819         /**
7820          * More flexible version of {@link #setStyle} for setting style properties.
7821          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7822          * a function which returns such a specification.
7823          * @return {Roo.Element} this
7824          */
7825         applyStyles : function(style){
7826             Roo.DomHelper.applyStyles(this.dom, style);
7827             return this;
7828         },
7829
7830         /**
7831           * 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).
7832           * @return {Number} The X position of the element
7833           */
7834         getX : function(){
7835             return D.getX(this.dom);
7836         },
7837
7838         /**
7839           * 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).
7840           * @return {Number} The Y position of the element
7841           */
7842         getY : function(){
7843             return D.getY(this.dom);
7844         },
7845
7846         /**
7847           * 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).
7848           * @return {Array} The XY position of the element
7849           */
7850         getXY : function(){
7851             return D.getXY(this.dom);
7852         },
7853
7854         /**
7855          * 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).
7856          * @param {Number} The X position of the element
7857          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7858          * @return {Roo.Element} this
7859          */
7860         setX : function(x, animate){
7861             if(!animate || !A){
7862                 D.setX(this.dom, x);
7863             }else{
7864                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7865             }
7866             return this;
7867         },
7868
7869         /**
7870          * 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).
7871          * @param {Number} The Y position of the element
7872          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7873          * @return {Roo.Element} this
7874          */
7875         setY : function(y, animate){
7876             if(!animate || !A){
7877                 D.setY(this.dom, y);
7878             }else{
7879                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7880             }
7881             return this;
7882         },
7883
7884         /**
7885          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7886          * @param {String} left The left CSS property value
7887          * @return {Roo.Element} this
7888          */
7889         setLeft : function(left){
7890             this.setStyle("left", this.addUnits(left));
7891             return this;
7892         },
7893
7894         /**
7895          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7896          * @param {String} top The top CSS property value
7897          * @return {Roo.Element} this
7898          */
7899         setTop : function(top){
7900             this.setStyle("top", this.addUnits(top));
7901             return this;
7902         },
7903
7904         /**
7905          * Sets the element's CSS right style.
7906          * @param {String} right The right CSS property value
7907          * @return {Roo.Element} this
7908          */
7909         setRight : function(right){
7910             this.setStyle("right", this.addUnits(right));
7911             return this;
7912         },
7913
7914         /**
7915          * Sets the element's CSS bottom style.
7916          * @param {String} bottom The bottom CSS property value
7917          * @return {Roo.Element} this
7918          */
7919         setBottom : function(bottom){
7920             this.setStyle("bottom", this.addUnits(bottom));
7921             return this;
7922         },
7923
7924         /**
7925          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7926          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7927          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7928          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7929          * @return {Roo.Element} this
7930          */
7931         setXY : function(pos, animate){
7932             if(!animate || !A){
7933                 D.setXY(this.dom, pos);
7934             }else{
7935                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7936             }
7937             return this;
7938         },
7939
7940         /**
7941          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7942          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7943          * @param {Number} x X value for new position (coordinates are page-based)
7944          * @param {Number} y Y value for new position (coordinates are page-based)
7945          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7946          * @return {Roo.Element} this
7947          */
7948         setLocation : function(x, y, animate){
7949             this.setXY([x, y], this.preanim(arguments, 2));
7950             return this;
7951         },
7952
7953         /**
7954          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7955          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         moveTo : function(x, y, animate){
7962             this.setXY([x, y], this.preanim(arguments, 2));
7963             return this;
7964         },
7965
7966         /**
7967          * Returns the region of the given element.
7968          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7969          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7970          */
7971         getRegion : function(){
7972             return D.getRegion(this.dom);
7973         },
7974
7975         /**
7976          * Returns the offset height of the element
7977          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7978          * @return {Number} The element's height
7979          */
7980         getHeight : function(contentHeight){
7981             var h = this.dom.offsetHeight || 0;
7982             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7983         },
7984
7985         /**
7986          * Returns the offset width of the element
7987          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7988          * @return {Number} The element's width
7989          */
7990         getWidth : function(contentWidth){
7991             var w = this.dom.offsetWidth || 0;
7992             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7993         },
7994
7995         /**
7996          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7997          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7998          * if a height has not been set using CSS.
7999          * @return {Number}
8000          */
8001         getComputedHeight : function(){
8002             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8003             if(!h){
8004                 h = parseInt(this.getStyle('height'), 10) || 0;
8005                 if(!this.isBorderBox()){
8006                     h += this.getFrameWidth('tb');
8007                 }
8008             }
8009             return h;
8010         },
8011
8012         /**
8013          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8014          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8015          * if a width has not been set using CSS.
8016          * @return {Number}
8017          */
8018         getComputedWidth : function(){
8019             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8020             if(!w){
8021                 w = parseInt(this.getStyle('width'), 10) || 0;
8022                 if(!this.isBorderBox()){
8023                     w += this.getFrameWidth('lr');
8024                 }
8025             }
8026             return w;
8027         },
8028
8029         /**
8030          * Returns the size of the element.
8031          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8032          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8033          */
8034         getSize : function(contentSize){
8035             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8036         },
8037
8038         /**
8039          * Returns the width and height of the viewport.
8040          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8041          */
8042         getViewSize : function(){
8043             var d = this.dom, doc = document, aw = 0, ah = 0;
8044             if(d == doc || d == doc.body){
8045                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8046             }else{
8047                 return {
8048                     width : d.clientWidth,
8049                     height: d.clientHeight
8050                 };
8051             }
8052         },
8053
8054         /**
8055          * Returns the value of the "value" attribute
8056          * @param {Boolean} asNumber true to parse the value as a number
8057          * @return {String/Number}
8058          */
8059         getValue : function(asNumber){
8060             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8061         },
8062
8063         // private
8064         adjustWidth : function(width){
8065             if(typeof width == "number"){
8066                 if(this.autoBoxAdjust && !this.isBorderBox()){
8067                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8068                 }
8069                 if(width < 0){
8070                     width = 0;
8071                 }
8072             }
8073             return width;
8074         },
8075
8076         // private
8077         adjustHeight : function(height){
8078             if(typeof height == "number"){
8079                if(this.autoBoxAdjust && !this.isBorderBox()){
8080                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8081                }
8082                if(height < 0){
8083                    height = 0;
8084                }
8085             }
8086             return height;
8087         },
8088
8089         /**
8090          * Set the width of the element
8091          * @param {Number} width The new width
8092          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8093          * @return {Roo.Element} this
8094          */
8095         setWidth : function(width, animate){
8096             width = this.adjustWidth(width);
8097             if(!animate || !A){
8098                 this.dom.style.width = this.addUnits(width);
8099             }else{
8100                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8101             }
8102             return this;
8103         },
8104
8105         /**
8106          * Set the height of the element
8107          * @param {Number} height The new height
8108          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8109          * @return {Roo.Element} this
8110          */
8111          setHeight : function(height, animate){
8112             height = this.adjustHeight(height);
8113             if(!animate || !A){
8114                 this.dom.style.height = this.addUnits(height);
8115             }else{
8116                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8117             }
8118             return this;
8119         },
8120
8121         /**
8122          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8123          * @param {Number} width The new width
8124          * @param {Number} height The new height
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          setSize : function(width, height, animate){
8129             if(typeof width == "object"){ // in case of object from getSize()
8130                 height = width.height; width = width.width;
8131             }
8132             width = this.adjustWidth(width); height = this.adjustHeight(height);
8133             if(!animate || !A){
8134                 this.dom.style.width = this.addUnits(width);
8135                 this.dom.style.height = this.addUnits(height);
8136             }else{
8137                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8138             }
8139             return this;
8140         },
8141
8142         /**
8143          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8144          * @param {Number} x X value for new position (coordinates are page-based)
8145          * @param {Number} y Y value for new position (coordinates are page-based)
8146          * @param {Number} width The new width
8147          * @param {Number} height The new height
8148          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8149          * @return {Roo.Element} this
8150          */
8151         setBounds : function(x, y, width, height, animate){
8152             if(!animate || !A){
8153                 this.setSize(width, height);
8154                 this.setLocation(x, y);
8155             }else{
8156                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8157                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8158                               this.preanim(arguments, 4), 'motion');
8159             }
8160             return this;
8161         },
8162
8163         /**
8164          * 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.
8165          * @param {Roo.lib.Region} region The region to fill
8166          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8167          * @return {Roo.Element} this
8168          */
8169         setRegion : function(region, animate){
8170             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8171             return this;
8172         },
8173
8174         /**
8175          * Appends an event handler
8176          *
8177          * @param {String}   eventName     The type of event to append
8178          * @param {Function} fn        The method the event invokes
8179          * @param {Object} scope       (optional) The scope (this object) of the fn
8180          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8181          */
8182         addListener : function(eventName, fn, scope, options){
8183             if (this.dom) {
8184                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8185             }
8186         },
8187
8188         /**
8189          * Removes an event handler from this element
8190          * @param {String} eventName the type of event to remove
8191          * @param {Function} fn the method the event invokes
8192          * @return {Roo.Element} this
8193          */
8194         removeListener : function(eventName, fn){
8195             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8196             return this;
8197         },
8198
8199         /**
8200          * Removes all previous added listeners from this element
8201          * @return {Roo.Element} this
8202          */
8203         removeAllListeners : function(){
8204             E.purgeElement(this.dom);
8205             return this;
8206         },
8207
8208         relayEvent : function(eventName, observable){
8209             this.on(eventName, function(e){
8210                 observable.fireEvent(eventName, e);
8211             });
8212         },
8213
8214         /**
8215          * Set the opacity of the element
8216          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8217          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8218          * @return {Roo.Element} this
8219          */
8220          setOpacity : function(opacity, animate){
8221             if(!animate || !A){
8222                 var s = this.dom.style;
8223                 if(Roo.isIE){
8224                     s.zoom = 1;
8225                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8226                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8227                 }else{
8228                     s.opacity = opacity;
8229                 }
8230             }else{
8231                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8232             }
8233             return this;
8234         },
8235
8236         /**
8237          * Gets the left X coordinate
8238          * @param {Boolean} local True to get the local css position instead of page coordinate
8239          * @return {Number}
8240          */
8241         getLeft : function(local){
8242             if(!local){
8243                 return this.getX();
8244             }else{
8245                 return parseInt(this.getStyle("left"), 10) || 0;
8246             }
8247         },
8248
8249         /**
8250          * Gets the right X coordinate of the element (element X position + element width)
8251          * @param {Boolean} local True to get the local css position instead of page coordinate
8252          * @return {Number}
8253          */
8254         getRight : function(local){
8255             if(!local){
8256                 return this.getX() + this.getWidth();
8257             }else{
8258                 return (this.getLeft(true) + this.getWidth()) || 0;
8259             }
8260         },
8261
8262         /**
8263          * Gets the top Y coordinate
8264          * @param {Boolean} local True to get the local css position instead of page coordinate
8265          * @return {Number}
8266          */
8267         getTop : function(local) {
8268             if(!local){
8269                 return this.getY();
8270             }else{
8271                 return parseInt(this.getStyle("top"), 10) || 0;
8272             }
8273         },
8274
8275         /**
8276          * Gets the bottom Y coordinate of the element (element Y position + element height)
8277          * @param {Boolean} local True to get the local css position instead of page coordinate
8278          * @return {Number}
8279          */
8280         getBottom : function(local){
8281             if(!local){
8282                 return this.getY() + this.getHeight();
8283             }else{
8284                 return (this.getTop(true) + this.getHeight()) || 0;
8285             }
8286         },
8287
8288         /**
8289         * Initializes positioning on this element. If a desired position is not passed, it will make the
8290         * the element positioned relative IF it is not already positioned.
8291         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8292         * @param {Number} zIndex (optional) The zIndex to apply
8293         * @param {Number} x (optional) Set the page X position
8294         * @param {Number} y (optional) Set the page Y position
8295         */
8296         position : function(pos, zIndex, x, y){
8297             if(!pos){
8298                if(this.getStyle('position') == 'static'){
8299                    this.setStyle('position', 'relative');
8300                }
8301             }else{
8302                 this.setStyle("position", pos);
8303             }
8304             if(zIndex){
8305                 this.setStyle("z-index", zIndex);
8306             }
8307             if(x !== undefined && y !== undefined){
8308                 this.setXY([x, y]);
8309             }else if(x !== undefined){
8310                 this.setX(x);
8311             }else if(y !== undefined){
8312                 this.setY(y);
8313             }
8314         },
8315
8316         /**
8317         * Clear positioning back to the default when the document was loaded
8318         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8319         * @return {Roo.Element} this
8320          */
8321         clearPositioning : function(value){
8322             value = value ||'';
8323             this.setStyle({
8324                 "left": value,
8325                 "right": value,
8326                 "top": value,
8327                 "bottom": value,
8328                 "z-index": "",
8329                 "position" : "static"
8330             });
8331             return this;
8332         },
8333
8334         /**
8335         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8336         * snapshot before performing an update and then restoring the element.
8337         * @return {Object}
8338         */
8339         getPositioning : function(){
8340             var l = this.getStyle("left");
8341             var t = this.getStyle("top");
8342             return {
8343                 "position" : this.getStyle("position"),
8344                 "left" : l,
8345                 "right" : l ? "" : this.getStyle("right"),
8346                 "top" : t,
8347                 "bottom" : t ? "" : this.getStyle("bottom"),
8348                 "z-index" : this.getStyle("z-index")
8349             };
8350         },
8351
8352         /**
8353          * Gets the width of the border(s) for the specified side(s)
8354          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8355          * passing lr would get the border (l)eft width + the border (r)ight width.
8356          * @return {Number} The width of the sides passed added together
8357          */
8358         getBorderWidth : function(side){
8359             return this.addStyles(side, El.borders);
8360         },
8361
8362         /**
8363          * Gets the width of the padding(s) for the specified side(s)
8364          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8365          * passing lr would get the padding (l)eft + the padding (r)ight.
8366          * @return {Number} The padding of the sides passed added together
8367          */
8368         getPadding : function(side){
8369             return this.addStyles(side, El.paddings);
8370         },
8371
8372         /**
8373         * Set positioning with an object returned by getPositioning().
8374         * @param {Object} posCfg
8375         * @return {Roo.Element} this
8376          */
8377         setPositioning : function(pc){
8378             this.applyStyles(pc);
8379             if(pc.right == "auto"){
8380                 this.dom.style.right = "";
8381             }
8382             if(pc.bottom == "auto"){
8383                 this.dom.style.bottom = "";
8384             }
8385             return this;
8386         },
8387
8388         // private
8389         fixDisplay : function(){
8390             if(this.getStyle("display") == "none"){
8391                 this.setStyle("visibility", "hidden");
8392                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8393                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8394                     this.setStyle("display", "block");
8395                 }
8396             }
8397         },
8398
8399         /**
8400          * Quick set left and top adding default units
8401          * @param {String} left The left CSS property value
8402          * @param {String} top The top CSS property value
8403          * @return {Roo.Element} this
8404          */
8405          setLeftTop : function(left, top){
8406             this.dom.style.left = this.addUnits(left);
8407             this.dom.style.top = this.addUnits(top);
8408             return this;
8409         },
8410
8411         /**
8412          * Move this element relative to its current position.
8413          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8414          * @param {Number} distance How far to move the element in pixels
8415          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8416          * @return {Roo.Element} this
8417          */
8418          move : function(direction, distance, animate){
8419             var xy = this.getXY();
8420             direction = direction.toLowerCase();
8421             switch(direction){
8422                 case "l":
8423                 case "left":
8424                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8425                     break;
8426                case "r":
8427                case "right":
8428                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8429                     break;
8430                case "t":
8431                case "top":
8432                case "up":
8433                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8434                     break;
8435                case "b":
8436                case "bottom":
8437                case "down":
8438                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8439                     break;
8440             }
8441             return this;
8442         },
8443
8444         /**
8445          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8446          * @return {Roo.Element} this
8447          */
8448         clip : function(){
8449             if(!this.isClipped){
8450                this.isClipped = true;
8451                this.originalClip = {
8452                    "o": this.getStyle("overflow"),
8453                    "x": this.getStyle("overflow-x"),
8454                    "y": this.getStyle("overflow-y")
8455                };
8456                this.setStyle("overflow", "hidden");
8457                this.setStyle("overflow-x", "hidden");
8458                this.setStyle("overflow-y", "hidden");
8459             }
8460             return this;
8461         },
8462
8463         /**
8464          *  Return clipping (overflow) to original clipping before clip() was called
8465          * @return {Roo.Element} this
8466          */
8467         unclip : function(){
8468             if(this.isClipped){
8469                 this.isClipped = false;
8470                 var o = this.originalClip;
8471                 if(o.o){this.setStyle("overflow", o.o);}
8472                 if(o.x){this.setStyle("overflow-x", o.x);}
8473                 if(o.y){this.setStyle("overflow-y", o.y);}
8474             }
8475             return this;
8476         },
8477
8478
8479         /**
8480          * Gets the x,y coordinates specified by the anchor position on the element.
8481          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8482          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8483          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8484          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8485          * @return {Array} [x, y] An array containing the element's x and y coordinates
8486          */
8487         getAnchorXY : function(anchor, local, s){
8488             //Passing a different size is useful for pre-calculating anchors,
8489             //especially for anchored animations that change the el size.
8490
8491             var w, h, vp = false;
8492             if(!s){
8493                 var d = this.dom;
8494                 if(d == document.body || d == document){
8495                     vp = true;
8496                     w = D.getViewWidth(); h = D.getViewHeight();
8497                 }else{
8498                     w = this.getWidth(); h = this.getHeight();
8499                 }
8500             }else{
8501                 w = s.width;  h = s.height;
8502             }
8503             var x = 0, y = 0, r = Math.round;
8504             switch((anchor || "tl").toLowerCase()){
8505                 case "c":
8506                     x = r(w*.5);
8507                     y = r(h*.5);
8508                 break;
8509                 case "t":
8510                     x = r(w*.5);
8511                     y = 0;
8512                 break;
8513                 case "l":
8514                     x = 0;
8515                     y = r(h*.5);
8516                 break;
8517                 case "r":
8518                     x = w;
8519                     y = r(h*.5);
8520                 break;
8521                 case "b":
8522                     x = r(w*.5);
8523                     y = h;
8524                 break;
8525                 case "tl":
8526                     x = 0;
8527                     y = 0;
8528                 break;
8529                 case "bl":
8530                     x = 0;
8531                     y = h;
8532                 break;
8533                 case "br":
8534                     x = w;
8535                     y = h;
8536                 break;
8537                 case "tr":
8538                     x = w;
8539                     y = 0;
8540                 break;
8541             }
8542             if(local === true){
8543                 return [x, y];
8544             }
8545             if(vp){
8546                 var sc = this.getScroll();
8547                 return [x + sc.left, y + sc.top];
8548             }
8549             //Add the element's offset xy
8550             var o = this.getXY();
8551             return [x+o[0], y+o[1]];
8552         },
8553
8554         /**
8555          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8556          * supported position values.
8557          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8558          * @param {String} position The position to align to.
8559          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8560          * @return {Array} [x, y]
8561          */
8562         getAlignToXY : function(el, p, o){
8563             el = Roo.get(el);
8564             var d = this.dom;
8565             if(!el.dom){
8566                 throw "Element.alignTo with an element that doesn't exist";
8567             }
8568             var c = false; //constrain to viewport
8569             var p1 = "", p2 = "";
8570             o = o || [0,0];
8571
8572             if(!p){
8573                 p = "tl-bl";
8574             }else if(p == "?"){
8575                 p = "tl-bl?";
8576             }else if(p.indexOf("-") == -1){
8577                 p = "tl-" + p;
8578             }
8579             p = p.toLowerCase();
8580             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8581             if(!m){
8582                throw "Element.alignTo with an invalid alignment " + p;
8583             }
8584             p1 = m[1]; p2 = m[2]; c = !!m[3];
8585
8586             //Subtract the aligned el's internal xy from the target's offset xy
8587             //plus custom offset to get the aligned el's new offset xy
8588             var a1 = this.getAnchorXY(p1, true);
8589             var a2 = el.getAnchorXY(p2, false);
8590             var x = a2[0] - a1[0] + o[0];
8591             var y = a2[1] - a1[1] + o[1];
8592             if(c){
8593                 //constrain the aligned el to viewport if necessary
8594                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8595                 // 5px of margin for ie
8596                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8597
8598                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8599                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8600                 //otherwise swap the aligned el to the opposite border of the target.
8601                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8602                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8603                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8604                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8605
8606                var doc = document;
8607                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8608                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8609
8610                if((x+w) > dw + scrollX){
8611                     x = swapX ? r.left-w : dw+scrollX-w;
8612                 }
8613                if(x < scrollX){
8614                    x = swapX ? r.right : scrollX;
8615                }
8616                if((y+h) > dh + scrollY){
8617                     y = swapY ? r.top-h : dh+scrollY-h;
8618                 }
8619                if (y < scrollY){
8620                    y = swapY ? r.bottom : scrollY;
8621                }
8622             }
8623             return [x,y];
8624         },
8625
8626         // private
8627         getConstrainToXY : function(){
8628             var os = {top:0, left:0, bottom:0, right: 0};
8629
8630             return function(el, local, offsets, proposedXY){
8631                 el = Roo.get(el);
8632                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8633
8634                 var vw, vh, vx = 0, vy = 0;
8635                 if(el.dom == document.body || el.dom == document){
8636                     vw = Roo.lib.Dom.getViewWidth();
8637                     vh = Roo.lib.Dom.getViewHeight();
8638                 }else{
8639                     vw = el.dom.clientWidth;
8640                     vh = el.dom.clientHeight;
8641                     if(!local){
8642                         var vxy = el.getXY();
8643                         vx = vxy[0];
8644                         vy = vxy[1];
8645                     }
8646                 }
8647
8648                 var s = el.getScroll();
8649
8650                 vx += offsets.left + s.left;
8651                 vy += offsets.top + s.top;
8652
8653                 vw -= offsets.right;
8654                 vh -= offsets.bottom;
8655
8656                 var vr = vx+vw;
8657                 var vb = vy+vh;
8658
8659                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8660                 var x = xy[0], y = xy[1];
8661                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8662
8663                 // only move it if it needs it
8664                 var moved = false;
8665
8666                 // first validate right/bottom
8667                 if((x + w) > vr){
8668                     x = vr - w;
8669                     moved = true;
8670                 }
8671                 if((y + h) > vb){
8672                     y = vb - h;
8673                     moved = true;
8674                 }
8675                 // then make sure top/left isn't negative
8676                 if(x < vx){
8677                     x = vx;
8678                     moved = true;
8679                 }
8680                 if(y < vy){
8681                     y = vy;
8682                     moved = true;
8683                 }
8684                 return moved ? [x, y] : false;
8685             };
8686         }(),
8687
8688         // private
8689         adjustForConstraints : function(xy, parent, offsets){
8690             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8691         },
8692
8693         /**
8694          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8695          * document it aligns it to the viewport.
8696          * The position parameter is optional, and can be specified in any one of the following formats:
8697          * <ul>
8698          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8699          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8700          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8701          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8702          *   <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
8703          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8704          * </ul>
8705          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8706          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8707          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8708          * that specified in order to enforce the viewport constraints.
8709          * Following are all of the supported anchor positions:
8710     <pre>
8711     Value  Description
8712     -----  -----------------------------
8713     tl     The top left corner (default)
8714     t      The center of the top edge
8715     tr     The top right corner
8716     l      The center of the left edge
8717     c      In the center of the element
8718     r      The center of the right edge
8719     bl     The bottom left corner
8720     b      The center of the bottom edge
8721     br     The bottom right corner
8722     </pre>
8723     Example Usage:
8724     <pre><code>
8725     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8726     el.alignTo("other-el");
8727
8728     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8729     el.alignTo("other-el", "tr?");
8730
8731     // align the bottom right corner of el with the center left edge of other-el
8732     el.alignTo("other-el", "br-l?");
8733
8734     // align the center of el with the bottom left corner of other-el and
8735     // adjust the x position by -6 pixels (and the y position by 0)
8736     el.alignTo("other-el", "c-bl", [-6, 0]);
8737     </code></pre>
8738          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8739          * @param {String} position The position to align to.
8740          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8741          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         alignTo : function(element, position, offsets, animate){
8745             var xy = this.getAlignToXY(element, position, offsets);
8746             this.setXY(xy, this.preanim(arguments, 3));
8747             return this;
8748         },
8749
8750         /**
8751          * Anchors an element to another element and realigns it when the window is resized.
8752          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8753          * @param {String} position The position to align to.
8754          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8755          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8756          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8757          * is a number, it is used as the buffer delay (defaults to 50ms).
8758          * @param {Function} callback The function to call after the animation finishes
8759          * @return {Roo.Element} this
8760          */
8761         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8762             var action = function(){
8763                 this.alignTo(el, alignment, offsets, animate);
8764                 Roo.callback(callback, this);
8765             };
8766             Roo.EventManager.onWindowResize(action, this);
8767             var tm = typeof monitorScroll;
8768             if(tm != 'undefined'){
8769                 Roo.EventManager.on(window, 'scroll', action, this,
8770                     {buffer: tm == 'number' ? monitorScroll : 50});
8771             }
8772             action.call(this); // align immediately
8773             return this;
8774         },
8775         /**
8776          * Clears any opacity settings from this element. Required in some cases for IE.
8777          * @return {Roo.Element} this
8778          */
8779         clearOpacity : function(){
8780             if (window.ActiveXObject) {
8781                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8782                     this.dom.style.filter = "";
8783                 }
8784             } else {
8785                 this.dom.style.opacity = "";
8786                 this.dom.style["-moz-opacity"] = "";
8787                 this.dom.style["-khtml-opacity"] = "";
8788             }
8789             return this;
8790         },
8791
8792         /**
8793          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8794          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8795          * @return {Roo.Element} this
8796          */
8797         hide : function(animate){
8798             this.setVisible(false, this.preanim(arguments, 0));
8799             return this;
8800         },
8801
8802         /**
8803         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8804         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8805          * @return {Roo.Element} this
8806          */
8807         show : function(animate){
8808             this.setVisible(true, this.preanim(arguments, 0));
8809             return this;
8810         },
8811
8812         /**
8813          * @private Test if size has a unit, otherwise appends the default
8814          */
8815         addUnits : function(size){
8816             return Roo.Element.addUnits(size, this.defaultUnit);
8817         },
8818
8819         /**
8820          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8821          * @return {Roo.Element} this
8822          */
8823         beginMeasure : function(){
8824             var el = this.dom;
8825             if(el.offsetWidth || el.offsetHeight){
8826                 return this; // offsets work already
8827             }
8828             var changed = [];
8829             var p = this.dom, b = document.body; // start with this element
8830             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8831                 var pe = Roo.get(p);
8832                 if(pe.getStyle('display') == 'none'){
8833                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8834                     p.style.visibility = "hidden";
8835                     p.style.display = "block";
8836                 }
8837                 p = p.parentNode;
8838             }
8839             this._measureChanged = changed;
8840             return this;
8841
8842         },
8843
8844         /**
8845          * Restores displays to before beginMeasure was called
8846          * @return {Roo.Element} this
8847          */
8848         endMeasure : function(){
8849             var changed = this._measureChanged;
8850             if(changed){
8851                 for(var i = 0, len = changed.length; i < len; i++) {
8852                     var r = changed[i];
8853                     r.el.style.visibility = r.visibility;
8854                     r.el.style.display = "none";
8855                 }
8856                 this._measureChanged = null;
8857             }
8858             return this;
8859         },
8860
8861         /**
8862         * Update the innerHTML of this element, optionally searching for and processing scripts
8863         * @param {String} html The new HTML
8864         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8865         * @param {Function} callback For async script loading you can be noticed when the update completes
8866         * @return {Roo.Element} this
8867          */
8868         update : function(html, loadScripts, callback){
8869             if(typeof html == "undefined"){
8870                 html = "";
8871             }
8872             if(loadScripts !== true){
8873                 this.dom.innerHTML = html;
8874                 if(typeof callback == "function"){
8875                     callback();
8876                 }
8877                 return this;
8878             }
8879             var id = Roo.id();
8880             var dom = this.dom;
8881
8882             html += '<span id="' + id + '"></span>';
8883
8884             E.onAvailable(id, function(){
8885                 var hd = document.getElementsByTagName("head")[0];
8886                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8887                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8888                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8889
8890                 var match;
8891                 while(match = re.exec(html)){
8892                     var attrs = match[1];
8893                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8894                     if(srcMatch && srcMatch[2]){
8895                        var s = document.createElement("script");
8896                        s.src = srcMatch[2];
8897                        var typeMatch = attrs.match(typeRe);
8898                        if(typeMatch && typeMatch[2]){
8899                            s.type = typeMatch[2];
8900                        }
8901                        hd.appendChild(s);
8902                     }else if(match[2] && match[2].length > 0){
8903                         if(window.execScript) {
8904                            window.execScript(match[2]);
8905                         } else {
8906                             /**
8907                              * eval:var:id
8908                              * eval:var:dom
8909                              * eval:var:html
8910                              * 
8911                              */
8912                            window.eval(match[2]);
8913                         }
8914                     }
8915                 }
8916                 var el = document.getElementById(id);
8917                 if(el){el.parentNode.removeChild(el);}
8918                 if(typeof callback == "function"){
8919                     callback();
8920                 }
8921             });
8922             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8923             return this;
8924         },
8925
8926         /**
8927          * Direct access to the UpdateManager update() method (takes the same parameters).
8928          * @param {String/Function} url The url for this request or a function to call to get the url
8929          * @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}
8930          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8931          * @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.
8932          * @return {Roo.Element} this
8933          */
8934         load : function(){
8935             var um = this.getUpdateManager();
8936             um.update.apply(um, arguments);
8937             return this;
8938         },
8939
8940         /**
8941         * Gets this element's UpdateManager
8942         * @return {Roo.UpdateManager} The UpdateManager
8943         */
8944         getUpdateManager : function(){
8945             if(!this.updateManager){
8946                 this.updateManager = new Roo.UpdateManager(this);
8947             }
8948             return this.updateManager;
8949         },
8950
8951         /**
8952          * Disables text selection for this element (normalized across browsers)
8953          * @return {Roo.Element} this
8954          */
8955         unselectable : function(){
8956             this.dom.unselectable = "on";
8957             this.swallowEvent("selectstart", true);
8958             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8959             this.addClass("x-unselectable");
8960             return this;
8961         },
8962
8963         /**
8964         * Calculates the x, y to center this element on the screen
8965         * @return {Array} The x, y values [x, y]
8966         */
8967         getCenterXY : function(){
8968             return this.getAlignToXY(document, 'c-c');
8969         },
8970
8971         /**
8972         * Centers the Element in either the viewport, or another Element.
8973         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8974         */
8975         center : function(centerIn){
8976             this.alignTo(centerIn || document, 'c-c');
8977             return this;
8978         },
8979
8980         /**
8981          * Tests various css rules/browsers to determine if this element uses a border box
8982          * @return {Boolean}
8983          */
8984         isBorderBox : function(){
8985             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8986         },
8987
8988         /**
8989          * Return a box {x, y, width, height} that can be used to set another elements
8990          * size/location to match this element.
8991          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8992          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8993          * @return {Object} box An object in the format {x, y, width, height}
8994          */
8995         getBox : function(contentBox, local){
8996             var xy;
8997             if(!local){
8998                 xy = this.getXY();
8999             }else{
9000                 var left = parseInt(this.getStyle("left"), 10) || 0;
9001                 var top = parseInt(this.getStyle("top"), 10) || 0;
9002                 xy = [left, top];
9003             }
9004             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9005             if(!contentBox){
9006                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9007             }else{
9008                 var l = this.getBorderWidth("l")+this.getPadding("l");
9009                 var r = this.getBorderWidth("r")+this.getPadding("r");
9010                 var t = this.getBorderWidth("t")+this.getPadding("t");
9011                 var b = this.getBorderWidth("b")+this.getPadding("b");
9012                 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)};
9013             }
9014             bx.right = bx.x + bx.width;
9015             bx.bottom = bx.y + bx.height;
9016             return bx;
9017         },
9018
9019         /**
9020          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9021          for more information about the sides.
9022          * @param {String} sides
9023          * @return {Number}
9024          */
9025         getFrameWidth : function(sides, onlyContentBox){
9026             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9027         },
9028
9029         /**
9030          * 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.
9031          * @param {Object} box The box to fill {x, y, width, height}
9032          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9033          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9034          * @return {Roo.Element} this
9035          */
9036         setBox : function(box, adjust, animate){
9037             var w = box.width, h = box.height;
9038             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9039                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9040                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9041             }
9042             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9043             return this;
9044         },
9045
9046         /**
9047          * Forces the browser to repaint this element
9048          * @return {Roo.Element} this
9049          */
9050          repaint : function(){
9051             var dom = this.dom;
9052             this.addClass("x-repaint");
9053             setTimeout(function(){
9054                 Roo.get(dom).removeClass("x-repaint");
9055             }, 1);
9056             return this;
9057         },
9058
9059         /**
9060          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9061          * then it returns the calculated width of the sides (see getPadding)
9062          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9063          * @return {Object/Number}
9064          */
9065         getMargins : function(side){
9066             if(!side){
9067                 return {
9068                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9069                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9070                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9071                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9072                 };
9073             }else{
9074                 return this.addStyles(side, El.margins);
9075              }
9076         },
9077
9078         // private
9079         addStyles : function(sides, styles){
9080             var val = 0, v, w;
9081             for(var i = 0, len = sides.length; i < len; i++){
9082                 v = this.getStyle(styles[sides.charAt(i)]);
9083                 if(v){
9084                      w = parseInt(v, 10);
9085                      if(w){ val += w; }
9086                 }
9087             }
9088             return val;
9089         },
9090
9091         /**
9092          * Creates a proxy element of this element
9093          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9094          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9095          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9096          * @return {Roo.Element} The new proxy element
9097          */
9098         createProxy : function(config, renderTo, matchBox){
9099             if(renderTo){
9100                 renderTo = Roo.getDom(renderTo);
9101             }else{
9102                 renderTo = document.body;
9103             }
9104             config = typeof config == "object" ?
9105                 config : {tag : "div", cls: config};
9106             var proxy = Roo.DomHelper.append(renderTo, config, true);
9107             if(matchBox){
9108                proxy.setBox(this.getBox());
9109             }
9110             return proxy;
9111         },
9112
9113         /**
9114          * Puts a mask over this element to disable user interaction. Requires core.css.
9115          * This method can only be applied to elements which accept child nodes.
9116          * @param {String} msg (optional) A message to display in the mask
9117          * @param {String} msgCls (optional) A css class to apply to the msg element
9118          * @return {Element} The mask  element
9119          */
9120         mask : function(msg, msgCls)
9121         {
9122             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9123                 this.setStyle("position", "relative");
9124             }
9125             if(!this._mask){
9126                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9127             }
9128             this.addClass("x-masked");
9129             this._mask.setDisplayed(true);
9130             
9131             // we wander
9132             var z = 0;
9133             var dom = this.dom;
9134             while (dom && dom.style) {
9135                 if (!isNaN(parseInt(dom.style.zIndex))) {
9136                     z = Math.max(z, parseInt(dom.style.zIndex));
9137                 }
9138                 dom = dom.parentNode;
9139             }
9140             // if we are masking the body - then it hides everything..
9141             if (this.dom == document.body) {
9142                 z = 1000000;
9143                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9144                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9145             }
9146            
9147             if(typeof msg == 'string'){
9148                 if(!this._maskMsg){
9149                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9150                         cls: "roo-el-mask-msg", 
9151                         cn: [
9152                             {
9153                                 tag: 'i',
9154                                 cls: 'fa fa-spinner fa-spin'
9155                             },
9156                             {
9157                                 tag: 'div'
9158                             }   
9159                         ]
9160                     }, true);
9161                 }
9162                 var mm = this._maskMsg;
9163                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9164                 if (mm.dom.lastChild) { // weird IE issue?
9165                     mm.dom.lastChild.innerHTML = msg;
9166                 }
9167                 mm.setDisplayed(true);
9168                 mm.center(this);
9169                 mm.setStyle('z-index', z + 102);
9170             }
9171             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9172                 this._mask.setHeight(this.getHeight());
9173             }
9174             this._mask.setStyle('z-index', z + 100);
9175             
9176             return this._mask;
9177         },
9178
9179         /**
9180          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9181          * it is cached for reuse.
9182          */
9183         unmask : function(removeEl){
9184             if(this._mask){
9185                 if(removeEl === true){
9186                     this._mask.remove();
9187                     delete this._mask;
9188                     if(this._maskMsg){
9189                         this._maskMsg.remove();
9190                         delete this._maskMsg;
9191                     }
9192                 }else{
9193                     this._mask.setDisplayed(false);
9194                     if(this._maskMsg){
9195                         this._maskMsg.setDisplayed(false);
9196                     }
9197                 }
9198             }
9199             this.removeClass("x-masked");
9200         },
9201
9202         /**
9203          * Returns true if this element is masked
9204          * @return {Boolean}
9205          */
9206         isMasked : function(){
9207             return this._mask && this._mask.isVisible();
9208         },
9209
9210         /**
9211          * Creates an iframe shim for this element to keep selects and other windowed objects from
9212          * showing through.
9213          * @return {Roo.Element} The new shim element
9214          */
9215         createShim : function(){
9216             var el = document.createElement('iframe');
9217             el.frameBorder = 'no';
9218             el.className = 'roo-shim';
9219             if(Roo.isIE && Roo.isSecure){
9220                 el.src = Roo.SSL_SECURE_URL;
9221             }
9222             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9223             shim.autoBoxAdjust = false;
9224             return shim;
9225         },
9226
9227         /**
9228          * Removes this element from the DOM and deletes it from the cache
9229          */
9230         remove : function(){
9231             if(this.dom.parentNode){
9232                 this.dom.parentNode.removeChild(this.dom);
9233             }
9234             delete El.cache[this.dom.id];
9235         },
9236
9237         /**
9238          * Sets up event handlers to add and remove a css class when the mouse is over this element
9239          * @param {String} className
9240          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9241          * mouseout events for children elements
9242          * @return {Roo.Element} this
9243          */
9244         addClassOnOver : function(className, preventFlicker){
9245             this.on("mouseover", function(){
9246                 Roo.fly(this, '_internal').addClass(className);
9247             }, this.dom);
9248             var removeFn = function(e){
9249                 if(preventFlicker !== true || !e.within(this, true)){
9250                     Roo.fly(this, '_internal').removeClass(className);
9251                 }
9252             };
9253             this.on("mouseout", removeFn, this.dom);
9254             return this;
9255         },
9256
9257         /**
9258          * Sets up event handlers to add and remove a css class when this element has the focus
9259          * @param {String} className
9260          * @return {Roo.Element} this
9261          */
9262         addClassOnFocus : function(className){
9263             this.on("focus", function(){
9264                 Roo.fly(this, '_internal').addClass(className);
9265             }, this.dom);
9266             this.on("blur", function(){
9267                 Roo.fly(this, '_internal').removeClass(className);
9268             }, this.dom);
9269             return this;
9270         },
9271         /**
9272          * 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)
9273          * @param {String} className
9274          * @return {Roo.Element} this
9275          */
9276         addClassOnClick : function(className){
9277             var dom = this.dom;
9278             this.on("mousedown", function(){
9279                 Roo.fly(dom, '_internal').addClass(className);
9280                 var d = Roo.get(document);
9281                 var fn = function(){
9282                     Roo.fly(dom, '_internal').removeClass(className);
9283                     d.removeListener("mouseup", fn);
9284                 };
9285                 d.on("mouseup", fn);
9286             });
9287             return this;
9288         },
9289
9290         /**
9291          * Stops the specified event from bubbling and optionally prevents the default action
9292          * @param {String} eventName
9293          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9294          * @return {Roo.Element} this
9295          */
9296         swallowEvent : function(eventName, preventDefault){
9297             var fn = function(e){
9298                 e.stopPropagation();
9299                 if(preventDefault){
9300                     e.preventDefault();
9301                 }
9302             };
9303             if(eventName instanceof Array){
9304                 for(var i = 0, len = eventName.length; i < len; i++){
9305                      this.on(eventName[i], fn);
9306                 }
9307                 return this;
9308             }
9309             this.on(eventName, fn);
9310             return this;
9311         },
9312
9313         /**
9314          * @private
9315          */
9316       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9317
9318         /**
9319          * Sizes this element to its parent element's dimensions performing
9320          * neccessary box adjustments.
9321          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9322          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9323          * @return {Roo.Element} this
9324          */
9325         fitToParent : function(monitorResize, targetParent) {
9326           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9327           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9328           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9329             return;
9330           }
9331           var p = Roo.get(targetParent || this.dom.parentNode);
9332           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9333           if (monitorResize === true) {
9334             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9335             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9336           }
9337           return this;
9338         },
9339
9340         /**
9341          * Gets the next sibling, skipping text nodes
9342          * @return {HTMLElement} The next sibling or null
9343          */
9344         getNextSibling : function(){
9345             var n = this.dom.nextSibling;
9346             while(n && n.nodeType != 1){
9347                 n = n.nextSibling;
9348             }
9349             return n;
9350         },
9351
9352         /**
9353          * Gets the previous sibling, skipping text nodes
9354          * @return {HTMLElement} The previous sibling or null
9355          */
9356         getPrevSibling : function(){
9357             var n = this.dom.previousSibling;
9358             while(n && n.nodeType != 1){
9359                 n = n.previousSibling;
9360             }
9361             return n;
9362         },
9363
9364
9365         /**
9366          * Appends the passed element(s) to this element
9367          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9368          * @return {Roo.Element} this
9369          */
9370         appendChild: function(el){
9371             el = Roo.get(el);
9372             el.appendTo(this);
9373             return this;
9374         },
9375
9376         /**
9377          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9378          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9379          * automatically generated with the specified attributes.
9380          * @param {HTMLElement} insertBefore (optional) a child element of this element
9381          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9382          * @return {Roo.Element} The new child element
9383          */
9384         createChild: function(config, insertBefore, returnDom){
9385             config = config || {tag:'div'};
9386             if(insertBefore){
9387                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9388             }
9389             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9390         },
9391
9392         /**
9393          * Appends this element to the passed element
9394          * @param {String/HTMLElement/Element} el The new parent element
9395          * @return {Roo.Element} this
9396          */
9397         appendTo: function(el){
9398             el = Roo.getDom(el);
9399             el.appendChild(this.dom);
9400             return this;
9401         },
9402
9403         /**
9404          * Inserts this element before the passed element in the DOM
9405          * @param {String/HTMLElement/Element} el The element to insert before
9406          * @return {Roo.Element} this
9407          */
9408         insertBefore: function(el){
9409             el = Roo.getDom(el);
9410             el.parentNode.insertBefore(this.dom, el);
9411             return this;
9412         },
9413
9414         /**
9415          * Inserts this element after the passed element in the DOM
9416          * @param {String/HTMLElement/Element} el The element to insert after
9417          * @return {Roo.Element} this
9418          */
9419         insertAfter: function(el){
9420             el = Roo.getDom(el);
9421             el.parentNode.insertBefore(this.dom, el.nextSibling);
9422             return this;
9423         },
9424
9425         /**
9426          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9427          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9428          * @return {Roo.Element} The new child
9429          */
9430         insertFirst: function(el, returnDom){
9431             el = el || {};
9432             if(typeof el == 'object' && !el.nodeType){ // dh config
9433                 return this.createChild(el, this.dom.firstChild, returnDom);
9434             }else{
9435                 el = Roo.getDom(el);
9436                 this.dom.insertBefore(el, this.dom.firstChild);
9437                 return !returnDom ? Roo.get(el) : el;
9438             }
9439         },
9440
9441         /**
9442          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9443          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9444          * @param {String} where (optional) 'before' or 'after' defaults to before
9445          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9446          * @return {Roo.Element} the inserted Element
9447          */
9448         insertSibling: function(el, where, returnDom){
9449             where = where ? where.toLowerCase() : 'before';
9450             el = el || {};
9451             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9452
9453             if(typeof el == 'object' && !el.nodeType){ // dh config
9454                 if(where == 'after' && !this.dom.nextSibling){
9455                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9456                 }else{
9457                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9458                 }
9459
9460             }else{
9461                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9462                             where == 'before' ? this.dom : this.dom.nextSibling);
9463                 if(!returnDom){
9464                     rt = Roo.get(rt);
9465                 }
9466             }
9467             return rt;
9468         },
9469
9470         /**
9471          * Creates and wraps this element with another element
9472          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9473          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9474          * @return {HTMLElement/Element} The newly created wrapper element
9475          */
9476         wrap: function(config, returnDom){
9477             if(!config){
9478                 config = {tag: "div"};
9479             }
9480             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9481             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9482             return newEl;
9483         },
9484
9485         /**
9486          * Replaces the passed element with this element
9487          * @param {String/HTMLElement/Element} el The element to replace
9488          * @return {Roo.Element} this
9489          */
9490         replace: function(el){
9491             el = Roo.get(el);
9492             this.insertBefore(el);
9493             el.remove();
9494             return this;
9495         },
9496
9497         /**
9498          * Inserts an html fragment into this element
9499          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9500          * @param {String} html The HTML fragment
9501          * @param {Boolean} returnEl True to return an Roo.Element
9502          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9503          */
9504         insertHtml : function(where, html, returnEl){
9505             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9506             return returnEl ? Roo.get(el) : el;
9507         },
9508
9509         /**
9510          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9511          * @param {Object} o The object with the attributes
9512          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9513          * @return {Roo.Element} this
9514          */
9515         set : function(o, useSet){
9516             var el = this.dom;
9517             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9518             for(var attr in o){
9519                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9520                 if(attr=="cls"){
9521                     el.className = o["cls"];
9522                 }else{
9523                     if(useSet) {
9524                         el.setAttribute(attr, o[attr]);
9525                     } else {
9526                         el[attr] = o[attr];
9527                     }
9528                 }
9529             }
9530             if(o.style){
9531                 Roo.DomHelper.applyStyles(el, o.style);
9532             }
9533             return this;
9534         },
9535
9536         /**
9537          * Convenience method for constructing a KeyMap
9538          * @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:
9539          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9540          * @param {Function} fn The function to call
9541          * @param {Object} scope (optional) The scope of the function
9542          * @return {Roo.KeyMap} The KeyMap created
9543          */
9544         addKeyListener : function(key, fn, scope){
9545             var config;
9546             if(typeof key != "object" || key instanceof Array){
9547                 config = {
9548                     key: key,
9549                     fn: fn,
9550                     scope: scope
9551                 };
9552             }else{
9553                 config = {
9554                     key : key.key,
9555                     shift : key.shift,
9556                     ctrl : key.ctrl,
9557                     alt : key.alt,
9558                     fn: fn,
9559                     scope: scope
9560                 };
9561             }
9562             return new Roo.KeyMap(this, config);
9563         },
9564
9565         /**
9566          * Creates a KeyMap for this element
9567          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9568          * @return {Roo.KeyMap} The KeyMap created
9569          */
9570         addKeyMap : function(config){
9571             return new Roo.KeyMap(this, config);
9572         },
9573
9574         /**
9575          * Returns true if this element is scrollable.
9576          * @return {Boolean}
9577          */
9578          isScrollable : function(){
9579             var dom = this.dom;
9580             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9581         },
9582
9583         /**
9584          * 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().
9585          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9586          * @param {Number} value The new scroll value
9587          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9588          * @return {Element} this
9589          */
9590
9591         scrollTo : function(side, value, animate){
9592             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9593             if(!animate || !A){
9594                 this.dom[prop] = value;
9595             }else{
9596                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9597                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9598             }
9599             return this;
9600         },
9601
9602         /**
9603          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9604          * within this element's scrollable range.
9605          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9606          * @param {Number} distance How far to scroll the element in pixels
9607          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9608          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9609          * was scrolled as far as it could go.
9610          */
9611          scroll : function(direction, distance, animate){
9612              if(!this.isScrollable()){
9613                  return;
9614              }
9615              var el = this.dom;
9616              var l = el.scrollLeft, t = el.scrollTop;
9617              var w = el.scrollWidth, h = el.scrollHeight;
9618              var cw = el.clientWidth, ch = el.clientHeight;
9619              direction = direction.toLowerCase();
9620              var scrolled = false;
9621              var a = this.preanim(arguments, 2);
9622              switch(direction){
9623                  case "l":
9624                  case "left":
9625                      if(w - l > cw){
9626                          var v = Math.min(l + distance, w-cw);
9627                          this.scrollTo("left", v, a);
9628                          scrolled = true;
9629                      }
9630                      break;
9631                 case "r":
9632                 case "right":
9633                      if(l > 0){
9634                          var v = Math.max(l - distance, 0);
9635                          this.scrollTo("left", v, a);
9636                          scrolled = true;
9637                      }
9638                      break;
9639                 case "t":
9640                 case "top":
9641                 case "up":
9642                      if(t > 0){
9643                          var v = Math.max(t - distance, 0);
9644                          this.scrollTo("top", v, a);
9645                          scrolled = true;
9646                      }
9647                      break;
9648                 case "b":
9649                 case "bottom":
9650                 case "down":
9651                      if(h - t > ch){
9652                          var v = Math.min(t + distance, h-ch);
9653                          this.scrollTo("top", v, a);
9654                          scrolled = true;
9655                      }
9656                      break;
9657              }
9658              return scrolled;
9659         },
9660
9661         /**
9662          * Translates the passed page coordinates into left/top css values for this element
9663          * @param {Number/Array} x The page x or an array containing [x, y]
9664          * @param {Number} y The page y
9665          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9666          */
9667         translatePoints : function(x, y){
9668             if(typeof x == 'object' || x instanceof Array){
9669                 y = x[1]; x = x[0];
9670             }
9671             var p = this.getStyle('position');
9672             var o = this.getXY();
9673
9674             var l = parseInt(this.getStyle('left'), 10);
9675             var t = parseInt(this.getStyle('top'), 10);
9676
9677             if(isNaN(l)){
9678                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9679             }
9680             if(isNaN(t)){
9681                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9682             }
9683
9684             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9685         },
9686
9687         /**
9688          * Returns the current scroll position of the element.
9689          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9690          */
9691         getScroll : function(){
9692             var d = this.dom, doc = document;
9693             if(d == doc || d == doc.body){
9694                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9695                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9696                 return {left: l, top: t};
9697             }else{
9698                 return {left: d.scrollLeft, top: d.scrollTop};
9699             }
9700         },
9701
9702         /**
9703          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9704          * are convert to standard 6 digit hex color.
9705          * @param {String} attr The css attribute
9706          * @param {String} defaultValue The default value to use when a valid color isn't found
9707          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9708          * YUI color anims.
9709          */
9710         getColor : function(attr, defaultValue, prefix){
9711             var v = this.getStyle(attr);
9712             if(!v || v == "transparent" || v == "inherit") {
9713                 return defaultValue;
9714             }
9715             var color = typeof prefix == "undefined" ? "#" : prefix;
9716             if(v.substr(0, 4) == "rgb("){
9717                 var rvs = v.slice(4, v.length -1).split(",");
9718                 for(var i = 0; i < 3; i++){
9719                     var h = parseInt(rvs[i]).toString(16);
9720                     if(h < 16){
9721                         h = "0" + h;
9722                     }
9723                     color += h;
9724                 }
9725             } else {
9726                 if(v.substr(0, 1) == "#"){
9727                     if(v.length == 4) {
9728                         for(var i = 1; i < 4; i++){
9729                             var c = v.charAt(i);
9730                             color +=  c + c;
9731                         }
9732                     }else if(v.length == 7){
9733                         color += v.substr(1);
9734                     }
9735                 }
9736             }
9737             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9738         },
9739
9740         /**
9741          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9742          * gradient background, rounded corners and a 4-way shadow.
9743          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9744          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9745          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9746          * @return {Roo.Element} this
9747          */
9748         boxWrap : function(cls){
9749             cls = cls || 'x-box';
9750             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9751             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9752             return el;
9753         },
9754
9755         /**
9756          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9757          * @param {String} namespace The namespace in which to look for the attribute
9758          * @param {String} name The attribute name
9759          * @return {String} The attribute value
9760          */
9761         getAttributeNS : Roo.isIE ? function(ns, name){
9762             var d = this.dom;
9763             var type = typeof d[ns+":"+name];
9764             if(type != 'undefined' && type != 'unknown'){
9765                 return d[ns+":"+name];
9766             }
9767             return d[name];
9768         } : function(ns, name){
9769             var d = this.dom;
9770             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9771         },
9772         
9773         
9774         /**
9775          * Sets or Returns the value the dom attribute value
9776          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9777          * @param {String} value (optional) The value to set the attribute to
9778          * @return {String} The attribute value
9779          */
9780         attr : function(name){
9781             if (arguments.length > 1) {
9782                 this.dom.setAttribute(name, arguments[1]);
9783                 return arguments[1];
9784             }
9785             if (typeof(name) == 'object') {
9786                 for(var i in name) {
9787                     this.attr(i, name[i]);
9788                 }
9789                 return name;
9790             }
9791             
9792             
9793             if (!this.dom.hasAttribute(name)) {
9794                 return undefined;
9795             }
9796             return this.dom.getAttribute(name);
9797         }
9798         
9799         
9800         
9801     };
9802
9803     var ep = El.prototype;
9804
9805     /**
9806      * Appends an event handler (Shorthand for addListener)
9807      * @param {String}   eventName     The type of event to append
9808      * @param {Function} fn        The method the event invokes
9809      * @param {Object} scope       (optional) The scope (this object) of the fn
9810      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9811      * @method
9812      */
9813     ep.on = ep.addListener;
9814         // backwards compat
9815     ep.mon = ep.addListener;
9816
9817     /**
9818      * Removes an event handler from this element (shorthand for removeListener)
9819      * @param {String} eventName the type of event to remove
9820      * @param {Function} fn the method the event invokes
9821      * @return {Roo.Element} this
9822      * @method
9823      */
9824     ep.un = ep.removeListener;
9825
9826     /**
9827      * true to automatically adjust width and height settings for box-model issues (default to true)
9828      */
9829     ep.autoBoxAdjust = true;
9830
9831     // private
9832     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9833
9834     // private
9835     El.addUnits = function(v, defaultUnit){
9836         if(v === "" || v == "auto"){
9837             return v;
9838         }
9839         if(v === undefined){
9840             return '';
9841         }
9842         if(typeof v == "number" || !El.unitPattern.test(v)){
9843             return v + (defaultUnit || 'px');
9844         }
9845         return v;
9846     };
9847
9848     // special markup used throughout Roo when box wrapping elements
9849     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>';
9850     /**
9851      * Visibility mode constant - Use visibility to hide element
9852      * @static
9853      * @type Number
9854      */
9855     El.VISIBILITY = 1;
9856     /**
9857      * Visibility mode constant - Use display to hide element
9858      * @static
9859      * @type Number
9860      */
9861     El.DISPLAY = 2;
9862
9863     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9864     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9865     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9866
9867
9868
9869     /**
9870      * @private
9871      */
9872     El.cache = {};
9873
9874     var docEl;
9875
9876     /**
9877      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9878      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9879      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9880      * @return {Element} The Element object
9881      * @static
9882      */
9883     El.get = function(el){
9884         var ex, elm, id;
9885         if(!el){ return null; }
9886         if(typeof el == "string"){ // element id
9887             if(!(elm = document.getElementById(el))){
9888                 return null;
9889             }
9890             if(ex = El.cache[el]){
9891                 ex.dom = elm;
9892             }else{
9893                 ex = El.cache[el] = new El(elm);
9894             }
9895             return ex;
9896         }else if(el.tagName){ // dom element
9897             if(!(id = el.id)){
9898                 id = Roo.id(el);
9899             }
9900             if(ex = El.cache[id]){
9901                 ex.dom = el;
9902             }else{
9903                 ex = El.cache[id] = new El(el);
9904             }
9905             return ex;
9906         }else if(el instanceof El){
9907             if(el != docEl){
9908                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9909                                                               // catch case where it hasn't been appended
9910                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9911             }
9912             return el;
9913         }else if(el.isComposite){
9914             return el;
9915         }else if(el instanceof Array){
9916             return El.select(el);
9917         }else if(el == document){
9918             // create a bogus element object representing the document object
9919             if(!docEl){
9920                 var f = function(){};
9921                 f.prototype = El.prototype;
9922                 docEl = new f();
9923                 docEl.dom = document;
9924             }
9925             return docEl;
9926         }
9927         return null;
9928     };
9929
9930     // private
9931     El.uncache = function(el){
9932         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9933             if(a[i]){
9934                 delete El.cache[a[i].id || a[i]];
9935             }
9936         }
9937     };
9938
9939     // private
9940     // Garbage collection - uncache elements/purge listeners on orphaned elements
9941     // so we don't hold a reference and cause the browser to retain them
9942     El.garbageCollect = function(){
9943         if(!Roo.enableGarbageCollector){
9944             clearInterval(El.collectorThread);
9945             return;
9946         }
9947         for(var eid in El.cache){
9948             var el = El.cache[eid], d = el.dom;
9949             // -------------------------------------------------------
9950             // Determining what is garbage:
9951             // -------------------------------------------------------
9952             // !d
9953             // dom node is null, definitely garbage
9954             // -------------------------------------------------------
9955             // !d.parentNode
9956             // no parentNode == direct orphan, definitely garbage
9957             // -------------------------------------------------------
9958             // !d.offsetParent && !document.getElementById(eid)
9959             // display none elements have no offsetParent so we will
9960             // also try to look it up by it's id. However, check
9961             // offsetParent first so we don't do unneeded lookups.
9962             // This enables collection of elements that are not orphans
9963             // directly, but somewhere up the line they have an orphan
9964             // parent.
9965             // -------------------------------------------------------
9966             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9967                 delete El.cache[eid];
9968                 if(d && Roo.enableListenerCollection){
9969                     E.purgeElement(d);
9970                 }
9971             }
9972         }
9973     }
9974     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9975
9976
9977     // dom is optional
9978     El.Flyweight = function(dom){
9979         this.dom = dom;
9980     };
9981     El.Flyweight.prototype = El.prototype;
9982
9983     El._flyweights = {};
9984     /**
9985      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9986      * the dom node can be overwritten by other code.
9987      * @param {String/HTMLElement} el The dom node or id
9988      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9989      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9990      * @static
9991      * @return {Element} The shared Element object
9992      */
9993     El.fly = function(el, named){
9994         named = named || '_global';
9995         el = Roo.getDom(el);
9996         if(!el){
9997             return null;
9998         }
9999         if(!El._flyweights[named]){
10000             El._flyweights[named] = new El.Flyweight();
10001         }
10002         El._flyweights[named].dom = el;
10003         return El._flyweights[named];
10004     };
10005
10006     /**
10007      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10008      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10009      * Shorthand of {@link Roo.Element#get}
10010      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10011      * @return {Element} The Element object
10012      * @member Roo
10013      * @method get
10014      */
10015     Roo.get = El.get;
10016     /**
10017      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10018      * the dom node can be overwritten by other code.
10019      * Shorthand of {@link Roo.Element#fly}
10020      * @param {String/HTMLElement} el The dom node or id
10021      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10022      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10023      * @static
10024      * @return {Element} The shared Element object
10025      * @member Roo
10026      * @method fly
10027      */
10028     Roo.fly = El.fly;
10029
10030     // speedy lookup for elements never to box adjust
10031     var noBoxAdjust = Roo.isStrict ? {
10032         select:1
10033     } : {
10034         input:1, select:1, textarea:1
10035     };
10036     if(Roo.isIE || Roo.isGecko){
10037         noBoxAdjust['button'] = 1;
10038     }
10039
10040
10041     Roo.EventManager.on(window, 'unload', function(){
10042         delete El.cache;
10043         delete El._flyweights;
10044     });
10045 })();
10046
10047
10048
10049
10050 if(Roo.DomQuery){
10051     Roo.Element.selectorFunction = Roo.DomQuery.select;
10052 }
10053
10054 Roo.Element.select = function(selector, unique, root){
10055     var els;
10056     if(typeof selector == "string"){
10057         els = Roo.Element.selectorFunction(selector, root);
10058     }else if(selector.length !== undefined){
10059         els = selector;
10060     }else{
10061         throw "Invalid selector";
10062     }
10063     if(unique === true){
10064         return new Roo.CompositeElement(els);
10065     }else{
10066         return new Roo.CompositeElementLite(els);
10067     }
10068 };
10069 /**
10070  * Selects elements based on the passed CSS selector to enable working on them as 1.
10071  * @param {String/Array} selector The CSS selector or an array of elements
10072  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10073  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10074  * @return {CompositeElementLite/CompositeElement}
10075  * @member Roo
10076  * @method select
10077  */
10078 Roo.select = Roo.Element.select;
10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093 /*
10094  * Based on:
10095  * Ext JS Library 1.1.1
10096  * Copyright(c) 2006-2007, Ext JS, LLC.
10097  *
10098  * Originally Released Under LGPL - original licence link has changed is not relivant.
10099  *
10100  * Fork - LGPL
10101  * <script type="text/javascript">
10102  */
10103
10104
10105
10106 //Notifies Element that fx methods are available
10107 Roo.enableFx = true;
10108
10109 /**
10110  * @class Roo.Fx
10111  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10112  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10113  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10114  * Element effects to work.</p><br/>
10115  *
10116  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10117  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10118  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10119  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10120  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10121  * expected results and should be done with care.</p><br/>
10122  *
10123  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10124  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10125 <pre>
10126 Value  Description
10127 -----  -----------------------------
10128 tl     The top left corner
10129 t      The center of the top edge
10130 tr     The top right corner
10131 l      The center of the left edge
10132 r      The center of the right edge
10133 bl     The bottom left corner
10134 b      The center of the bottom edge
10135 br     The bottom right corner
10136 </pre>
10137  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10138  * below are common options that can be passed to any Fx method.</b>
10139  * @cfg {Function} callback A function called when the effect is finished
10140  * @cfg {Object} scope The scope of the effect function
10141  * @cfg {String} easing A valid Easing value for the effect
10142  * @cfg {String} afterCls A css class to apply after the effect
10143  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10144  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10145  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10146  * effects that end with the element being visually hidden, ignored otherwise)
10147  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10148  * a function which returns such a specification that will be applied to the Element after the effect finishes
10149  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10150  * @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
10151  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10152  */
10153 Roo.Fx = {
10154         /**
10155          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10156          * origin for the slide effect.  This function automatically handles wrapping the element with
10157          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10158          * Usage:
10159          *<pre><code>
10160 // default: slide the element in from the top
10161 el.slideIn();
10162
10163 // custom: slide the element in from the right with a 2-second duration
10164 el.slideIn('r', { duration: 2 });
10165
10166 // common config options shown with default values
10167 el.slideIn('t', {
10168     easing: 'easeOut',
10169     duration: .5
10170 });
10171 </code></pre>
10172          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10173          * @param {Object} options (optional) Object literal with any of the Fx config options
10174          * @return {Roo.Element} The Element
10175          */
10176     slideIn : function(anchor, o){
10177         var el = this.getFxEl();
10178         o = o || {};
10179
10180         el.queueFx(o, function(){
10181
10182             anchor = anchor || "t";
10183
10184             // fix display to visibility
10185             this.fixDisplay();
10186
10187             // restore values after effect
10188             var r = this.getFxRestore();
10189             var b = this.getBox();
10190             // fixed size for slide
10191             this.setSize(b);
10192
10193             // wrap if needed
10194             var wrap = this.fxWrap(r.pos, o, "hidden");
10195
10196             var st = this.dom.style;
10197             st.visibility = "visible";
10198             st.position = "absolute";
10199
10200             // clear out temp styles after slide and unwrap
10201             var after = function(){
10202                 el.fxUnwrap(wrap, r.pos, o);
10203                 st.width = r.width;
10204                 st.height = r.height;
10205                 el.afterFx(o);
10206             };
10207             // time to calc the positions
10208             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10209
10210             switch(anchor.toLowerCase()){
10211                 case "t":
10212                     wrap.setSize(b.width, 0);
10213                     st.left = st.bottom = "0";
10214                     a = {height: bh};
10215                 break;
10216                 case "l":
10217                     wrap.setSize(0, b.height);
10218                     st.right = st.top = "0";
10219                     a = {width: bw};
10220                 break;
10221                 case "r":
10222                     wrap.setSize(0, b.height);
10223                     wrap.setX(b.right);
10224                     st.left = st.top = "0";
10225                     a = {width: bw, points: pt};
10226                 break;
10227                 case "b":
10228                     wrap.setSize(b.width, 0);
10229                     wrap.setY(b.bottom);
10230                     st.left = st.top = "0";
10231                     a = {height: bh, points: pt};
10232                 break;
10233                 case "tl":
10234                     wrap.setSize(0, 0);
10235                     st.right = st.bottom = "0";
10236                     a = {width: bw, height: bh};
10237                 break;
10238                 case "bl":
10239                     wrap.setSize(0, 0);
10240                     wrap.setY(b.y+b.height);
10241                     st.right = st.top = "0";
10242                     a = {width: bw, height: bh, points: pt};
10243                 break;
10244                 case "br":
10245                     wrap.setSize(0, 0);
10246                     wrap.setXY([b.right, b.bottom]);
10247                     st.left = st.top = "0";
10248                     a = {width: bw, height: bh, points: pt};
10249                 break;
10250                 case "tr":
10251                     wrap.setSize(0, 0);
10252                     wrap.setX(b.x+b.width);
10253                     st.left = st.bottom = "0";
10254                     a = {width: bw, height: bh, points: pt};
10255                 break;
10256             }
10257             this.dom.style.visibility = "visible";
10258             wrap.show();
10259
10260             arguments.callee.anim = wrap.fxanim(a,
10261                 o,
10262                 'motion',
10263                 .5,
10264                 'easeOut', after);
10265         });
10266         return this;
10267     },
10268     
10269         /**
10270          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10271          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10272          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10273          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10274          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10275          * Usage:
10276          *<pre><code>
10277 // default: slide the element out to the top
10278 el.slideOut();
10279
10280 // custom: slide the element out to the right with a 2-second duration
10281 el.slideOut('r', { duration: 2 });
10282
10283 // common config options shown with default values
10284 el.slideOut('t', {
10285     easing: 'easeOut',
10286     duration: .5,
10287     remove: false,
10288     useDisplay: false
10289 });
10290 </code></pre>
10291          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10292          * @param {Object} options (optional) Object literal with any of the Fx config options
10293          * @return {Roo.Element} The Element
10294          */
10295     slideOut : function(anchor, o){
10296         var el = this.getFxEl();
10297         o = o || {};
10298
10299         el.queueFx(o, function(){
10300
10301             anchor = anchor || "t";
10302
10303             // restore values after effect
10304             var r = this.getFxRestore();
10305             
10306             var b = this.getBox();
10307             // fixed size for slide
10308             this.setSize(b);
10309
10310             // wrap if needed
10311             var wrap = this.fxWrap(r.pos, o, "visible");
10312
10313             var st = this.dom.style;
10314             st.visibility = "visible";
10315             st.position = "absolute";
10316
10317             wrap.setSize(b);
10318
10319             var after = function(){
10320                 if(o.useDisplay){
10321                     el.setDisplayed(false);
10322                 }else{
10323                     el.hide();
10324                 }
10325
10326                 el.fxUnwrap(wrap, r.pos, o);
10327
10328                 st.width = r.width;
10329                 st.height = r.height;
10330
10331                 el.afterFx(o);
10332             };
10333
10334             var a, zero = {to: 0};
10335             switch(anchor.toLowerCase()){
10336                 case "t":
10337                     st.left = st.bottom = "0";
10338                     a = {height: zero};
10339                 break;
10340                 case "l":
10341                     st.right = st.top = "0";
10342                     a = {width: zero};
10343                 break;
10344                 case "r":
10345                     st.left = st.top = "0";
10346                     a = {width: zero, points: {to:[b.right, b.y]}};
10347                 break;
10348                 case "b":
10349                     st.left = st.top = "0";
10350                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10351                 break;
10352                 case "tl":
10353                     st.right = st.bottom = "0";
10354                     a = {width: zero, height: zero};
10355                 break;
10356                 case "bl":
10357                     st.right = st.top = "0";
10358                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10359                 break;
10360                 case "br":
10361                     st.left = st.top = "0";
10362                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10363                 break;
10364                 case "tr":
10365                     st.left = st.bottom = "0";
10366                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10367                 break;
10368             }
10369
10370             arguments.callee.anim = wrap.fxanim(a,
10371                 o,
10372                 'motion',
10373                 .5,
10374                 "easeOut", after);
10375         });
10376         return this;
10377     },
10378
10379         /**
10380          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10381          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10382          * The element must be removed from the DOM using the 'remove' config option if desired.
10383          * Usage:
10384          *<pre><code>
10385 // default
10386 el.puff();
10387
10388 // common config options shown with default values
10389 el.puff({
10390     easing: 'easeOut',
10391     duration: .5,
10392     remove: false,
10393     useDisplay: false
10394 });
10395 </code></pre>
10396          * @param {Object} options (optional) Object literal with any of the Fx config options
10397          * @return {Roo.Element} The Element
10398          */
10399     puff : function(o){
10400         var el = this.getFxEl();
10401         o = o || {};
10402
10403         el.queueFx(o, function(){
10404             this.clearOpacity();
10405             this.show();
10406
10407             // restore values after effect
10408             var r = this.getFxRestore();
10409             var st = this.dom.style;
10410
10411             var after = function(){
10412                 if(o.useDisplay){
10413                     el.setDisplayed(false);
10414                 }else{
10415                     el.hide();
10416                 }
10417
10418                 el.clearOpacity();
10419
10420                 el.setPositioning(r.pos);
10421                 st.width = r.width;
10422                 st.height = r.height;
10423                 st.fontSize = '';
10424                 el.afterFx(o);
10425             };
10426
10427             var width = this.getWidth();
10428             var height = this.getHeight();
10429
10430             arguments.callee.anim = this.fxanim({
10431                     width : {to: this.adjustWidth(width * 2)},
10432                     height : {to: this.adjustHeight(height * 2)},
10433                     points : {by: [-(width * .5), -(height * .5)]},
10434                     opacity : {to: 0},
10435                     fontSize: {to:200, unit: "%"}
10436                 },
10437                 o,
10438                 'motion',
10439                 .5,
10440                 "easeOut", after);
10441         });
10442         return this;
10443     },
10444
10445         /**
10446          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10447          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10448          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10449          * Usage:
10450          *<pre><code>
10451 // default
10452 el.switchOff();
10453
10454 // all config options shown with default values
10455 el.switchOff({
10456     easing: 'easeIn',
10457     duration: .3,
10458     remove: false,
10459     useDisplay: false
10460 });
10461 </code></pre>
10462          * @param {Object} options (optional) Object literal with any of the Fx config options
10463          * @return {Roo.Element} The Element
10464          */
10465     switchOff : function(o){
10466         var el = this.getFxEl();
10467         o = o || {};
10468
10469         el.queueFx(o, function(){
10470             this.clearOpacity();
10471             this.clip();
10472
10473             // restore values after effect
10474             var r = this.getFxRestore();
10475             var st = this.dom.style;
10476
10477             var after = function(){
10478                 if(o.useDisplay){
10479                     el.setDisplayed(false);
10480                 }else{
10481                     el.hide();
10482                 }
10483
10484                 el.clearOpacity();
10485                 el.setPositioning(r.pos);
10486                 st.width = r.width;
10487                 st.height = r.height;
10488
10489                 el.afterFx(o);
10490             };
10491
10492             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10493                 this.clearOpacity();
10494                 (function(){
10495                     this.fxanim({
10496                         height:{to:1},
10497                         points:{by:[0, this.getHeight() * .5]}
10498                     }, o, 'motion', 0.3, 'easeIn', after);
10499                 }).defer(100, this);
10500             });
10501         });
10502         return this;
10503     },
10504
10505     /**
10506      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10507      * changed using the "attr" config option) and then fading back to the original color. If no original
10508      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10509      * Usage:
10510 <pre><code>
10511 // default: highlight background to yellow
10512 el.highlight();
10513
10514 // custom: highlight foreground text to blue for 2 seconds
10515 el.highlight("0000ff", { attr: 'color', duration: 2 });
10516
10517 // common config options shown with default values
10518 el.highlight("ffff9c", {
10519     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10520     endColor: (current color) or "ffffff",
10521     easing: 'easeIn',
10522     duration: 1
10523 });
10524 </code></pre>
10525      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10526      * @param {Object} options (optional) Object literal with any of the Fx config options
10527      * @return {Roo.Element} The Element
10528      */ 
10529     highlight : function(color, o){
10530         var el = this.getFxEl();
10531         o = o || {};
10532
10533         el.queueFx(o, function(){
10534             color = color || "ffff9c";
10535             attr = o.attr || "backgroundColor";
10536
10537             this.clearOpacity();
10538             this.show();
10539
10540             var origColor = this.getColor(attr);
10541             var restoreColor = this.dom.style[attr];
10542             endColor = (o.endColor || origColor) || "ffffff";
10543
10544             var after = function(){
10545                 el.dom.style[attr] = restoreColor;
10546                 el.afterFx(o);
10547             };
10548
10549             var a = {};
10550             a[attr] = {from: color, to: endColor};
10551             arguments.callee.anim = this.fxanim(a,
10552                 o,
10553                 'color',
10554                 1,
10555                 'easeIn', after);
10556         });
10557         return this;
10558     },
10559
10560    /**
10561     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10562     * Usage:
10563 <pre><code>
10564 // default: a single light blue ripple
10565 el.frame();
10566
10567 // custom: 3 red ripples lasting 3 seconds total
10568 el.frame("ff0000", 3, { duration: 3 });
10569
10570 // common config options shown with default values
10571 el.frame("C3DAF9", 1, {
10572     duration: 1 //duration of entire animation (not each individual ripple)
10573     // Note: Easing is not configurable and will be ignored if included
10574 });
10575 </code></pre>
10576     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10577     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10578     * @param {Object} options (optional) Object literal with any of the Fx config options
10579     * @return {Roo.Element} The Element
10580     */
10581     frame : function(color, count, o){
10582         var el = this.getFxEl();
10583         o = o || {};
10584
10585         el.queueFx(o, function(){
10586             color = color || "#C3DAF9";
10587             if(color.length == 6){
10588                 color = "#" + color;
10589             }
10590             count = count || 1;
10591             duration = o.duration || 1;
10592             this.show();
10593
10594             var b = this.getBox();
10595             var animFn = function(){
10596                 var proxy = this.createProxy({
10597
10598                      style:{
10599                         visbility:"hidden",
10600                         position:"absolute",
10601                         "z-index":"35000", // yee haw
10602                         border:"0px solid " + color
10603                      }
10604                   });
10605                 var scale = Roo.isBorderBox ? 2 : 1;
10606                 proxy.animate({
10607                     top:{from:b.y, to:b.y - 20},
10608                     left:{from:b.x, to:b.x - 20},
10609                     borderWidth:{from:0, to:10},
10610                     opacity:{from:1, to:0},
10611                     height:{from:b.height, to:(b.height + (20*scale))},
10612                     width:{from:b.width, to:(b.width + (20*scale))}
10613                 }, duration, function(){
10614                     proxy.remove();
10615                 });
10616                 if(--count > 0){
10617                      animFn.defer((duration/2)*1000, this);
10618                 }else{
10619                     el.afterFx(o);
10620                 }
10621             };
10622             animFn.call(this);
10623         });
10624         return this;
10625     },
10626
10627    /**
10628     * Creates a pause before any subsequent queued effects begin.  If there are
10629     * no effects queued after the pause it will have no effect.
10630     * Usage:
10631 <pre><code>
10632 el.pause(1);
10633 </code></pre>
10634     * @param {Number} seconds The length of time to pause (in seconds)
10635     * @return {Roo.Element} The Element
10636     */
10637     pause : function(seconds){
10638         var el = this.getFxEl();
10639         var o = {};
10640
10641         el.queueFx(o, function(){
10642             setTimeout(function(){
10643                 el.afterFx(o);
10644             }, seconds * 1000);
10645         });
10646         return this;
10647     },
10648
10649    /**
10650     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10651     * using the "endOpacity" config option.
10652     * Usage:
10653 <pre><code>
10654 // default: fade in from opacity 0 to 100%
10655 el.fadeIn();
10656
10657 // custom: fade in from opacity 0 to 75% over 2 seconds
10658 el.fadeIn({ endOpacity: .75, duration: 2});
10659
10660 // common config options shown with default values
10661 el.fadeIn({
10662     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10663     easing: 'easeOut',
10664     duration: .5
10665 });
10666 </code></pre>
10667     * @param {Object} options (optional) Object literal with any of the Fx config options
10668     * @return {Roo.Element} The Element
10669     */
10670     fadeIn : function(o){
10671         var el = this.getFxEl();
10672         o = o || {};
10673         el.queueFx(o, function(){
10674             this.setOpacity(0);
10675             this.fixDisplay();
10676             this.dom.style.visibility = 'visible';
10677             var to = o.endOpacity || 1;
10678             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10679                 o, null, .5, "easeOut", function(){
10680                 if(to == 1){
10681                     this.clearOpacity();
10682                 }
10683                 el.afterFx(o);
10684             });
10685         });
10686         return this;
10687     },
10688
10689    /**
10690     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10691     * using the "endOpacity" config option.
10692     * Usage:
10693 <pre><code>
10694 // default: fade out from the element's current opacity to 0
10695 el.fadeOut();
10696
10697 // custom: fade out from the element's current opacity to 25% over 2 seconds
10698 el.fadeOut({ endOpacity: .25, duration: 2});
10699
10700 // common config options shown with default values
10701 el.fadeOut({
10702     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10703     easing: 'easeOut',
10704     duration: .5
10705     remove: false,
10706     useDisplay: false
10707 });
10708 </code></pre>
10709     * @param {Object} options (optional) Object literal with any of the Fx config options
10710     * @return {Roo.Element} The Element
10711     */
10712     fadeOut : function(o){
10713         var el = this.getFxEl();
10714         o = o || {};
10715         el.queueFx(o, function(){
10716             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10717                 o, null, .5, "easeOut", function(){
10718                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10719                      this.dom.style.display = "none";
10720                 }else{
10721                      this.dom.style.visibility = "hidden";
10722                 }
10723                 this.clearOpacity();
10724                 el.afterFx(o);
10725             });
10726         });
10727         return this;
10728     },
10729
10730    /**
10731     * Animates the transition of an element's dimensions from a starting height/width
10732     * to an ending height/width.
10733     * Usage:
10734 <pre><code>
10735 // change height and width to 100x100 pixels
10736 el.scale(100, 100);
10737
10738 // common config options shown with default values.  The height and width will default to
10739 // the element's existing values if passed as null.
10740 el.scale(
10741     [element's width],
10742     [element's height], {
10743     easing: 'easeOut',
10744     duration: .35
10745 });
10746 </code></pre>
10747     * @param {Number} width  The new width (pass undefined to keep the original width)
10748     * @param {Number} height  The new height (pass undefined to keep the original height)
10749     * @param {Object} options (optional) Object literal with any of the Fx config options
10750     * @return {Roo.Element} The Element
10751     */
10752     scale : function(w, h, o){
10753         this.shift(Roo.apply({}, o, {
10754             width: w,
10755             height: h
10756         }));
10757         return this;
10758     },
10759
10760    /**
10761     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10762     * Any of these properties not specified in the config object will not be changed.  This effect 
10763     * requires that at least one new dimension, position or opacity setting must be passed in on
10764     * the config object in order for the function to have any effect.
10765     * Usage:
10766 <pre><code>
10767 // slide the element horizontally to x position 200 while changing the height and opacity
10768 el.shift({ x: 200, height: 50, opacity: .8 });
10769
10770 // common config options shown with default values.
10771 el.shift({
10772     width: [element's width],
10773     height: [element's height],
10774     x: [element's x position],
10775     y: [element's y position],
10776     opacity: [element's opacity],
10777     easing: 'easeOut',
10778     duration: .35
10779 });
10780 </code></pre>
10781     * @param {Object} options  Object literal with any of the Fx config options
10782     * @return {Roo.Element} The Element
10783     */
10784     shift : function(o){
10785         var el = this.getFxEl();
10786         o = o || {};
10787         el.queueFx(o, function(){
10788             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10789             if(w !== undefined){
10790                 a.width = {to: this.adjustWidth(w)};
10791             }
10792             if(h !== undefined){
10793                 a.height = {to: this.adjustHeight(h)};
10794             }
10795             if(x !== undefined || y !== undefined){
10796                 a.points = {to: [
10797                     x !== undefined ? x : this.getX(),
10798                     y !== undefined ? y : this.getY()
10799                 ]};
10800             }
10801             if(op !== undefined){
10802                 a.opacity = {to: op};
10803             }
10804             if(o.xy !== undefined){
10805                 a.points = {to: o.xy};
10806             }
10807             arguments.callee.anim = this.fxanim(a,
10808                 o, 'motion', .35, "easeOut", function(){
10809                 el.afterFx(o);
10810             });
10811         });
10812         return this;
10813     },
10814
10815         /**
10816          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10817          * ending point of the effect.
10818          * Usage:
10819          *<pre><code>
10820 // default: slide the element downward while fading out
10821 el.ghost();
10822
10823 // custom: slide the element out to the right with a 2-second duration
10824 el.ghost('r', { duration: 2 });
10825
10826 // common config options shown with default values
10827 el.ghost('b', {
10828     easing: 'easeOut',
10829     duration: .5
10830     remove: false,
10831     useDisplay: false
10832 });
10833 </code></pre>
10834          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10835          * @param {Object} options (optional) Object literal with any of the Fx config options
10836          * @return {Roo.Element} The Element
10837          */
10838     ghost : function(anchor, o){
10839         var el = this.getFxEl();
10840         o = o || {};
10841
10842         el.queueFx(o, function(){
10843             anchor = anchor || "b";
10844
10845             // restore values after effect
10846             var r = this.getFxRestore();
10847             var w = this.getWidth(),
10848                 h = this.getHeight();
10849
10850             var st = this.dom.style;
10851
10852             var after = function(){
10853                 if(o.useDisplay){
10854                     el.setDisplayed(false);
10855                 }else{
10856                     el.hide();
10857                 }
10858
10859                 el.clearOpacity();
10860                 el.setPositioning(r.pos);
10861                 st.width = r.width;
10862                 st.height = r.height;
10863
10864                 el.afterFx(o);
10865             };
10866
10867             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10868             switch(anchor.toLowerCase()){
10869                 case "t":
10870                     pt.by = [0, -h];
10871                 break;
10872                 case "l":
10873                     pt.by = [-w, 0];
10874                 break;
10875                 case "r":
10876                     pt.by = [w, 0];
10877                 break;
10878                 case "b":
10879                     pt.by = [0, h];
10880                 break;
10881                 case "tl":
10882                     pt.by = [-w, -h];
10883                 break;
10884                 case "bl":
10885                     pt.by = [-w, h];
10886                 break;
10887                 case "br":
10888                     pt.by = [w, h];
10889                 break;
10890                 case "tr":
10891                     pt.by = [w, -h];
10892                 break;
10893             }
10894
10895             arguments.callee.anim = this.fxanim(a,
10896                 o,
10897                 'motion',
10898                 .5,
10899                 "easeOut", after);
10900         });
10901         return this;
10902     },
10903
10904         /**
10905          * Ensures that all effects queued after syncFx is called on the element are
10906          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10907          * @return {Roo.Element} The Element
10908          */
10909     syncFx : function(){
10910         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10911             block : false,
10912             concurrent : true,
10913             stopFx : false
10914         });
10915         return this;
10916     },
10917
10918         /**
10919          * Ensures that all effects queued after sequenceFx is called on the element are
10920          * run in sequence.  This is the opposite of {@link #syncFx}.
10921          * @return {Roo.Element} The Element
10922          */
10923     sequenceFx : function(){
10924         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10925             block : false,
10926             concurrent : false,
10927             stopFx : false
10928         });
10929         return this;
10930     },
10931
10932         /* @private */
10933     nextFx : function(){
10934         var ef = this.fxQueue[0];
10935         if(ef){
10936             ef.call(this);
10937         }
10938     },
10939
10940         /**
10941          * Returns true if the element has any effects actively running or queued, else returns false.
10942          * @return {Boolean} True if element has active effects, else false
10943          */
10944     hasActiveFx : function(){
10945         return this.fxQueue && this.fxQueue[0];
10946     },
10947
10948         /**
10949          * Stops any running effects and clears the element's internal effects queue if it contains
10950          * any additional effects that haven't started yet.
10951          * @return {Roo.Element} The Element
10952          */
10953     stopFx : function(){
10954         if(this.hasActiveFx()){
10955             var cur = this.fxQueue[0];
10956             if(cur && cur.anim && cur.anim.isAnimated()){
10957                 this.fxQueue = [cur]; // clear out others
10958                 cur.anim.stop(true);
10959             }
10960         }
10961         return this;
10962     },
10963
10964         /* @private */
10965     beforeFx : function(o){
10966         if(this.hasActiveFx() && !o.concurrent){
10967            if(o.stopFx){
10968                this.stopFx();
10969                return true;
10970            }
10971            return false;
10972         }
10973         return true;
10974     },
10975
10976         /**
10977          * Returns true if the element is currently blocking so that no other effect can be queued
10978          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10979          * used to ensure that an effect initiated by a user action runs to completion prior to the
10980          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10981          * @return {Boolean} True if blocking, else false
10982          */
10983     hasFxBlock : function(){
10984         var q = this.fxQueue;
10985         return q && q[0] && q[0].block;
10986     },
10987
10988         /* @private */
10989     queueFx : function(o, fn){
10990         if(!this.fxQueue){
10991             this.fxQueue = [];
10992         }
10993         if(!this.hasFxBlock()){
10994             Roo.applyIf(o, this.fxDefaults);
10995             if(!o.concurrent){
10996                 var run = this.beforeFx(o);
10997                 fn.block = o.block;
10998                 this.fxQueue.push(fn);
10999                 if(run){
11000                     this.nextFx();
11001                 }
11002             }else{
11003                 fn.call(this);
11004             }
11005         }
11006         return this;
11007     },
11008
11009         /* @private */
11010     fxWrap : function(pos, o, vis){
11011         var wrap;
11012         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11013             var wrapXY;
11014             if(o.fixPosition){
11015                 wrapXY = this.getXY();
11016             }
11017             var div = document.createElement("div");
11018             div.style.visibility = vis;
11019             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11020             wrap.setPositioning(pos);
11021             if(wrap.getStyle("position") == "static"){
11022                 wrap.position("relative");
11023             }
11024             this.clearPositioning('auto');
11025             wrap.clip();
11026             wrap.dom.appendChild(this.dom);
11027             if(wrapXY){
11028                 wrap.setXY(wrapXY);
11029             }
11030         }
11031         return wrap;
11032     },
11033
11034         /* @private */
11035     fxUnwrap : function(wrap, pos, o){
11036         this.clearPositioning();
11037         this.setPositioning(pos);
11038         if(!o.wrap){
11039             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11040             wrap.remove();
11041         }
11042     },
11043
11044         /* @private */
11045     getFxRestore : function(){
11046         var st = this.dom.style;
11047         return {pos: this.getPositioning(), width: st.width, height : st.height};
11048     },
11049
11050         /* @private */
11051     afterFx : function(o){
11052         if(o.afterStyle){
11053             this.applyStyles(o.afterStyle);
11054         }
11055         if(o.afterCls){
11056             this.addClass(o.afterCls);
11057         }
11058         if(o.remove === true){
11059             this.remove();
11060         }
11061         Roo.callback(o.callback, o.scope, [this]);
11062         if(!o.concurrent){
11063             this.fxQueue.shift();
11064             this.nextFx();
11065         }
11066     },
11067
11068         /* @private */
11069     getFxEl : function(){ // support for composite element fx
11070         return Roo.get(this.dom);
11071     },
11072
11073         /* @private */
11074     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11075         animType = animType || 'run';
11076         opt = opt || {};
11077         var anim = Roo.lib.Anim[animType](
11078             this.dom, args,
11079             (opt.duration || defaultDur) || .35,
11080             (opt.easing || defaultEase) || 'easeOut',
11081             function(){
11082                 Roo.callback(cb, this);
11083             },
11084             this
11085         );
11086         opt.anim = anim;
11087         return anim;
11088     }
11089 };
11090
11091 // backwords compat
11092 Roo.Fx.resize = Roo.Fx.scale;
11093
11094 //When included, Roo.Fx is automatically applied to Element so that all basic
11095 //effects are available directly via the Element API
11096 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11097  * Based on:
11098  * Ext JS Library 1.1.1
11099  * Copyright(c) 2006-2007, Ext JS, LLC.
11100  *
11101  * Originally Released Under LGPL - original licence link has changed is not relivant.
11102  *
11103  * Fork - LGPL
11104  * <script type="text/javascript">
11105  */
11106
11107
11108 /**
11109  * @class Roo.CompositeElement
11110  * Standard composite class. Creates a Roo.Element for every element in the collection.
11111  * <br><br>
11112  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11113  * actions will be performed on all the elements in this collection.</b>
11114  * <br><br>
11115  * All methods return <i>this</i> and can be chained.
11116  <pre><code>
11117  var els = Roo.select("#some-el div.some-class", true);
11118  // or select directly from an existing element
11119  var el = Roo.get('some-el');
11120  el.select('div.some-class', true);
11121
11122  els.setWidth(100); // all elements become 100 width
11123  els.hide(true); // all elements fade out and hide
11124  // or
11125  els.setWidth(100).hide(true);
11126  </code></pre>
11127  */
11128 Roo.CompositeElement = function(els){
11129     this.elements = [];
11130     this.addElements(els);
11131 };
11132 Roo.CompositeElement.prototype = {
11133     isComposite: true,
11134     addElements : function(els){
11135         if(!els) {
11136             return this;
11137         }
11138         if(typeof els == "string"){
11139             els = Roo.Element.selectorFunction(els);
11140         }
11141         var yels = this.elements;
11142         var index = yels.length-1;
11143         for(var i = 0, len = els.length; i < len; i++) {
11144                 yels[++index] = Roo.get(els[i]);
11145         }
11146         return this;
11147     },
11148
11149     /**
11150     * Clears this composite and adds the elements returned by the passed selector.
11151     * @param {String/Array} els A string CSS selector, an array of elements or an element
11152     * @return {CompositeElement} this
11153     */
11154     fill : function(els){
11155         this.elements = [];
11156         this.add(els);
11157         return this;
11158     },
11159
11160     /**
11161     * Filters this composite to only elements that match the passed selector.
11162     * @param {String} selector A string CSS selector
11163     * @param {Boolean} inverse return inverse filter (not matches)
11164     * @return {CompositeElement} this
11165     */
11166     filter : function(selector, inverse){
11167         var els = [];
11168         inverse = inverse || false;
11169         this.each(function(el){
11170             var match = inverse ? !el.is(selector) : el.is(selector);
11171             if(match){
11172                 els[els.length] = el.dom;
11173             }
11174         });
11175         this.fill(els);
11176         return this;
11177     },
11178
11179     invoke : function(fn, args){
11180         var els = this.elements;
11181         for(var i = 0, len = els.length; i < len; i++) {
11182                 Roo.Element.prototype[fn].apply(els[i], args);
11183         }
11184         return this;
11185     },
11186     /**
11187     * Adds elements to this composite.
11188     * @param {String/Array} els A string CSS selector, an array of elements or an element
11189     * @return {CompositeElement} this
11190     */
11191     add : function(els){
11192         if(typeof els == "string"){
11193             this.addElements(Roo.Element.selectorFunction(els));
11194         }else if(els.length !== undefined){
11195             this.addElements(els);
11196         }else{
11197             this.addElements([els]);
11198         }
11199         return this;
11200     },
11201     /**
11202     * Calls the passed function passing (el, this, index) for each element in this composite.
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         for(var i = 0, len = els.length; i < len; i++){
11210             if(fn.call(scope || els[i], els[i], this, i) === false) {
11211                 break;
11212             }
11213         }
11214         return this;
11215     },
11216
11217     /**
11218      * Returns the Element object at the specified index
11219      * @param {Number} index
11220      * @return {Roo.Element}
11221      */
11222     item : function(index){
11223         return this.elements[index] || null;
11224     },
11225
11226     /**
11227      * Returns the first Element
11228      * @return {Roo.Element}
11229      */
11230     first : function(){
11231         return this.item(0);
11232     },
11233
11234     /**
11235      * Returns the last Element
11236      * @return {Roo.Element}
11237      */
11238     last : function(){
11239         return this.item(this.elements.length-1);
11240     },
11241
11242     /**
11243      * Returns the number of elements in this composite
11244      * @return Number
11245      */
11246     getCount : function(){
11247         return this.elements.length;
11248     },
11249
11250     /**
11251      * Returns true if this composite contains the passed element
11252      * @return Boolean
11253      */
11254     contains : function(el){
11255         return this.indexOf(el) !== -1;
11256     },
11257
11258     /**
11259      * Returns true if this composite contains the passed element
11260      * @return Boolean
11261      */
11262     indexOf : function(el){
11263         return this.elements.indexOf(Roo.get(el));
11264     },
11265
11266
11267     /**
11268     * Removes the specified element(s).
11269     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11270     * or an array of any of those.
11271     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11272     * @return {CompositeElement} this
11273     */
11274     removeElement : function(el, removeDom){
11275         if(el instanceof Array){
11276             for(var i = 0, len = el.length; i < len; i++){
11277                 this.removeElement(el[i]);
11278             }
11279             return this;
11280         }
11281         var index = typeof el == 'number' ? el : this.indexOf(el);
11282         if(index !== -1){
11283             if(removeDom){
11284                 var d = this.elements[index];
11285                 if(d.dom){
11286                     d.remove();
11287                 }else{
11288                     d.parentNode.removeChild(d);
11289                 }
11290             }
11291             this.elements.splice(index, 1);
11292         }
11293         return this;
11294     },
11295
11296     /**
11297     * Replaces the specified element with the passed element.
11298     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11299     * to replace.
11300     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11301     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11302     * @return {CompositeElement} this
11303     */
11304     replaceElement : function(el, replacement, domReplace){
11305         var index = typeof el == 'number' ? el : this.indexOf(el);
11306         if(index !== -1){
11307             if(domReplace){
11308                 this.elements[index].replaceWith(replacement);
11309             }else{
11310                 this.elements.splice(index, 1, Roo.get(replacement))
11311             }
11312         }
11313         return this;
11314     },
11315
11316     /**
11317      * Removes all elements.
11318      */
11319     clear : function(){
11320         this.elements = [];
11321     }
11322 };
11323 (function(){
11324     Roo.CompositeElement.createCall = function(proto, fnName){
11325         if(!proto[fnName]){
11326             proto[fnName] = function(){
11327                 return this.invoke(fnName, arguments);
11328             };
11329         }
11330     };
11331     for(var fnName in Roo.Element.prototype){
11332         if(typeof Roo.Element.prototype[fnName] == "function"){
11333             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11334         }
11335     };
11336 })();
11337 /*
11338  * Based on:
11339  * Ext JS Library 1.1.1
11340  * Copyright(c) 2006-2007, Ext JS, LLC.
11341  *
11342  * Originally Released Under LGPL - original licence link has changed is not relivant.
11343  *
11344  * Fork - LGPL
11345  * <script type="text/javascript">
11346  */
11347
11348 /**
11349  * @class Roo.CompositeElementLite
11350  * @extends Roo.CompositeElement
11351  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11352  <pre><code>
11353  var els = Roo.select("#some-el div.some-class");
11354  // or select directly from an existing element
11355  var el = Roo.get('some-el');
11356  el.select('div.some-class');
11357
11358  els.setWidth(100); // all elements become 100 width
11359  els.hide(true); // all elements fade out and hide
11360  // or
11361  els.setWidth(100).hide(true);
11362  </code></pre><br><br>
11363  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11364  * actions will be performed on all the elements in this collection.</b>
11365  */
11366 Roo.CompositeElementLite = function(els){
11367     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11368     this.el = new Roo.Element.Flyweight();
11369 };
11370 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11371     addElements : function(els){
11372         if(els){
11373             if(els instanceof Array){
11374                 this.elements = this.elements.concat(els);
11375             }else{
11376                 var yels = this.elements;
11377                 var index = yels.length-1;
11378                 for(var i = 0, len = els.length; i < len; i++) {
11379                     yels[++index] = els[i];
11380                 }
11381             }
11382         }
11383         return this;
11384     },
11385     invoke : function(fn, args){
11386         var els = this.elements;
11387         var el = this.el;
11388         for(var i = 0, len = els.length; i < len; i++) {
11389             el.dom = els[i];
11390                 Roo.Element.prototype[fn].apply(el, args);
11391         }
11392         return this;
11393     },
11394     /**
11395      * Returns a flyweight Element of the dom element object at the specified index
11396      * @param {Number} index
11397      * @return {Roo.Element}
11398      */
11399     item : function(index){
11400         if(!this.elements[index]){
11401             return null;
11402         }
11403         this.el.dom = this.elements[index];
11404         return this.el;
11405     },
11406
11407     // fixes scope with flyweight
11408     addListener : function(eventName, handler, scope, opt){
11409         var els = this.elements;
11410         for(var i = 0, len = els.length; i < len; i++) {
11411             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11412         }
11413         return this;
11414     },
11415
11416     /**
11417     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11418     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11419     * a reference to the dom node, use el.dom.</b>
11420     * @param {Function} fn The function to call
11421     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11422     * @return {CompositeElement} this
11423     */
11424     each : function(fn, scope){
11425         var els = this.elements;
11426         var el = this.el;
11427         for(var i = 0, len = els.length; i < len; i++){
11428             el.dom = els[i];
11429                 if(fn.call(scope || el, el, this, i) === false){
11430                 break;
11431             }
11432         }
11433         return this;
11434     },
11435
11436     indexOf : function(el){
11437         return this.elements.indexOf(Roo.getDom(el));
11438     },
11439
11440     replaceElement : function(el, replacement, domReplace){
11441         var index = typeof el == 'number' ? el : this.indexOf(el);
11442         if(index !== -1){
11443             replacement = Roo.getDom(replacement);
11444             if(domReplace){
11445                 var d = this.elements[index];
11446                 d.parentNode.insertBefore(replacement, d);
11447                 d.parentNode.removeChild(d);
11448             }
11449             this.elements.splice(index, 1, replacement);
11450         }
11451         return this;
11452     }
11453 });
11454 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11455
11456 /*
11457  * Based on:
11458  * Ext JS Library 1.1.1
11459  * Copyright(c) 2006-2007, Ext JS, LLC.
11460  *
11461  * Originally Released Under LGPL - original licence link has changed is not relivant.
11462  *
11463  * Fork - LGPL
11464  * <script type="text/javascript">
11465  */
11466
11467  
11468
11469 /**
11470  * @class Roo.data.Connection
11471  * @extends Roo.util.Observable
11472  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11473  * either to a configured URL, or to a URL specified at request time.<br><br>
11474  * <p>
11475  * Requests made by this class are asynchronous, and will return immediately. No data from
11476  * the server will be available to the statement immediately following the {@link #request} call.
11477  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11478  * <p>
11479  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11480  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11481  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11482  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11483  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11484  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11485  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11486  * standard DOM methods.
11487  * @constructor
11488  * @param {Object} config a configuration object.
11489  */
11490 Roo.data.Connection = function(config){
11491     Roo.apply(this, config);
11492     this.addEvents({
11493         /**
11494          * @event beforerequest
11495          * Fires before a network request is made to retrieve a data object.
11496          * @param {Connection} conn This Connection object.
11497          * @param {Object} options The options config object passed to the {@link #request} method.
11498          */
11499         "beforerequest" : true,
11500         /**
11501          * @event requestcomplete
11502          * Fires if the request was successfully completed.
11503          * @param {Connection} conn This Connection object.
11504          * @param {Object} response The XHR object containing the response data.
11505          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11506          * @param {Object} options The options config object passed to the {@link #request} method.
11507          */
11508         "requestcomplete" : true,
11509         /**
11510          * @event requestexception
11511          * Fires if an error HTTP status was returned from the server.
11512          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11513          * @param {Connection} conn This Connection object.
11514          * @param {Object} response The XHR object containing the response data.
11515          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11516          * @param {Object} options The options config object passed to the {@link #request} method.
11517          */
11518         "requestexception" : true
11519     });
11520     Roo.data.Connection.superclass.constructor.call(this);
11521 };
11522
11523 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11524     /**
11525      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11526      */
11527     /**
11528      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11529      * extra parameters to each request made by this object. (defaults to undefined)
11530      */
11531     /**
11532      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11533      *  to each request made by this object. (defaults to undefined)
11534      */
11535     /**
11536      * @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)
11537      */
11538     /**
11539      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11540      */
11541     timeout : 30000,
11542     /**
11543      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11544      * @type Boolean
11545      */
11546     autoAbort:false,
11547
11548     /**
11549      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11550      * @type Boolean
11551      */
11552     disableCaching: true,
11553
11554     /**
11555      * Sends an HTTP request to a remote server.
11556      * @param {Object} options An object which may contain the following properties:<ul>
11557      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11558      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11559      * request, a url encoded string or a function to call to get either.</li>
11560      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11561      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11562      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11563      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11564      * <li>options {Object} The parameter to the request call.</li>
11565      * <li>success {Boolean} True if the request succeeded.</li>
11566      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11567      * </ul></li>
11568      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11569      * The callback is passed the following parameters:<ul>
11570      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11571      * <li>options {Object} The parameter to the request call.</li>
11572      * </ul></li>
11573      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11574      * The callback is passed the following parameters:<ul>
11575      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11576      * <li>options {Object} The parameter to the request call.</li>
11577      * </ul></li>
11578      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11579      * for the callback function. Defaults to the browser window.</li>
11580      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11581      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11582      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11583      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11584      * params for the post data. Any params will be appended to the URL.</li>
11585      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11586      * </ul>
11587      * @return {Number} transactionId
11588      */
11589     request : function(o){
11590         if(this.fireEvent("beforerequest", this, o) !== false){
11591             var p = o.params;
11592
11593             if(typeof p == "function"){
11594                 p = p.call(o.scope||window, o);
11595             }
11596             if(typeof p == "object"){
11597                 p = Roo.urlEncode(o.params);
11598             }
11599             if(this.extraParams){
11600                 var extras = Roo.urlEncode(this.extraParams);
11601                 p = p ? (p + '&' + extras) : extras;
11602             }
11603
11604             var url = o.url || this.url;
11605             if(typeof url == 'function'){
11606                 url = url.call(o.scope||window, o);
11607             }
11608
11609             if(o.form){
11610                 var form = Roo.getDom(o.form);
11611                 url = url || form.action;
11612
11613                 var enctype = form.getAttribute("enctype");
11614                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11615                     return this.doFormUpload(o, p, url);
11616                 }
11617                 var f = Roo.lib.Ajax.serializeForm(form);
11618                 p = p ? (p + '&' + f) : f;
11619             }
11620
11621             var hs = o.headers;
11622             if(this.defaultHeaders){
11623                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11624                 if(!o.headers){
11625                     o.headers = hs;
11626                 }
11627             }
11628
11629             var cb = {
11630                 success: this.handleResponse,
11631                 failure: this.handleFailure,
11632                 scope: this,
11633                 argument: {options: o},
11634                 timeout : o.timeout || this.timeout
11635             };
11636
11637             var method = o.method||this.method||(p ? "POST" : "GET");
11638
11639             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11640                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11641             }
11642
11643             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11644                 if(o.autoAbort){
11645                     this.abort();
11646                 }
11647             }else if(this.autoAbort !== false){
11648                 this.abort();
11649             }
11650
11651             if((method == 'GET' && p) || o.xmlData){
11652                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11653                 p = '';
11654             }
11655             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11656             return this.transId;
11657         }else{
11658             Roo.callback(o.callback, o.scope, [o, null, null]);
11659             return null;
11660         }
11661     },
11662
11663     /**
11664      * Determine whether this object has a request outstanding.
11665      * @param {Number} transactionId (Optional) defaults to the last transaction
11666      * @return {Boolean} True if there is an outstanding request.
11667      */
11668     isLoading : function(transId){
11669         if(transId){
11670             return Roo.lib.Ajax.isCallInProgress(transId);
11671         }else{
11672             return this.transId ? true : false;
11673         }
11674     },
11675
11676     /**
11677      * Aborts any outstanding request.
11678      * @param {Number} transactionId (Optional) defaults to the last transaction
11679      */
11680     abort : function(transId){
11681         if(transId || this.isLoading()){
11682             Roo.lib.Ajax.abort(transId || this.transId);
11683         }
11684     },
11685
11686     // private
11687     handleResponse : function(response){
11688         this.transId = false;
11689         var options = response.argument.options;
11690         response.argument = options ? options.argument : null;
11691         this.fireEvent("requestcomplete", this, response, options);
11692         Roo.callback(options.success, options.scope, [response, options]);
11693         Roo.callback(options.callback, options.scope, [options, true, response]);
11694     },
11695
11696     // private
11697     handleFailure : function(response, e){
11698         this.transId = false;
11699         var options = response.argument.options;
11700         response.argument = options ? options.argument : null;
11701         this.fireEvent("requestexception", this, response, options, e);
11702         Roo.callback(options.failure, options.scope, [response, options]);
11703         Roo.callback(options.callback, options.scope, [options, false, response]);
11704     },
11705
11706     // private
11707     doFormUpload : function(o, ps, url){
11708         var id = Roo.id();
11709         var frame = document.createElement('iframe');
11710         frame.id = id;
11711         frame.name = id;
11712         frame.className = 'x-hidden';
11713         if(Roo.isIE){
11714             frame.src = Roo.SSL_SECURE_URL;
11715         }
11716         document.body.appendChild(frame);
11717
11718         if(Roo.isIE){
11719            document.frames[id].name = id;
11720         }
11721
11722         var form = Roo.getDom(o.form);
11723         form.target = id;
11724         form.method = 'POST';
11725         form.enctype = form.encoding = 'multipart/form-data';
11726         if(url){
11727             form.action = url;
11728         }
11729
11730         var hiddens, hd;
11731         if(ps){ // add dynamic params
11732             hiddens = [];
11733             ps = Roo.urlDecode(ps, false);
11734             for(var k in ps){
11735                 if(ps.hasOwnProperty(k)){
11736                     hd = document.createElement('input');
11737                     hd.type = 'hidden';
11738                     hd.name = k;
11739                     hd.value = ps[k];
11740                     form.appendChild(hd);
11741                     hiddens.push(hd);
11742                 }
11743             }
11744         }
11745
11746         function cb(){
11747             var r = {  // bogus response object
11748                 responseText : '',
11749                 responseXML : null
11750             };
11751
11752             r.argument = o ? o.argument : null;
11753
11754             try { //
11755                 var doc;
11756                 if(Roo.isIE){
11757                     doc = frame.contentWindow.document;
11758                 }else {
11759                     doc = (frame.contentDocument || window.frames[id].document);
11760                 }
11761                 if(doc && doc.body){
11762                     r.responseText = doc.body.innerHTML;
11763                 }
11764                 if(doc && doc.XMLDocument){
11765                     r.responseXML = doc.XMLDocument;
11766                 }else {
11767                     r.responseXML = doc;
11768                 }
11769             }
11770             catch(e) {
11771                 // ignore
11772             }
11773
11774             Roo.EventManager.removeListener(frame, 'load', cb, this);
11775
11776             this.fireEvent("requestcomplete", this, r, o);
11777             Roo.callback(o.success, o.scope, [r, o]);
11778             Roo.callback(o.callback, o.scope, [o, true, r]);
11779
11780             setTimeout(function(){document.body.removeChild(frame);}, 100);
11781         }
11782
11783         Roo.EventManager.on(frame, 'load', cb, this);
11784         form.submit();
11785
11786         if(hiddens){ // remove dynamic params
11787             for(var i = 0, len = hiddens.length; i < len; i++){
11788                 form.removeChild(hiddens[i]);
11789             }
11790         }
11791     }
11792 });
11793 /*
11794  * Based on:
11795  * Ext JS Library 1.1.1
11796  * Copyright(c) 2006-2007, Ext JS, LLC.
11797  *
11798  * Originally Released Under LGPL - original licence link has changed is not relivant.
11799  *
11800  * Fork - LGPL
11801  * <script type="text/javascript">
11802  */
11803  
11804 /**
11805  * Global Ajax request class.
11806  * 
11807  * @class Roo.Ajax
11808  * @extends Roo.data.Connection
11809  * @static
11810  * 
11811  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11812  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11813  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11814  * @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)
11815  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11816  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11817  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11818  */
11819 Roo.Ajax = new Roo.data.Connection({
11820     // fix up the docs
11821     /**
11822      * @scope Roo.Ajax
11823      * @type {Boolear} 
11824      */
11825     autoAbort : false,
11826
11827     /**
11828      * Serialize the passed form into a url encoded string
11829      * @scope Roo.Ajax
11830      * @param {String/HTMLElement} form
11831      * @return {String}
11832      */
11833     serializeForm : function(form){
11834         return Roo.lib.Ajax.serializeForm(form);
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 /**
11849  * @class Roo.UpdateManager
11850  * @extends Roo.util.Observable
11851  * Provides AJAX-style update for Element object.<br><br>
11852  * Usage:<br>
11853  * <pre><code>
11854  * // Get it from a Roo.Element object
11855  * var el = Roo.get("foo");
11856  * var mgr = el.getUpdateManager();
11857  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11858  * ...
11859  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11860  * <br>
11861  * // or directly (returns the same UpdateManager instance)
11862  * var mgr = new Roo.UpdateManager("myElementId");
11863  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11864  * mgr.on("update", myFcnNeedsToKnow);
11865  * <br>
11866    // short handed call directly from the element object
11867    Roo.get("foo").load({
11868         url: "bar.php",
11869         scripts:true,
11870         params: "for=bar",
11871         text: "Loading Foo..."
11872    });
11873  * </code></pre>
11874  * @constructor
11875  * Create new UpdateManager directly.
11876  * @param {String/HTMLElement/Roo.Element} el The element to update
11877  * @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).
11878  */
11879 Roo.UpdateManager = function(el, forceNew){
11880     el = Roo.get(el);
11881     if(!forceNew && el.updateManager){
11882         return el.updateManager;
11883     }
11884     /**
11885      * The Element object
11886      * @type Roo.Element
11887      */
11888     this.el = el;
11889     /**
11890      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11891      * @type String
11892      */
11893     this.defaultUrl = null;
11894
11895     this.addEvents({
11896         /**
11897          * @event beforeupdate
11898          * Fired before an update is made, return false from your handler and the update is cancelled.
11899          * @param {Roo.Element} el
11900          * @param {String/Object/Function} url
11901          * @param {String/Object} params
11902          */
11903         "beforeupdate": true,
11904         /**
11905          * @event update
11906          * Fired after successful update is made.
11907          * @param {Roo.Element} el
11908          * @param {Object} oResponseObject The response Object
11909          */
11910         "update": true,
11911         /**
11912          * @event failure
11913          * Fired on update failure.
11914          * @param {Roo.Element} el
11915          * @param {Object} oResponseObject The response Object
11916          */
11917         "failure": true
11918     });
11919     var d = Roo.UpdateManager.defaults;
11920     /**
11921      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11922      * @type String
11923      */
11924     this.sslBlankUrl = d.sslBlankUrl;
11925     /**
11926      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11927      * @type Boolean
11928      */
11929     this.disableCaching = d.disableCaching;
11930     /**
11931      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11932      * @type String
11933      */
11934     this.indicatorText = d.indicatorText;
11935     /**
11936      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11937      * @type String
11938      */
11939     this.showLoadIndicator = d.showLoadIndicator;
11940     /**
11941      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11942      * @type Number
11943      */
11944     this.timeout = d.timeout;
11945
11946     /**
11947      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11948      * @type Boolean
11949      */
11950     this.loadScripts = d.loadScripts;
11951
11952     /**
11953      * Transaction object of current executing transaction
11954      */
11955     this.transaction = null;
11956
11957     /**
11958      * @private
11959      */
11960     this.autoRefreshProcId = null;
11961     /**
11962      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11963      * @type Function
11964      */
11965     this.refreshDelegate = this.refresh.createDelegate(this);
11966     /**
11967      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11968      * @type Function
11969      */
11970     this.updateDelegate = this.update.createDelegate(this);
11971     /**
11972      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11973      * @type Function
11974      */
11975     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11976     /**
11977      * @private
11978      */
11979     this.successDelegate = this.processSuccess.createDelegate(this);
11980     /**
11981      * @private
11982      */
11983     this.failureDelegate = this.processFailure.createDelegate(this);
11984
11985     if(!this.renderer){
11986      /**
11987       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11988       */
11989     this.renderer = new Roo.UpdateManager.BasicRenderer();
11990     }
11991     
11992     Roo.UpdateManager.superclass.constructor.call(this);
11993 };
11994
11995 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11996     /**
11997      * Get the Element this UpdateManager is bound to
11998      * @return {Roo.Element} The element
11999      */
12000     getEl : function(){
12001         return this.el;
12002     },
12003     /**
12004      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12005      * @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:
12006 <pre><code>
12007 um.update({<br/>
12008     url: "your-url.php",<br/>
12009     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12010     callback: yourFunction,<br/>
12011     scope: yourObject, //(optional scope)  <br/>
12012     discardUrl: false, <br/>
12013     nocache: false,<br/>
12014     text: "Loading...",<br/>
12015     timeout: 30,<br/>
12016     scripts: false<br/>
12017 });
12018 </code></pre>
12019      * The only required property is url. The optional properties nocache, text and scripts
12020      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12021      * @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}
12022      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12023      * @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.
12024      */
12025     update : function(url, params, callback, discardUrl){
12026         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12027             var method = this.method,
12028                 cfg;
12029             if(typeof url == "object"){ // must be config object
12030                 cfg = url;
12031                 url = cfg.url;
12032                 params = params || cfg.params;
12033                 callback = callback || cfg.callback;
12034                 discardUrl = discardUrl || cfg.discardUrl;
12035                 if(callback && cfg.scope){
12036                     callback = callback.createDelegate(cfg.scope);
12037                 }
12038                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12039                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12040                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12041                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12042                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12043             }
12044             this.showLoading();
12045             if(!discardUrl){
12046                 this.defaultUrl = url;
12047             }
12048             if(typeof url == "function"){
12049                 url = url.call(this);
12050             }
12051
12052             method = method || (params ? "POST" : "GET");
12053             if(method == "GET"){
12054                 url = this.prepareUrl(url);
12055             }
12056
12057             var o = Roo.apply(cfg ||{}, {
12058                 url : url,
12059                 params: params,
12060                 success: this.successDelegate,
12061                 failure: this.failureDelegate,
12062                 callback: undefined,
12063                 timeout: (this.timeout*1000),
12064                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12065             });
12066             Roo.log("updated manager called with timeout of " + o.timeout);
12067             this.transaction = Roo.Ajax.request(o);
12068         }
12069     },
12070
12071     /**
12072      * 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.
12073      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12074      * @param {String/HTMLElement} form The form Id or form element
12075      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12076      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12077      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12078      */
12079     formUpdate : function(form, url, reset, callback){
12080         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12081             if(typeof url == "function"){
12082                 url = url.call(this);
12083             }
12084             form = Roo.getDom(form);
12085             this.transaction = Roo.Ajax.request({
12086                 form: form,
12087                 url:url,
12088                 success: this.successDelegate,
12089                 failure: this.failureDelegate,
12090                 timeout: (this.timeout*1000),
12091                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12092             });
12093             this.showLoading.defer(1, this);
12094         }
12095     },
12096
12097     /**
12098      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12099      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12100      */
12101     refresh : function(callback){
12102         if(this.defaultUrl == null){
12103             return;
12104         }
12105         this.update(this.defaultUrl, null, callback, true);
12106     },
12107
12108     /**
12109      * Set this element to auto refresh.
12110      * @param {Number} interval How often to update (in seconds).
12111      * @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)
12112      * @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}
12113      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12114      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12115      */
12116     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12117         if(refreshNow){
12118             this.update(url || this.defaultUrl, params, callback, true);
12119         }
12120         if(this.autoRefreshProcId){
12121             clearInterval(this.autoRefreshProcId);
12122         }
12123         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12124     },
12125
12126     /**
12127      * Stop auto refresh on this element.
12128      */
12129      stopAutoRefresh : function(){
12130         if(this.autoRefreshProcId){
12131             clearInterval(this.autoRefreshProcId);
12132             delete this.autoRefreshProcId;
12133         }
12134     },
12135
12136     isAutoRefreshing : function(){
12137        return this.autoRefreshProcId ? true : false;
12138     },
12139     /**
12140      * Called to update the element to "Loading" state. Override to perform custom action.
12141      */
12142     showLoading : function(){
12143         if(this.showLoadIndicator){
12144             this.el.update(this.indicatorText);
12145         }
12146     },
12147
12148     /**
12149      * Adds unique parameter to query string if disableCaching = true
12150      * @private
12151      */
12152     prepareUrl : function(url){
12153         if(this.disableCaching){
12154             var append = "_dc=" + (new Date().getTime());
12155             if(url.indexOf("?") !== -1){
12156                 url += "&" + append;
12157             }else{
12158                 url += "?" + append;
12159             }
12160         }
12161         return url;
12162     },
12163
12164     /**
12165      * @private
12166      */
12167     processSuccess : function(response){
12168         this.transaction = null;
12169         if(response.argument.form && response.argument.reset){
12170             try{ // put in try/catch since some older FF releases had problems with this
12171                 response.argument.form.reset();
12172             }catch(e){}
12173         }
12174         if(this.loadScripts){
12175             this.renderer.render(this.el, response, this,
12176                 this.updateComplete.createDelegate(this, [response]));
12177         }else{
12178             this.renderer.render(this.el, response, this);
12179             this.updateComplete(response);
12180         }
12181     },
12182
12183     updateComplete : function(response){
12184         this.fireEvent("update", this.el, response);
12185         if(typeof response.argument.callback == "function"){
12186             response.argument.callback(this.el, true, response);
12187         }
12188     },
12189
12190     /**
12191      * @private
12192      */
12193     processFailure : function(response){
12194         this.transaction = null;
12195         this.fireEvent("failure", this.el, response);
12196         if(typeof response.argument.callback == "function"){
12197             response.argument.callback(this.el, false, response);
12198         }
12199     },
12200
12201     /**
12202      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12203      * @param {Object} renderer The object implementing the render() method
12204      */
12205     setRenderer : function(renderer){
12206         this.renderer = renderer;
12207     },
12208
12209     getRenderer : function(){
12210        return this.renderer;
12211     },
12212
12213     /**
12214      * Set the defaultUrl used for updates
12215      * @param {String/Function} defaultUrl The url or a function to call to get the url
12216      */
12217     setDefaultUrl : function(defaultUrl){
12218         this.defaultUrl = defaultUrl;
12219     },
12220
12221     /**
12222      * Aborts the executing transaction
12223      */
12224     abort : function(){
12225         if(this.transaction){
12226             Roo.Ajax.abort(this.transaction);
12227         }
12228     },
12229
12230     /**
12231      * Returns true if an update is in progress
12232      * @return {Boolean}
12233      */
12234     isUpdating : function(){
12235         if(this.transaction){
12236             return Roo.Ajax.isLoading(this.transaction);
12237         }
12238         return false;
12239     }
12240 });
12241
12242 /**
12243  * @class Roo.UpdateManager.defaults
12244  * @static (not really - but it helps the doc tool)
12245  * The defaults collection enables customizing the default properties of UpdateManager
12246  */
12247    Roo.UpdateManager.defaults = {
12248        /**
12249          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12250          * @type Number
12251          */
12252          timeout : 30,
12253
12254          /**
12255          * True to process scripts by default (Defaults to false).
12256          * @type Boolean
12257          */
12258         loadScripts : false,
12259
12260         /**
12261         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12262         * @type String
12263         */
12264         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12265         /**
12266          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12267          * @type Boolean
12268          */
12269         disableCaching : false,
12270         /**
12271          * Whether to show indicatorText when loading (Defaults to true).
12272          * @type Boolean
12273          */
12274         showLoadIndicator : true,
12275         /**
12276          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12277          * @type String
12278          */
12279         indicatorText : '<div class="loading-indicator">Loading...</div>'
12280    };
12281
12282 /**
12283  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12284  *Usage:
12285  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12286  * @param {String/HTMLElement/Roo.Element} el The element to update
12287  * @param {String} url The url
12288  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12289  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12290  * @static
12291  * @deprecated
12292  * @member Roo.UpdateManager
12293  */
12294 Roo.UpdateManager.updateElement = function(el, url, params, options){
12295     var um = Roo.get(el, true).getUpdateManager();
12296     Roo.apply(um, options);
12297     um.update(url, params, options ? options.callback : null);
12298 };
12299 // alias for backwards compat
12300 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12301 /**
12302  * @class Roo.UpdateManager.BasicRenderer
12303  * Default Content renderer. Updates the elements innerHTML with the responseText.
12304  */
12305 Roo.UpdateManager.BasicRenderer = function(){};
12306
12307 Roo.UpdateManager.BasicRenderer.prototype = {
12308     /**
12309      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12310      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12311      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12312      * @param {Roo.Element} el The element being rendered
12313      * @param {Object} response The YUI Connect response object
12314      * @param {UpdateManager} updateManager The calling update manager
12315      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12316      */
12317      render : function(el, response, updateManager, callback){
12318         el.update(response.responseText, updateManager.loadScripts, callback);
12319     }
12320 };
12321 /*
12322  * Based on:
12323  * Roo JS
12324  * (c)) Alan Knowles
12325  * Licence : LGPL
12326  */
12327
12328
12329 /**
12330  * @class Roo.DomTemplate
12331  * @extends Roo.Template
12332  * An effort at a dom based template engine..
12333  *
12334  * Similar to XTemplate, except it uses dom parsing to create the template..
12335  *
12336  * Supported features:
12337  *
12338  *  Tags:
12339
12340 <pre><code>
12341       {a_variable} - output encoded.
12342       {a_variable.format:("Y-m-d")} - call a method on the variable
12343       {a_variable:raw} - unencoded output
12344       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12345       {a_variable:this.method_on_template(...)} - call a method on the template object.
12346  
12347 </code></pre>
12348  *  The tpl tag:
12349 <pre><code>
12350         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12351         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12352         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12353         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12354   
12355 </code></pre>
12356  *      
12357  */
12358 Roo.DomTemplate = function()
12359 {
12360      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12361      if (this.html) {
12362         this.compile();
12363      }
12364 };
12365
12366
12367 Roo.extend(Roo.DomTemplate, Roo.Template, {
12368     /**
12369      * id counter for sub templates.
12370      */
12371     id : 0,
12372     /**
12373      * flag to indicate if dom parser is inside a pre,
12374      * it will strip whitespace if not.
12375      */
12376     inPre : false,
12377     
12378     /**
12379      * The various sub templates
12380      */
12381     tpls : false,
12382     
12383     
12384     
12385     /**
12386      *
12387      * basic tag replacing syntax
12388      * WORD:WORD()
12389      *
12390      * // you can fake an object call by doing this
12391      *  x.t:(test,tesT) 
12392      * 
12393      */
12394     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12395     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12396     
12397     iterChild : function (node, method) {
12398         
12399         var oldPre = this.inPre;
12400         if (node.tagName == 'PRE') {
12401             this.inPre = true;
12402         }
12403         for( var i = 0; i < node.childNodes.length; i++) {
12404             method.call(this, node.childNodes[i]);
12405         }
12406         this.inPre = oldPre;
12407     },
12408     
12409     
12410     
12411     /**
12412      * compile the template
12413      *
12414      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12415      *
12416      */
12417     compile: function()
12418     {
12419         var s = this.html;
12420         
12421         // covert the html into DOM...
12422         var doc = false;
12423         var div =false;
12424         try {
12425             doc = document.implementation.createHTMLDocument("");
12426             doc.documentElement.innerHTML =   this.html  ;
12427             div = doc.documentElement;
12428         } catch (e) {
12429             // old IE... - nasty -- it causes all sorts of issues.. with
12430             // images getting pulled from server..
12431             div = document.createElement('div');
12432             div.innerHTML = this.html;
12433         }
12434         //doc.documentElement.innerHTML = htmlBody
12435          
12436         
12437         
12438         this.tpls = [];
12439         var _t = this;
12440         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12441         
12442         var tpls = this.tpls;
12443         
12444         // create a top level template from the snippet..
12445         
12446         //Roo.log(div.innerHTML);
12447         
12448         var tpl = {
12449             uid : 'master',
12450             id : this.id++,
12451             attr : false,
12452             value : false,
12453             body : div.innerHTML,
12454             
12455             forCall : false,
12456             execCall : false,
12457             dom : div,
12458             isTop : true
12459             
12460         };
12461         tpls.unshift(tpl);
12462         
12463         
12464         // compile them...
12465         this.tpls = [];
12466         Roo.each(tpls, function(tp){
12467             this.compileTpl(tp);
12468             this.tpls[tp.id] = tp;
12469         }, this);
12470         
12471         this.master = tpls[0];
12472         return this;
12473         
12474         
12475     },
12476     
12477     compileNode : function(node, istop) {
12478         // test for
12479         //Roo.log(node);
12480         
12481         
12482         // skip anything not a tag..
12483         if (node.nodeType != 1) {
12484             if (node.nodeType == 3 && !this.inPre) {
12485                 // reduce white space..
12486                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12487                 
12488             }
12489             return;
12490         }
12491         
12492         var tpl = {
12493             uid : false,
12494             id : false,
12495             attr : false,
12496             value : false,
12497             body : '',
12498             
12499             forCall : false,
12500             execCall : false,
12501             dom : false,
12502             isTop : istop
12503             
12504             
12505         };
12506         
12507         
12508         switch(true) {
12509             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12510             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12511             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12512             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12513             // no default..
12514         }
12515         
12516         
12517         if (!tpl.attr) {
12518             // just itterate children..
12519             this.iterChild(node,this.compileNode);
12520             return;
12521         }
12522         tpl.uid = this.id++;
12523         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12524         node.removeAttribute('roo-'+ tpl.attr);
12525         if (tpl.attr != 'name') {
12526             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12527             node.parentNode.replaceChild(placeholder,  node);
12528         } else {
12529             
12530             var placeholder =  document.createElement('span');
12531             placeholder.className = 'roo-tpl-' + tpl.value;
12532             node.parentNode.replaceChild(placeholder,  node);
12533         }
12534         
12535         // parent now sees '{domtplXXXX}
12536         this.iterChild(node,this.compileNode);
12537         
12538         // we should now have node body...
12539         var div = document.createElement('div');
12540         div.appendChild(node);
12541         tpl.dom = node;
12542         // this has the unfortunate side effect of converting tagged attributes
12543         // eg. href="{...}" into %7C...%7D
12544         // this has been fixed by searching for those combo's although it's a bit hacky..
12545         
12546         
12547         tpl.body = div.innerHTML;
12548         
12549         
12550          
12551         tpl.id = tpl.uid;
12552         switch(tpl.attr) {
12553             case 'for' :
12554                 switch (tpl.value) {
12555                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12556                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12557                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12558                 }
12559                 break;
12560             
12561             case 'exec':
12562                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12563                 break;
12564             
12565             case 'if':     
12566                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12567                 break;
12568             
12569             case 'name':
12570                 tpl.id  = tpl.value; // replace non characters???
12571                 break;
12572             
12573         }
12574         
12575         
12576         this.tpls.push(tpl);
12577         
12578         
12579         
12580     },
12581     
12582     
12583     
12584     
12585     /**
12586      * Compile a segment of the template into a 'sub-template'
12587      *
12588      * 
12589      * 
12590      *
12591      */
12592     compileTpl : function(tpl)
12593     {
12594         var fm = Roo.util.Format;
12595         var useF = this.disableFormats !== true;
12596         
12597         var sep = Roo.isGecko ? "+\n" : ",\n";
12598         
12599         var undef = function(str) {
12600             Roo.debug && Roo.log("Property not found :"  + str);
12601             return '';
12602         };
12603           
12604         //Roo.log(tpl.body);
12605         
12606         
12607         
12608         var fn = function(m, lbrace, name, format, args)
12609         {
12610             //Roo.log("ARGS");
12611             //Roo.log(arguments);
12612             args = args ? args.replace(/\\'/g,"'") : args;
12613             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12614             if (typeof(format) == 'undefined') {
12615                 format =  'htmlEncode'; 
12616             }
12617             if (format == 'raw' ) {
12618                 format = false;
12619             }
12620             
12621             if(name.substr(0, 6) == 'domtpl'){
12622                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12623             }
12624             
12625             // build an array of options to determine if value is undefined..
12626             
12627             // basically get 'xxxx.yyyy' then do
12628             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12629             //    (function () { Roo.log("Property not found"); return ''; })() :
12630             //    ......
12631             
12632             var udef_ar = [];
12633             var lookfor = '';
12634             Roo.each(name.split('.'), function(st) {
12635                 lookfor += (lookfor.length ? '.': '') + st;
12636                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12637             });
12638             
12639             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12640             
12641             
12642             if(format && useF){
12643                 
12644                 args = args ? ',' + args : "";
12645                  
12646                 if(format.substr(0, 5) != "this."){
12647                     format = "fm." + format + '(';
12648                 }else{
12649                     format = 'this.call("'+ format.substr(5) + '", ';
12650                     args = ", values";
12651                 }
12652                 
12653                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12654             }
12655              
12656             if (args && args.length) {
12657                 // called with xxyx.yuu:(test,test)
12658                 // change to ()
12659                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12660             }
12661             // raw.. - :raw modifier..
12662             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12663             
12664         };
12665         var body;
12666         // branched to use + in gecko and [].join() in others
12667         if(Roo.isGecko){
12668             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12669                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12670                     "';};};";
12671         }else{
12672             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12673             body.push(tpl.body.replace(/(\r\n|\n)/g,
12674                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12675             body.push("'].join('');};};");
12676             body = body.join('');
12677         }
12678         
12679         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12680        
12681         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12682         eval(body);
12683         
12684         return this;
12685     },
12686      
12687     /**
12688      * same as applyTemplate, except it's done to one of the subTemplates
12689      * when using named templates, you can do:
12690      *
12691      * var str = pl.applySubTemplate('your-name', values);
12692      *
12693      * 
12694      * @param {Number} id of the template
12695      * @param {Object} values to apply to template
12696      * @param {Object} parent (normaly the instance of this object)
12697      */
12698     applySubTemplate : function(id, values, parent)
12699     {
12700         
12701         
12702         var t = this.tpls[id];
12703         
12704         
12705         try { 
12706             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12707                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12708                 return '';
12709             }
12710         } catch(e) {
12711             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12712             Roo.log(values);
12713           
12714             return '';
12715         }
12716         try { 
12717             
12718             if(t.execCall && t.execCall.call(this, values, parent)){
12719                 return '';
12720             }
12721         } catch(e) {
12722             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12723             Roo.log(values);
12724             return '';
12725         }
12726         
12727         try {
12728             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12729             parent = t.target ? values : parent;
12730             if(t.forCall && vs instanceof Array){
12731                 var buf = [];
12732                 for(var i = 0, len = vs.length; i < len; i++){
12733                     try {
12734                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12735                     } catch (e) {
12736                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12737                         Roo.log(e.body);
12738                         //Roo.log(t.compiled);
12739                         Roo.log(vs[i]);
12740                     }   
12741                 }
12742                 return buf.join('');
12743             }
12744         } catch (e) {
12745             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12746             Roo.log(values);
12747             return '';
12748         }
12749         try {
12750             return t.compiled.call(this, vs, parent);
12751         } catch (e) {
12752             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12753             Roo.log(e.body);
12754             //Roo.log(t.compiled);
12755             Roo.log(values);
12756             return '';
12757         }
12758     },
12759
12760    
12761
12762     applyTemplate : function(values){
12763         return this.master.compiled.call(this, values, {});
12764         //var s = this.subs;
12765     },
12766
12767     apply : function(){
12768         return this.applyTemplate.apply(this, arguments);
12769     }
12770
12771  });
12772
12773 Roo.DomTemplate.from = function(el){
12774     el = Roo.getDom(el);
12775     return new Roo.Domtemplate(el.value || el.innerHTML);
12776 };/*
12777  * Based on:
12778  * Ext JS Library 1.1.1
12779  * Copyright(c) 2006-2007, Ext JS, LLC.
12780  *
12781  * Originally Released Under LGPL - original licence link has changed is not relivant.
12782  *
12783  * Fork - LGPL
12784  * <script type="text/javascript">
12785  */
12786
12787 /**
12788  * @class Roo.util.DelayedTask
12789  * Provides a convenient method of performing setTimeout where a new
12790  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12791  * You can use this class to buffer
12792  * the keypress events for a certain number of milliseconds, and perform only if they stop
12793  * for that amount of time.
12794  * @constructor The parameters to this constructor serve as defaults and are not required.
12795  * @param {Function} fn (optional) The default function to timeout
12796  * @param {Object} scope (optional) The default scope of that timeout
12797  * @param {Array} args (optional) The default Array of arguments
12798  */
12799 Roo.util.DelayedTask = function(fn, scope, args){
12800     var id = null, d, t;
12801
12802     var call = function(){
12803         var now = new Date().getTime();
12804         if(now - t >= d){
12805             clearInterval(id);
12806             id = null;
12807             fn.apply(scope, args || []);
12808         }
12809     };
12810     /**
12811      * Cancels any pending timeout and queues a new one
12812      * @param {Number} delay The milliseconds to delay
12813      * @param {Function} newFn (optional) Overrides function passed to constructor
12814      * @param {Object} newScope (optional) Overrides scope passed to constructor
12815      * @param {Array} newArgs (optional) Overrides args passed to constructor
12816      */
12817     this.delay = function(delay, newFn, newScope, newArgs){
12818         if(id && delay != d){
12819             this.cancel();
12820         }
12821         d = delay;
12822         t = new Date().getTime();
12823         fn = newFn || fn;
12824         scope = newScope || scope;
12825         args = newArgs || args;
12826         if(!id){
12827             id = setInterval(call, d);
12828         }
12829     };
12830
12831     /**
12832      * Cancel the last queued timeout
12833      */
12834     this.cancel = function(){
12835         if(id){
12836             clearInterval(id);
12837             id = null;
12838         }
12839     };
12840 };/*
12841  * Based on:
12842  * Ext JS Library 1.1.1
12843  * Copyright(c) 2006-2007, Ext JS, LLC.
12844  *
12845  * Originally Released Under LGPL - original licence link has changed is not relivant.
12846  *
12847  * Fork - LGPL
12848  * <script type="text/javascript">
12849  */
12850  
12851  
12852 Roo.util.TaskRunner = function(interval){
12853     interval = interval || 10;
12854     var tasks = [], removeQueue = [];
12855     var id = 0;
12856     var running = false;
12857
12858     var stopThread = function(){
12859         running = false;
12860         clearInterval(id);
12861         id = 0;
12862     };
12863
12864     var startThread = function(){
12865         if(!running){
12866             running = true;
12867             id = setInterval(runTasks, interval);
12868         }
12869     };
12870
12871     var removeTask = function(task){
12872         removeQueue.push(task);
12873         if(task.onStop){
12874             task.onStop();
12875         }
12876     };
12877
12878     var runTasks = function(){
12879         if(removeQueue.length > 0){
12880             for(var i = 0, len = removeQueue.length; i < len; i++){
12881                 tasks.remove(removeQueue[i]);
12882             }
12883             removeQueue = [];
12884             if(tasks.length < 1){
12885                 stopThread();
12886                 return;
12887             }
12888         }
12889         var now = new Date().getTime();
12890         for(var i = 0, len = tasks.length; i < len; ++i){
12891             var t = tasks[i];
12892             var itime = now - t.taskRunTime;
12893             if(t.interval <= itime){
12894                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12895                 t.taskRunTime = now;
12896                 if(rt === false || t.taskRunCount === t.repeat){
12897                     removeTask(t);
12898                     return;
12899                 }
12900             }
12901             if(t.duration && t.duration <= (now - t.taskStartTime)){
12902                 removeTask(t);
12903             }
12904         }
12905     };
12906
12907     /**
12908      * Queues a new task.
12909      * @param {Object} task
12910      */
12911     this.start = function(task){
12912         tasks.push(task);
12913         task.taskStartTime = new Date().getTime();
12914         task.taskRunTime = 0;
12915         task.taskRunCount = 0;
12916         startThread();
12917         return task;
12918     };
12919
12920     this.stop = function(task){
12921         removeTask(task);
12922         return task;
12923     };
12924
12925     this.stopAll = function(){
12926         stopThread();
12927         for(var i = 0, len = tasks.length; i < len; i++){
12928             if(tasks[i].onStop){
12929                 tasks[i].onStop();
12930             }
12931         }
12932         tasks = [];
12933         removeQueue = [];
12934     };
12935 };
12936
12937 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12938  * Based on:
12939  * Ext JS Library 1.1.1
12940  * Copyright(c) 2006-2007, Ext JS, LLC.
12941  *
12942  * Originally Released Under LGPL - original licence link has changed is not relivant.
12943  *
12944  * Fork - LGPL
12945  * <script type="text/javascript">
12946  */
12947
12948  
12949 /**
12950  * @class Roo.util.MixedCollection
12951  * @extends Roo.util.Observable
12952  * A Collection class that maintains both numeric indexes and keys and exposes events.
12953  * @constructor
12954  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12955  * collection (defaults to false)
12956  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12957  * and return the key value for that item.  This is used when available to look up the key on items that
12958  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12959  * equivalent to providing an implementation for the {@link #getKey} method.
12960  */
12961 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12962     this.items = [];
12963     this.map = {};
12964     this.keys = [];
12965     this.length = 0;
12966     this.addEvents({
12967         /**
12968          * @event clear
12969          * Fires when the collection is cleared.
12970          */
12971         "clear" : true,
12972         /**
12973          * @event add
12974          * Fires when an item is added to the collection.
12975          * @param {Number} index The index at which the item was added.
12976          * @param {Object} o The item added.
12977          * @param {String} key The key associated with the added item.
12978          */
12979         "add" : true,
12980         /**
12981          * @event replace
12982          * Fires when an item is replaced in the collection.
12983          * @param {String} key he key associated with the new added.
12984          * @param {Object} old The item being replaced.
12985          * @param {Object} new The new item.
12986          */
12987         "replace" : true,
12988         /**
12989          * @event remove
12990          * Fires when an item is removed from the collection.
12991          * @param {Object} o The item being removed.
12992          * @param {String} key (optional) The key associated with the removed item.
12993          */
12994         "remove" : true,
12995         "sort" : true
12996     });
12997     this.allowFunctions = allowFunctions === true;
12998     if(keyFn){
12999         this.getKey = keyFn;
13000     }
13001     Roo.util.MixedCollection.superclass.constructor.call(this);
13002 };
13003
13004 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13005     allowFunctions : false,
13006     
13007 /**
13008  * Adds an item to the collection.
13009  * @param {String} key The key to associate with the item
13010  * @param {Object} o The item to add.
13011  * @return {Object} The item added.
13012  */
13013     add : function(key, o){
13014         if(arguments.length == 1){
13015             o = arguments[0];
13016             key = this.getKey(o);
13017         }
13018         if(typeof key == "undefined" || key === null){
13019             this.length++;
13020             this.items.push(o);
13021             this.keys.push(null);
13022         }else{
13023             var old = this.map[key];
13024             if(old){
13025                 return this.replace(key, o);
13026             }
13027             this.length++;
13028             this.items.push(o);
13029             this.map[key] = o;
13030             this.keys.push(key);
13031         }
13032         this.fireEvent("add", this.length-1, o, key);
13033         return o;
13034     },
13035        
13036 /**
13037   * MixedCollection has a generic way to fetch keys if you implement getKey.
13038 <pre><code>
13039 // normal way
13040 var mc = new Roo.util.MixedCollection();
13041 mc.add(someEl.dom.id, someEl);
13042 mc.add(otherEl.dom.id, otherEl);
13043 //and so on
13044
13045 // using getKey
13046 var mc = new Roo.util.MixedCollection();
13047 mc.getKey = function(el){
13048    return el.dom.id;
13049 };
13050 mc.add(someEl);
13051 mc.add(otherEl);
13052
13053 // or via the constructor
13054 var mc = new Roo.util.MixedCollection(false, function(el){
13055    return el.dom.id;
13056 });
13057 mc.add(someEl);
13058 mc.add(otherEl);
13059 </code></pre>
13060  * @param o {Object} The item for which to find the key.
13061  * @return {Object} The key for the passed item.
13062  */
13063     getKey : function(o){
13064          return o.id; 
13065     },
13066    
13067 /**
13068  * Replaces an item in the collection.
13069  * @param {String} key The key associated with the item to replace, or the item to replace.
13070  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13071  * @return {Object}  The new item.
13072  */
13073     replace : function(key, o){
13074         if(arguments.length == 1){
13075             o = arguments[0];
13076             key = this.getKey(o);
13077         }
13078         var old = this.item(key);
13079         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13080              return this.add(key, o);
13081         }
13082         var index = this.indexOfKey(key);
13083         this.items[index] = o;
13084         this.map[key] = o;
13085         this.fireEvent("replace", key, old, o);
13086         return o;
13087     },
13088    
13089 /**
13090  * Adds all elements of an Array or an Object to the collection.
13091  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13092  * an Array of values, each of which are added to the collection.
13093  */
13094     addAll : function(objs){
13095         if(arguments.length > 1 || objs instanceof Array){
13096             var args = arguments.length > 1 ? arguments : objs;
13097             for(var i = 0, len = args.length; i < len; i++){
13098                 this.add(args[i]);
13099             }
13100         }else{
13101             for(var key in objs){
13102                 if(this.allowFunctions || typeof objs[key] != "function"){
13103                     this.add(key, objs[key]);
13104                 }
13105             }
13106         }
13107     },
13108    
13109 /**
13110  * Executes the specified function once for every item in the collection, passing each
13111  * item as the first and only parameter. returning false from the function will stop the iteration.
13112  * @param {Function} fn The function to execute for each item.
13113  * @param {Object} scope (optional) The scope in which to execute the function.
13114  */
13115     each : function(fn, scope){
13116         var items = [].concat(this.items); // each safe for removal
13117         for(var i = 0, len = items.length; i < len; i++){
13118             if(fn.call(scope || items[i], items[i], i, len) === false){
13119                 break;
13120             }
13121         }
13122     },
13123    
13124 /**
13125  * Executes the specified function once for every key in the collection, passing each
13126  * key, and its associated item as the first two parameters.
13127  * @param {Function} fn The function to execute for each item.
13128  * @param {Object} scope (optional) The scope in which to execute the function.
13129  */
13130     eachKey : function(fn, scope){
13131         for(var i = 0, len = this.keys.length; i < len; i++){
13132             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13133         }
13134     },
13135    
13136 /**
13137  * Returns the first item in the collection which elicits a true return value from the
13138  * passed selection function.
13139  * @param {Function} fn The selection function to execute for each item.
13140  * @param {Object} scope (optional) The scope in which to execute the function.
13141  * @return {Object} The first item in the collection which returned true from the selection function.
13142  */
13143     find : function(fn, scope){
13144         for(var i = 0, len = this.items.length; i < len; i++){
13145             if(fn.call(scope || window, this.items[i], this.keys[i])){
13146                 return this.items[i];
13147             }
13148         }
13149         return null;
13150     },
13151    
13152 /**
13153  * Inserts an item at the specified index in the collection.
13154  * @param {Number} index The index to insert the item at.
13155  * @param {String} key The key to associate with the new item, or the item itself.
13156  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13157  * @return {Object} The item inserted.
13158  */
13159     insert : function(index, key, o){
13160         if(arguments.length == 2){
13161             o = arguments[1];
13162             key = this.getKey(o);
13163         }
13164         if(index >= this.length){
13165             return this.add(key, o);
13166         }
13167         this.length++;
13168         this.items.splice(index, 0, o);
13169         if(typeof key != "undefined" && key != null){
13170             this.map[key] = o;
13171         }
13172         this.keys.splice(index, 0, key);
13173         this.fireEvent("add", index, o, key);
13174         return o;
13175     },
13176    
13177 /**
13178  * Removed an item from the collection.
13179  * @param {Object} o The item to remove.
13180  * @return {Object} The item removed.
13181  */
13182     remove : function(o){
13183         return this.removeAt(this.indexOf(o));
13184     },
13185    
13186 /**
13187  * Remove an item from a specified index in the collection.
13188  * @param {Number} index The index within the collection of the item to remove.
13189  */
13190     removeAt : function(index){
13191         if(index < this.length && index >= 0){
13192             this.length--;
13193             var o = this.items[index];
13194             this.items.splice(index, 1);
13195             var key = this.keys[index];
13196             if(typeof key != "undefined"){
13197                 delete this.map[key];
13198             }
13199             this.keys.splice(index, 1);
13200             this.fireEvent("remove", o, key);
13201         }
13202     },
13203    
13204 /**
13205  * Removed an item associated with the passed key fom the collection.
13206  * @param {String} key The key of the item to remove.
13207  */
13208     removeKey : function(key){
13209         return this.removeAt(this.indexOfKey(key));
13210     },
13211    
13212 /**
13213  * Returns the number of items in the collection.
13214  * @return {Number} the number of items in the collection.
13215  */
13216     getCount : function(){
13217         return this.length; 
13218     },
13219    
13220 /**
13221  * Returns index within the collection of the passed Object.
13222  * @param {Object} o The item to find the index of.
13223  * @return {Number} index of the item.
13224  */
13225     indexOf : function(o){
13226         if(!this.items.indexOf){
13227             for(var i = 0, len = this.items.length; i < len; i++){
13228                 if(this.items[i] == o) {
13229                     return i;
13230                 }
13231             }
13232             return -1;
13233         }else{
13234             return this.items.indexOf(o);
13235         }
13236     },
13237    
13238 /**
13239  * Returns index within the collection of the passed key.
13240  * @param {String} key The key to find the index of.
13241  * @return {Number} index of the key.
13242  */
13243     indexOfKey : function(key){
13244         if(!this.keys.indexOf){
13245             for(var i = 0, len = this.keys.length; i < len; i++){
13246                 if(this.keys[i] == key) {
13247                     return i;
13248                 }
13249             }
13250             return -1;
13251         }else{
13252             return this.keys.indexOf(key);
13253         }
13254     },
13255    
13256 /**
13257  * Returns the item associated with the passed key OR index. Key has priority over index.
13258  * @param {String/Number} key The key or index of the item.
13259  * @return {Object} The item associated with the passed key.
13260  */
13261     item : function(key){
13262         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13263         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13264     },
13265     
13266 /**
13267  * Returns the item at the specified index.
13268  * @param {Number} index The index of the item.
13269  * @return {Object}
13270  */
13271     itemAt : function(index){
13272         return this.items[index];
13273     },
13274     
13275 /**
13276  * Returns the item associated with the passed key.
13277  * @param {String/Number} key The key of the item.
13278  * @return {Object} The item associated with the passed key.
13279  */
13280     key : function(key){
13281         return this.map[key];
13282     },
13283    
13284 /**
13285  * Returns true if the collection contains the passed Object as an item.
13286  * @param {Object} o  The Object to look for in the collection.
13287  * @return {Boolean} True if the collection contains the Object as an item.
13288  */
13289     contains : function(o){
13290         return this.indexOf(o) != -1;
13291     },
13292    
13293 /**
13294  * Returns true if the collection contains the passed Object as a key.
13295  * @param {String} key The key to look for in the collection.
13296  * @return {Boolean} True if the collection contains the Object as a key.
13297  */
13298     containsKey : function(key){
13299         return typeof this.map[key] != "undefined";
13300     },
13301    
13302 /**
13303  * Removes all items from the collection.
13304  */
13305     clear : function(){
13306         this.length = 0;
13307         this.items = [];
13308         this.keys = [];
13309         this.map = {};
13310         this.fireEvent("clear");
13311     },
13312    
13313 /**
13314  * Returns the first item in the collection.
13315  * @return {Object} the first item in the collection..
13316  */
13317     first : function(){
13318         return this.items[0]; 
13319     },
13320    
13321 /**
13322  * Returns the last item in the collection.
13323  * @return {Object} the last item in the collection..
13324  */
13325     last : function(){
13326         return this.items[this.length-1];   
13327     },
13328     
13329     _sort : function(property, dir, fn){
13330         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13331         fn = fn || function(a, b){
13332             return a-b;
13333         };
13334         var c = [], k = this.keys, items = this.items;
13335         for(var i = 0, len = items.length; i < len; i++){
13336             c[c.length] = {key: k[i], value: items[i], index: i};
13337         }
13338         c.sort(function(a, b){
13339             var v = fn(a[property], b[property]) * dsc;
13340             if(v == 0){
13341                 v = (a.index < b.index ? -1 : 1);
13342             }
13343             return v;
13344         });
13345         for(var i = 0, len = c.length; i < len; i++){
13346             items[i] = c[i].value;
13347             k[i] = c[i].key;
13348         }
13349         this.fireEvent("sort", this);
13350     },
13351     
13352     /**
13353      * Sorts this collection with the passed comparison function
13354      * @param {String} direction (optional) "ASC" or "DESC"
13355      * @param {Function} fn (optional) comparison function
13356      */
13357     sort : function(dir, fn){
13358         this._sort("value", dir, fn);
13359     },
13360     
13361     /**
13362      * Sorts this collection by keys
13363      * @param {String} direction (optional) "ASC" or "DESC"
13364      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13365      */
13366     keySort : function(dir, fn){
13367         this._sort("key", dir, fn || function(a, b){
13368             return String(a).toUpperCase()-String(b).toUpperCase();
13369         });
13370     },
13371     
13372     /**
13373      * Returns a range of items in this collection
13374      * @param {Number} startIndex (optional) defaults to 0
13375      * @param {Number} endIndex (optional) default to the last item
13376      * @return {Array} An array of items
13377      */
13378     getRange : function(start, end){
13379         var items = this.items;
13380         if(items.length < 1){
13381             return [];
13382         }
13383         start = start || 0;
13384         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13385         var r = [];
13386         if(start <= end){
13387             for(var i = start; i <= end; i++) {
13388                     r[r.length] = items[i];
13389             }
13390         }else{
13391             for(var i = start; i >= end; i--) {
13392                     r[r.length] = items[i];
13393             }
13394         }
13395         return r;
13396     },
13397         
13398     /**
13399      * Filter the <i>objects</i> in this collection by a specific property. 
13400      * Returns a new collection that has been filtered.
13401      * @param {String} property A property on your objects
13402      * @param {String/RegExp} value Either string that the property values 
13403      * should start with or a RegExp to test against the property
13404      * @return {MixedCollection} The new filtered collection
13405      */
13406     filter : function(property, value){
13407         if(!value.exec){ // not a regex
13408             value = String(value);
13409             if(value.length == 0){
13410                 return this.clone();
13411             }
13412             value = new RegExp("^" + Roo.escapeRe(value), "i");
13413         }
13414         return this.filterBy(function(o){
13415             return o && value.test(o[property]);
13416         });
13417         },
13418     
13419     /**
13420      * Filter by a function. * Returns a new collection that has been filtered.
13421      * The passed function will be called with each 
13422      * object in the collection. If the function returns true, the value is included 
13423      * otherwise it is filtered.
13424      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13425      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13426      * @return {MixedCollection} The new filtered collection
13427      */
13428     filterBy : function(fn, scope){
13429         var r = new Roo.util.MixedCollection();
13430         r.getKey = this.getKey;
13431         var k = this.keys, it = this.items;
13432         for(var i = 0, len = it.length; i < len; i++){
13433             if(fn.call(scope||this, it[i], k[i])){
13434                                 r.add(k[i], it[i]);
13435                         }
13436         }
13437         return r;
13438     },
13439     
13440     /**
13441      * Creates a duplicate of this collection
13442      * @return {MixedCollection}
13443      */
13444     clone : function(){
13445         var r = new Roo.util.MixedCollection();
13446         var k = this.keys, it = this.items;
13447         for(var i = 0, len = it.length; i < len; i++){
13448             r.add(k[i], it[i]);
13449         }
13450         r.getKey = this.getKey;
13451         return r;
13452     }
13453 });
13454 /**
13455  * Returns the item associated with the passed key or index.
13456  * @method
13457  * @param {String/Number} key The key or index of the item.
13458  * @return {Object} The item associated with the passed key.
13459  */
13460 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13461  * Based on:
13462  * Ext JS Library 1.1.1
13463  * Copyright(c) 2006-2007, Ext JS, LLC.
13464  *
13465  * Originally Released Under LGPL - original licence link has changed is not relivant.
13466  *
13467  * Fork - LGPL
13468  * <script type="text/javascript">
13469  */
13470 /**
13471  * @class Roo.util.JSON
13472  * Modified version of Douglas Crockford"s json.js that doesn"t
13473  * mess with the Object prototype 
13474  * http://www.json.org/js.html
13475  * @singleton
13476  */
13477 Roo.util.JSON = new (function(){
13478     var useHasOwn = {}.hasOwnProperty ? true : false;
13479     
13480     // crashes Safari in some instances
13481     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13482     
13483     var pad = function(n) {
13484         return n < 10 ? "0" + n : n;
13485     };
13486     
13487     var m = {
13488         "\b": '\\b',
13489         "\t": '\\t',
13490         "\n": '\\n',
13491         "\f": '\\f',
13492         "\r": '\\r',
13493         '"' : '\\"',
13494         "\\": '\\\\'
13495     };
13496
13497     var encodeString = function(s){
13498         if (/["\\\x00-\x1f]/.test(s)) {
13499             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13500                 var c = m[b];
13501                 if(c){
13502                     return c;
13503                 }
13504                 c = b.charCodeAt();
13505                 return "\\u00" +
13506                     Math.floor(c / 16).toString(16) +
13507                     (c % 16).toString(16);
13508             }) + '"';
13509         }
13510         return '"' + s + '"';
13511     };
13512     
13513     var encodeArray = function(o){
13514         var a = ["["], b, i, l = o.length, v;
13515             for (i = 0; i < l; i += 1) {
13516                 v = o[i];
13517                 switch (typeof v) {
13518                     case "undefined":
13519                     case "function":
13520                     case "unknown":
13521                         break;
13522                     default:
13523                         if (b) {
13524                             a.push(',');
13525                         }
13526                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13527                         b = true;
13528                 }
13529             }
13530             a.push("]");
13531             return a.join("");
13532     };
13533     
13534     var encodeDate = function(o){
13535         return '"' + o.getFullYear() + "-" +
13536                 pad(o.getMonth() + 1) + "-" +
13537                 pad(o.getDate()) + "T" +
13538                 pad(o.getHours()) + ":" +
13539                 pad(o.getMinutes()) + ":" +
13540                 pad(o.getSeconds()) + '"';
13541     };
13542     
13543     /**
13544      * Encodes an Object, Array or other value
13545      * @param {Mixed} o The variable to encode
13546      * @return {String} The JSON string
13547      */
13548     this.encode = function(o)
13549     {
13550         // should this be extended to fully wrap stringify..
13551         
13552         if(typeof o == "undefined" || o === null){
13553             return "null";
13554         }else if(o instanceof Array){
13555             return encodeArray(o);
13556         }else if(o instanceof Date){
13557             return encodeDate(o);
13558         }else if(typeof o == "string"){
13559             return encodeString(o);
13560         }else if(typeof o == "number"){
13561             return isFinite(o) ? String(o) : "null";
13562         }else if(typeof o == "boolean"){
13563             return String(o);
13564         }else {
13565             var a = ["{"], b, i, v;
13566             for (i in o) {
13567                 if(!useHasOwn || o.hasOwnProperty(i)) {
13568                     v = o[i];
13569                     switch (typeof v) {
13570                     case "undefined":
13571                     case "function":
13572                     case "unknown":
13573                         break;
13574                     default:
13575                         if(b){
13576                             a.push(',');
13577                         }
13578                         a.push(this.encode(i), ":",
13579                                 v === null ? "null" : this.encode(v));
13580                         b = true;
13581                     }
13582                 }
13583             }
13584             a.push("}");
13585             return a.join("");
13586         }
13587     };
13588     
13589     /**
13590      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13591      * @param {String} json The JSON string
13592      * @return {Object} The resulting object
13593      */
13594     this.decode = function(json){
13595         
13596         return  /** eval:var:json */ eval("(" + json + ')');
13597     };
13598 })();
13599 /** 
13600  * Shorthand for {@link Roo.util.JSON#encode}
13601  * @member Roo encode 
13602  * @method */
13603 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13604 /** 
13605  * Shorthand for {@link Roo.util.JSON#decode}
13606  * @member Roo decode 
13607  * @method */
13608 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13609 /*
13610  * Based on:
13611  * Ext JS Library 1.1.1
13612  * Copyright(c) 2006-2007, Ext JS, LLC.
13613  *
13614  * Originally Released Under LGPL - original licence link has changed is not relivant.
13615  *
13616  * Fork - LGPL
13617  * <script type="text/javascript">
13618  */
13619  
13620 /**
13621  * @class Roo.util.Format
13622  * Reusable data formatting functions
13623  * @singleton
13624  */
13625 Roo.util.Format = function(){
13626     var trimRe = /^\s+|\s+$/g;
13627     return {
13628         /**
13629          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13630          * @param {String} value The string to truncate
13631          * @param {Number} length The maximum length to allow before truncating
13632          * @return {String} The converted text
13633          */
13634         ellipsis : function(value, len){
13635             if(value && value.length > len){
13636                 return value.substr(0, len-3)+"...";
13637             }
13638             return value;
13639         },
13640
13641         /**
13642          * Checks a reference and converts it to empty string if it is undefined
13643          * @param {Mixed} value Reference to check
13644          * @return {Mixed} Empty string if converted, otherwise the original value
13645          */
13646         undef : function(value){
13647             return typeof value != "undefined" ? value : "";
13648         },
13649
13650         /**
13651          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13652          * @param {String} value The string to encode
13653          * @return {String} The encoded text
13654          */
13655         htmlEncode : function(value){
13656             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13657         },
13658
13659         /**
13660          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13661          * @param {String} value The string to decode
13662          * @return {String} The decoded text
13663          */
13664         htmlDecode : function(value){
13665             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13666         },
13667
13668         /**
13669          * Trims any whitespace from either side of a string
13670          * @param {String} value The text to trim
13671          * @return {String} The trimmed text
13672          */
13673         trim : function(value){
13674             return String(value).replace(trimRe, "");
13675         },
13676
13677         /**
13678          * Returns a substring from within an original string
13679          * @param {String} value The original text
13680          * @param {Number} start The start index of the substring
13681          * @param {Number} length The length of the substring
13682          * @return {String} The substring
13683          */
13684         substr : function(value, start, length){
13685             return String(value).substr(start, length);
13686         },
13687
13688         /**
13689          * Converts a string to all lower case letters
13690          * @param {String} value The text to convert
13691          * @return {String} The converted text
13692          */
13693         lowercase : function(value){
13694             return String(value).toLowerCase();
13695         },
13696
13697         /**
13698          * Converts a string to all upper case letters
13699          * @param {String} value The text to convert
13700          * @return {String} The converted text
13701          */
13702         uppercase : function(value){
13703             return String(value).toUpperCase();
13704         },
13705
13706         /**
13707          * Converts the first character only of a string to upper case
13708          * @param {String} value The text to convert
13709          * @return {String} The converted text
13710          */
13711         capitalize : function(value){
13712             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13713         },
13714
13715         // private
13716         call : function(value, fn){
13717             if(arguments.length > 2){
13718                 var args = Array.prototype.slice.call(arguments, 2);
13719                 args.unshift(value);
13720                  
13721                 return /** eval:var:value */  eval(fn).apply(window, args);
13722             }else{
13723                 /** eval:var:value */
13724                 return /** eval:var:value */ eval(fn).call(window, value);
13725             }
13726         },
13727
13728        
13729         /**
13730          * safer version of Math.toFixed..??/
13731          * @param {Number/String} value The numeric value to format
13732          * @param {Number/String} value Decimal places 
13733          * @return {String} The formatted currency string
13734          */
13735         toFixed : function(v, n)
13736         {
13737             // why not use to fixed - precision is buggered???
13738             if (!n) {
13739                 return Math.round(v-0);
13740             }
13741             var fact = Math.pow(10,n+1);
13742             v = (Math.round((v-0)*fact))/fact;
13743             var z = (''+fact).substring(2);
13744             if (v == Math.floor(v)) {
13745                 return Math.floor(v) + '.' + z;
13746             }
13747             
13748             // now just padd decimals..
13749             var ps = String(v).split('.');
13750             var fd = (ps[1] + z);
13751             var r = fd.substring(0,n); 
13752             var rm = fd.substring(n); 
13753             if (rm < 5) {
13754                 return ps[0] + '.' + r;
13755             }
13756             r*=1; // turn it into a number;
13757             r++;
13758             if (String(r).length != n) {
13759                 ps[0]*=1;
13760                 ps[0]++;
13761                 r = String(r).substring(1); // chop the end off.
13762             }
13763             
13764             return ps[0] + '.' + r;
13765              
13766         },
13767         
13768         /**
13769          * Format a number as US currency
13770          * @param {Number/String} value The numeric value to format
13771          * @return {String} The formatted currency string
13772          */
13773         usMoney : function(v){
13774             return '$' + Roo.util.Format.number(v);
13775         },
13776         
13777         /**
13778          * Format a number
13779          * eventually this should probably emulate php's number_format
13780          * @param {Number/String} value The numeric value to format
13781          * @param {Number} decimals number of decimal places
13782          * @param {String} delimiter for thousands (default comma)
13783          * @return {String} The formatted currency string
13784          */
13785         number : function(v, decimals, thousandsDelimiter)
13786         {
13787             // multiply and round.
13788             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13789             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13790             
13791             var mul = Math.pow(10, decimals);
13792             var zero = String(mul).substring(1);
13793             v = (Math.round((v-0)*mul))/mul;
13794             
13795             // if it's '0' number.. then
13796             
13797             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13798             v = String(v);
13799             var ps = v.split('.');
13800             var whole = ps[0];
13801             
13802             var r = /(\d+)(\d{3})/;
13803             // add comma's
13804             
13805             if(thousandsDelimiter.length != 0) {
13806                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13807             } 
13808             
13809             var sub = ps[1] ?
13810                     // has decimals..
13811                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13812                     // does not have decimals
13813                     (decimals ? ('.' + zero) : '');
13814             
13815             
13816             return whole + sub ;
13817         },
13818         
13819         /**
13820          * Parse a value into a formatted date using the specified format pattern.
13821          * @param {Mixed} value The value to format
13822          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13823          * @return {String} The formatted date string
13824          */
13825         date : function(v, format){
13826             if(!v){
13827                 return "";
13828             }
13829             if(!(v instanceof Date)){
13830                 v = new Date(Date.parse(v));
13831             }
13832             return v.dateFormat(format || Roo.util.Format.defaults.date);
13833         },
13834
13835         /**
13836          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13837          * @param {String} format Any valid date format string
13838          * @return {Function} The date formatting function
13839          */
13840         dateRenderer : function(format){
13841             return function(v){
13842                 return Roo.util.Format.date(v, format);  
13843             };
13844         },
13845
13846         // private
13847         stripTagsRE : /<\/?[^>]+>/gi,
13848         
13849         /**
13850          * Strips all HTML tags
13851          * @param {Mixed} value The text from which to strip tags
13852          * @return {String} The stripped text
13853          */
13854         stripTags : function(v){
13855             return !v ? v : String(v).replace(this.stripTagsRE, "");
13856         }
13857     };
13858 }();
13859 Roo.util.Format.defaults = {
13860     date : 'd/M/Y'
13861 };/*
13862  * Based on:
13863  * Ext JS Library 1.1.1
13864  * Copyright(c) 2006-2007, Ext JS, LLC.
13865  *
13866  * Originally Released Under LGPL - original licence link has changed is not relivant.
13867  *
13868  * Fork - LGPL
13869  * <script type="text/javascript">
13870  */
13871
13872
13873  
13874
13875 /**
13876  * @class Roo.MasterTemplate
13877  * @extends Roo.Template
13878  * Provides a template that can have child templates. The syntax is:
13879 <pre><code>
13880 var t = new Roo.MasterTemplate(
13881         '&lt;select name="{name}"&gt;',
13882                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13883         '&lt;/select&gt;'
13884 );
13885 t.add('options', {value: 'foo', text: 'bar'});
13886 // or you can add multiple child elements in one shot
13887 t.addAll('options', [
13888     {value: 'foo', text: 'bar'},
13889     {value: 'foo2', text: 'bar2'},
13890     {value: 'foo3', text: 'bar3'}
13891 ]);
13892 // then append, applying the master template values
13893 t.append('my-form', {name: 'my-select'});
13894 </code></pre>
13895 * A name attribute for the child template is not required if you have only one child
13896 * template or you want to refer to them by index.
13897  */
13898 Roo.MasterTemplate = function(){
13899     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13900     this.originalHtml = this.html;
13901     var st = {};
13902     var m, re = this.subTemplateRe;
13903     re.lastIndex = 0;
13904     var subIndex = 0;
13905     while(m = re.exec(this.html)){
13906         var name = m[1], content = m[2];
13907         st[subIndex] = {
13908             name: name,
13909             index: subIndex,
13910             buffer: [],
13911             tpl : new Roo.Template(content)
13912         };
13913         if(name){
13914             st[name] = st[subIndex];
13915         }
13916         st[subIndex].tpl.compile();
13917         st[subIndex].tpl.call = this.call.createDelegate(this);
13918         subIndex++;
13919     }
13920     this.subCount = subIndex;
13921     this.subs = st;
13922 };
13923 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13924     /**
13925     * The regular expression used to match sub templates
13926     * @type RegExp
13927     * @property
13928     */
13929     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13930
13931     /**
13932      * Applies the passed values to a child template.
13933      * @param {String/Number} name (optional) The name or index of the child template
13934      * @param {Array/Object} values The values to be applied to the template
13935      * @return {MasterTemplate} this
13936      */
13937      add : function(name, values){
13938         if(arguments.length == 1){
13939             values = arguments[0];
13940             name = 0;
13941         }
13942         var s = this.subs[name];
13943         s.buffer[s.buffer.length] = s.tpl.apply(values);
13944         return this;
13945     },
13946
13947     /**
13948      * Applies all the passed values to a child template.
13949      * @param {String/Number} name (optional) The name or index of the child template
13950      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13951      * @param {Boolean} reset (optional) True to reset the template first
13952      * @return {MasterTemplate} this
13953      */
13954     fill : function(name, values, reset){
13955         var a = arguments;
13956         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13957             values = a[0];
13958             name = 0;
13959             reset = a[1];
13960         }
13961         if(reset){
13962             this.reset();
13963         }
13964         for(var i = 0, len = values.length; i < len; i++){
13965             this.add(name, values[i]);
13966         }
13967         return this;
13968     },
13969
13970     /**
13971      * Resets the template for reuse
13972      * @return {MasterTemplate} this
13973      */
13974      reset : function(){
13975         var s = this.subs;
13976         for(var i = 0; i < this.subCount; i++){
13977             s[i].buffer = [];
13978         }
13979         return this;
13980     },
13981
13982     applyTemplate : function(values){
13983         var s = this.subs;
13984         var replaceIndex = -1;
13985         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13986             return s[++replaceIndex].buffer.join("");
13987         });
13988         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13989     },
13990
13991     apply : function(){
13992         return this.applyTemplate.apply(this, arguments);
13993     },
13994
13995     compile : function(){return this;}
13996 });
13997
13998 /**
13999  * Alias for fill().
14000  * @method
14001  */
14002 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14003  /**
14004  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14005  * var tpl = Roo.MasterTemplate.from('element-id');
14006  * @param {String/HTMLElement} el
14007  * @param {Object} config
14008  * @static
14009  */
14010 Roo.MasterTemplate.from = function(el, config){
14011     el = Roo.getDom(el);
14012     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14013 };/*
14014  * Based on:
14015  * Ext JS Library 1.1.1
14016  * Copyright(c) 2006-2007, Ext JS, LLC.
14017  *
14018  * Originally Released Under LGPL - original licence link has changed is not relivant.
14019  *
14020  * Fork - LGPL
14021  * <script type="text/javascript">
14022  */
14023
14024  
14025 /**
14026  * @class Roo.util.CSS
14027  * Utility class for manipulating CSS rules
14028  * @singleton
14029  */
14030 Roo.util.CSS = function(){
14031         var rules = null;
14032         var doc = document;
14033
14034     var camelRe = /(-[a-z])/gi;
14035     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14036
14037    return {
14038    /**
14039     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14040     * tag and appended to the HEAD of the document.
14041     * @param {String|Object} cssText The text containing the css rules
14042     * @param {String} id An id to add to the stylesheet for later removal
14043     * @return {StyleSheet}
14044     */
14045     createStyleSheet : function(cssText, id){
14046         var ss;
14047         var head = doc.getElementsByTagName("head")[0];
14048         var nrules = doc.createElement("style");
14049         nrules.setAttribute("type", "text/css");
14050         if(id){
14051             nrules.setAttribute("id", id);
14052         }
14053         if (typeof(cssText) != 'string') {
14054             // support object maps..
14055             // not sure if this a good idea.. 
14056             // perhaps it should be merged with the general css handling
14057             // and handle js style props.
14058             var cssTextNew = [];
14059             for(var n in cssText) {
14060                 var citems = [];
14061                 for(var k in cssText[n]) {
14062                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14063                 }
14064                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14065                 
14066             }
14067             cssText = cssTextNew.join("\n");
14068             
14069         }
14070        
14071        
14072        if(Roo.isIE){
14073            head.appendChild(nrules);
14074            ss = nrules.styleSheet;
14075            ss.cssText = cssText;
14076        }else{
14077            try{
14078                 nrules.appendChild(doc.createTextNode(cssText));
14079            }catch(e){
14080                nrules.cssText = cssText; 
14081            }
14082            head.appendChild(nrules);
14083            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14084        }
14085        this.cacheStyleSheet(ss);
14086        return ss;
14087    },
14088
14089    /**
14090     * Removes a style or link tag by id
14091     * @param {String} id The id of the tag
14092     */
14093    removeStyleSheet : function(id){
14094        var existing = doc.getElementById(id);
14095        if(existing){
14096            existing.parentNode.removeChild(existing);
14097        }
14098    },
14099
14100    /**
14101     * Dynamically swaps an existing stylesheet reference for a new one
14102     * @param {String} id The id of an existing link tag to remove
14103     * @param {String} url The href of the new stylesheet to include
14104     */
14105    swapStyleSheet : function(id, url){
14106        this.removeStyleSheet(id);
14107        var ss = doc.createElement("link");
14108        ss.setAttribute("rel", "stylesheet");
14109        ss.setAttribute("type", "text/css");
14110        ss.setAttribute("id", id);
14111        ss.setAttribute("href", url);
14112        doc.getElementsByTagName("head")[0].appendChild(ss);
14113    },
14114    
14115    /**
14116     * Refresh the rule cache if you have dynamically added stylesheets
14117     * @return {Object} An object (hash) of rules indexed by selector
14118     */
14119    refreshCache : function(){
14120        return this.getRules(true);
14121    },
14122
14123    // private
14124    cacheStyleSheet : function(stylesheet){
14125        if(!rules){
14126            rules = {};
14127        }
14128        try{// try catch for cross domain access issue
14129            var ssRules = stylesheet.cssRules || stylesheet.rules;
14130            for(var j = ssRules.length-1; j >= 0; --j){
14131                rules[ssRules[j].selectorText] = ssRules[j];
14132            }
14133        }catch(e){}
14134    },
14135    
14136    /**
14137     * Gets all css rules for the document
14138     * @param {Boolean} refreshCache true to refresh the internal cache
14139     * @return {Object} An object (hash) of rules indexed by selector
14140     */
14141    getRules : function(refreshCache){
14142                 if(rules == null || refreshCache){
14143                         rules = {};
14144                         var ds = doc.styleSheets;
14145                         for(var i =0, len = ds.length; i < len; i++){
14146                             try{
14147                         this.cacheStyleSheet(ds[i]);
14148                     }catch(e){} 
14149                 }
14150                 }
14151                 return rules;
14152         },
14153         
14154         /**
14155     * Gets an an individual CSS rule by selector(s)
14156     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14157     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14158     * @return {CSSRule} The CSS rule or null if one is not found
14159     */
14160    getRule : function(selector, refreshCache){
14161                 var rs = this.getRules(refreshCache);
14162                 if(!(selector instanceof Array)){
14163                     return rs[selector];
14164                 }
14165                 for(var i = 0; i < selector.length; i++){
14166                         if(rs[selector[i]]){
14167                                 return rs[selector[i]];
14168                         }
14169                 }
14170                 return null;
14171         },
14172         
14173         
14174         /**
14175     * Updates a rule property
14176     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14177     * @param {String} property The css property
14178     * @param {String} value The new value for the property
14179     * @return {Boolean} true If a rule was found and updated
14180     */
14181    updateRule : function(selector, property, value){
14182                 if(!(selector instanceof Array)){
14183                         var rule = this.getRule(selector);
14184                         if(rule){
14185                                 rule.style[property.replace(camelRe, camelFn)] = value;
14186                                 return true;
14187                         }
14188                 }else{
14189                         for(var i = 0; i < selector.length; i++){
14190                                 if(this.updateRule(selector[i], property, value)){
14191                                         return true;
14192                                 }
14193                         }
14194                 }
14195                 return false;
14196         }
14197    };   
14198 }();/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209  
14210
14211 /**
14212  * @class Roo.util.ClickRepeater
14213  * @extends Roo.util.Observable
14214  * 
14215  * A wrapper class which can be applied to any element. Fires a "click" event while the
14216  * mouse is pressed. The interval between firings may be specified in the config but
14217  * defaults to 10 milliseconds.
14218  * 
14219  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14220  * 
14221  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14222  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14223  * Similar to an autorepeat key delay.
14224  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14225  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14226  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14227  *           "interval" and "delay" are ignored. "immediate" is honored.
14228  * @cfg {Boolean} preventDefault True to prevent the default click event
14229  * @cfg {Boolean} stopDefault True to stop the default click event
14230  * 
14231  * @history
14232  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14233  *     2007-02-02 jvs Renamed to ClickRepeater
14234  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14235  *
14236  *  @constructor
14237  * @param {String/HTMLElement/Element} el The element to listen on
14238  * @param {Object} config
14239  **/
14240 Roo.util.ClickRepeater = function(el, config)
14241 {
14242     this.el = Roo.get(el);
14243     this.el.unselectable();
14244
14245     Roo.apply(this, config);
14246
14247     this.addEvents({
14248     /**
14249      * @event mousedown
14250      * Fires when the mouse button is depressed.
14251      * @param {Roo.util.ClickRepeater} this
14252      */
14253         "mousedown" : true,
14254     /**
14255      * @event click
14256      * Fires on a specified interval during the time the element is pressed.
14257      * @param {Roo.util.ClickRepeater} this
14258      */
14259         "click" : true,
14260     /**
14261      * @event mouseup
14262      * Fires when the mouse key is released.
14263      * @param {Roo.util.ClickRepeater} this
14264      */
14265         "mouseup" : true
14266     });
14267
14268     this.el.on("mousedown", this.handleMouseDown, this);
14269     if(this.preventDefault || this.stopDefault){
14270         this.el.on("click", function(e){
14271             if(this.preventDefault){
14272                 e.preventDefault();
14273             }
14274             if(this.stopDefault){
14275                 e.stopEvent();
14276             }
14277         }, this);
14278     }
14279
14280     // allow inline handler
14281     if(this.handler){
14282         this.on("click", this.handler,  this.scope || this);
14283     }
14284
14285     Roo.util.ClickRepeater.superclass.constructor.call(this);
14286 };
14287
14288 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14289     interval : 20,
14290     delay: 250,
14291     preventDefault : true,
14292     stopDefault : false,
14293     timer : 0,
14294
14295     // private
14296     handleMouseDown : function(){
14297         clearTimeout(this.timer);
14298         this.el.blur();
14299         if(this.pressClass){
14300             this.el.addClass(this.pressClass);
14301         }
14302         this.mousedownTime = new Date();
14303
14304         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14305         this.el.on("mouseout", this.handleMouseOut, this);
14306
14307         this.fireEvent("mousedown", this);
14308         this.fireEvent("click", this);
14309         
14310         this.timer = this.click.defer(this.delay || this.interval, this);
14311     },
14312
14313     // private
14314     click : function(){
14315         this.fireEvent("click", this);
14316         this.timer = this.click.defer(this.getInterval(), this);
14317     },
14318
14319     // private
14320     getInterval: function(){
14321         if(!this.accelerate){
14322             return this.interval;
14323         }
14324         var pressTime = this.mousedownTime.getElapsed();
14325         if(pressTime < 500){
14326             return 400;
14327         }else if(pressTime < 1700){
14328             return 320;
14329         }else if(pressTime < 2600){
14330             return 250;
14331         }else if(pressTime < 3500){
14332             return 180;
14333         }else if(pressTime < 4400){
14334             return 140;
14335         }else if(pressTime < 5300){
14336             return 80;
14337         }else if(pressTime < 6200){
14338             return 50;
14339         }else{
14340             return 10;
14341         }
14342     },
14343
14344     // private
14345     handleMouseOut : function(){
14346         clearTimeout(this.timer);
14347         if(this.pressClass){
14348             this.el.removeClass(this.pressClass);
14349         }
14350         this.el.on("mouseover", this.handleMouseReturn, this);
14351     },
14352
14353     // private
14354     handleMouseReturn : function(){
14355         this.el.un("mouseover", this.handleMouseReturn);
14356         if(this.pressClass){
14357             this.el.addClass(this.pressClass);
14358         }
14359         this.click();
14360     },
14361
14362     // private
14363     handleMouseUp : function(){
14364         clearTimeout(this.timer);
14365         this.el.un("mouseover", this.handleMouseReturn);
14366         this.el.un("mouseout", this.handleMouseOut);
14367         Roo.get(document).un("mouseup", this.handleMouseUp);
14368         this.el.removeClass(this.pressClass);
14369         this.fireEvent("mouseup", this);
14370     }
14371 });/*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381
14382  
14383 /**
14384  * @class Roo.KeyNav
14385  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14386  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14387  * way to implement custom navigation schemes for any UI component.</p>
14388  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14389  * pageUp, pageDown, del, home, end.  Usage:</p>
14390  <pre><code>
14391 var nav = new Roo.KeyNav("my-element", {
14392     "left" : function(e){
14393         this.moveLeft(e.ctrlKey);
14394     },
14395     "right" : function(e){
14396         this.moveRight(e.ctrlKey);
14397     },
14398     "enter" : function(e){
14399         this.save();
14400     },
14401     scope : this
14402 });
14403 </code></pre>
14404  * @constructor
14405  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14406  * @param {Object} config The config
14407  */
14408 Roo.KeyNav = function(el, config){
14409     this.el = Roo.get(el);
14410     Roo.apply(this, config);
14411     if(!this.disabled){
14412         this.disabled = true;
14413         this.enable();
14414     }
14415 };
14416
14417 Roo.KeyNav.prototype = {
14418     /**
14419      * @cfg {Boolean} disabled
14420      * True to disable this KeyNav instance (defaults to false)
14421      */
14422     disabled : false,
14423     /**
14424      * @cfg {String} defaultEventAction
14425      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14426      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14427      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14428      */
14429     defaultEventAction: "stopEvent",
14430     /**
14431      * @cfg {Boolean} forceKeyDown
14432      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14433      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14434      * handle keydown instead of keypress.
14435      */
14436     forceKeyDown : false,
14437
14438     // private
14439     prepareEvent : function(e){
14440         var k = e.getKey();
14441         var h = this.keyToHandler[k];
14442         //if(h && this[h]){
14443         //    e.stopPropagation();
14444         //}
14445         if(Roo.isSafari && h && k >= 37 && k <= 40){
14446             e.stopEvent();
14447         }
14448     },
14449
14450     // private
14451     relay : function(e){
14452         var k = e.getKey();
14453         var h = this.keyToHandler[k];
14454         if(h && this[h]){
14455             if(this.doRelay(e, this[h], h) !== true){
14456                 e[this.defaultEventAction]();
14457             }
14458         }
14459     },
14460
14461     // private
14462     doRelay : function(e, h, hname){
14463         return h.call(this.scope || this, e);
14464     },
14465
14466     // possible handlers
14467     enter : false,
14468     left : false,
14469     right : false,
14470     up : false,
14471     down : false,
14472     tab : false,
14473     esc : false,
14474     pageUp : false,
14475     pageDown : false,
14476     del : false,
14477     home : false,
14478     end : false,
14479
14480     // quick lookup hash
14481     keyToHandler : {
14482         37 : "left",
14483         39 : "right",
14484         38 : "up",
14485         40 : "down",
14486         33 : "pageUp",
14487         34 : "pageDown",
14488         46 : "del",
14489         36 : "home",
14490         35 : "end",
14491         13 : "enter",
14492         27 : "esc",
14493         9  : "tab"
14494     },
14495
14496         /**
14497          * Enable this KeyNav
14498          */
14499         enable: function(){
14500                 if(this.disabled){
14501             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14502             // the EventObject will normalize Safari automatically
14503             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14504                 this.el.on("keydown", this.relay,  this);
14505             }else{
14506                 this.el.on("keydown", this.prepareEvent,  this);
14507                 this.el.on("keypress", this.relay,  this);
14508             }
14509                     this.disabled = false;
14510                 }
14511         },
14512
14513         /**
14514          * Disable this KeyNav
14515          */
14516         disable: function(){
14517                 if(!this.disabled){
14518                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14519                 this.el.un("keydown", this.relay);
14520             }else{
14521                 this.el.un("keydown", this.prepareEvent);
14522                 this.el.un("keypress", this.relay);
14523             }
14524                     this.disabled = true;
14525                 }
14526         }
14527 };/*
14528  * Based on:
14529  * Ext JS Library 1.1.1
14530  * Copyright(c) 2006-2007, Ext JS, LLC.
14531  *
14532  * Originally Released Under LGPL - original licence link has changed is not relivant.
14533  *
14534  * Fork - LGPL
14535  * <script type="text/javascript">
14536  */
14537
14538  
14539 /**
14540  * @class Roo.KeyMap
14541  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14542  * The constructor accepts the same config object as defined by {@link #addBinding}.
14543  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14544  * combination it will call the function with this signature (if the match is a multi-key
14545  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14546  * A KeyMap can also handle a string representation of keys.<br />
14547  * Usage:
14548  <pre><code>
14549 // map one key by key code
14550 var map = new Roo.KeyMap("my-element", {
14551     key: 13, // or Roo.EventObject.ENTER
14552     fn: myHandler,
14553     scope: myObject
14554 });
14555
14556 // map multiple keys to one action by string
14557 var map = new Roo.KeyMap("my-element", {
14558     key: "a\r\n\t",
14559     fn: myHandler,
14560     scope: myObject
14561 });
14562
14563 // map multiple keys to multiple actions by strings and array of codes
14564 var map = new Roo.KeyMap("my-element", [
14565     {
14566         key: [10,13],
14567         fn: function(){ alert("Return was pressed"); }
14568     }, {
14569         key: "abc",
14570         fn: function(){ alert('a, b or c was pressed'); }
14571     }, {
14572         key: "\t",
14573         ctrl:true,
14574         shift:true,
14575         fn: function(){ alert('Control + shift + tab was pressed.'); }
14576     }
14577 ]);
14578 </code></pre>
14579  * <b>Note: A KeyMap starts enabled</b>
14580  * @constructor
14581  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14582  * @param {Object} config The config (see {@link #addBinding})
14583  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14584  */
14585 Roo.KeyMap = function(el, config, eventName){
14586     this.el  = Roo.get(el);
14587     this.eventName = eventName || "keydown";
14588     this.bindings = [];
14589     if(config){
14590         this.addBinding(config);
14591     }
14592     this.enable();
14593 };
14594
14595 Roo.KeyMap.prototype = {
14596     /**
14597      * True to stop the event from bubbling and prevent the default browser action if the
14598      * key was handled by the KeyMap (defaults to false)
14599      * @type Boolean
14600      */
14601     stopEvent : false,
14602
14603     /**
14604      * Add a new binding to this KeyMap. The following config object properties are supported:
14605      * <pre>
14606 Property    Type             Description
14607 ----------  ---------------  ----------------------------------------------------------------------
14608 key         String/Array     A single keycode or an array of keycodes to handle
14609 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14610 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14611 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14612 fn          Function         The function to call when KeyMap finds the expected key combination
14613 scope       Object           The scope of the callback function
14614 </pre>
14615      *
14616      * Usage:
14617      * <pre><code>
14618 // Create a KeyMap
14619 var map = new Roo.KeyMap(document, {
14620     key: Roo.EventObject.ENTER,
14621     fn: handleKey,
14622     scope: this
14623 });
14624
14625 //Add a new binding to the existing KeyMap later
14626 map.addBinding({
14627     key: 'abc',
14628     shift: true,
14629     fn: handleKey,
14630     scope: this
14631 });
14632 </code></pre>
14633      * @param {Object/Array} config A single KeyMap config or an array of configs
14634      */
14635         addBinding : function(config){
14636         if(config instanceof Array){
14637             for(var i = 0, len = config.length; i < len; i++){
14638                 this.addBinding(config[i]);
14639             }
14640             return;
14641         }
14642         var keyCode = config.key,
14643             shift = config.shift, 
14644             ctrl = config.ctrl, 
14645             alt = config.alt,
14646             fn = config.fn,
14647             scope = config.scope;
14648         if(typeof keyCode == "string"){
14649             var ks = [];
14650             var keyString = keyCode.toUpperCase();
14651             for(var j = 0, len = keyString.length; j < len; j++){
14652                 ks.push(keyString.charCodeAt(j));
14653             }
14654             keyCode = ks;
14655         }
14656         var keyArray = keyCode instanceof Array;
14657         var handler = function(e){
14658             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14659                 var k = e.getKey();
14660                 if(keyArray){
14661                     for(var i = 0, len = keyCode.length; i < len; i++){
14662                         if(keyCode[i] == k){
14663                           if(this.stopEvent){
14664                               e.stopEvent();
14665                           }
14666                           fn.call(scope || window, k, e);
14667                           return;
14668                         }
14669                     }
14670                 }else{
14671                     if(k == keyCode){
14672                         if(this.stopEvent){
14673                            e.stopEvent();
14674                         }
14675                         fn.call(scope || window, k, e);
14676                     }
14677                 }
14678             }
14679         };
14680         this.bindings.push(handler);  
14681         },
14682
14683     /**
14684      * Shorthand for adding a single key listener
14685      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14686      * following options:
14687      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14688      * @param {Function} fn The function to call
14689      * @param {Object} scope (optional) The scope of the function
14690      */
14691     on : function(key, fn, scope){
14692         var keyCode, shift, ctrl, alt;
14693         if(typeof key == "object" && !(key instanceof Array)){
14694             keyCode = key.key;
14695             shift = key.shift;
14696             ctrl = key.ctrl;
14697             alt = key.alt;
14698         }else{
14699             keyCode = key;
14700         }
14701         this.addBinding({
14702             key: keyCode,
14703             shift: shift,
14704             ctrl: ctrl,
14705             alt: alt,
14706             fn: fn,
14707             scope: scope
14708         })
14709     },
14710
14711     // private
14712     handleKeyDown : function(e){
14713             if(this.enabled){ //just in case
14714             var b = this.bindings;
14715             for(var i = 0, len = b.length; i < len; i++){
14716                 b[i].call(this, e);
14717             }
14718             }
14719         },
14720         
14721         /**
14722          * Returns true if this KeyMap is enabled
14723          * @return {Boolean} 
14724          */
14725         isEnabled : function(){
14726             return this.enabled;  
14727         },
14728         
14729         /**
14730          * Enables this KeyMap
14731          */
14732         enable: function(){
14733                 if(!this.enabled){
14734                     this.el.on(this.eventName, this.handleKeyDown, this);
14735                     this.enabled = true;
14736                 }
14737         },
14738
14739         /**
14740          * Disable this KeyMap
14741          */
14742         disable: function(){
14743                 if(this.enabled){
14744                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14745                     this.enabled = false;
14746                 }
14747         }
14748 };/*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758
14759  
14760 /**
14761  * @class Roo.util.TextMetrics
14762  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14763  * wide, in pixels, a given block of text will be.
14764  * @singleton
14765  */
14766 Roo.util.TextMetrics = function(){
14767     var shared;
14768     return {
14769         /**
14770          * Measures the size of the specified text
14771          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14772          * that can affect the size of the rendered text
14773          * @param {String} text The text to measure
14774          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14775          * in order to accurately measure the text height
14776          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14777          */
14778         measure : function(el, text, fixedWidth){
14779             if(!shared){
14780                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14781             }
14782             shared.bind(el);
14783             shared.setFixedWidth(fixedWidth || 'auto');
14784             return shared.getSize(text);
14785         },
14786
14787         /**
14788          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14789          * the overhead of multiple calls to initialize the style properties on each measurement.
14790          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14791          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14792          * in order to accurately measure the text height
14793          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14794          */
14795         createInstance : function(el, fixedWidth){
14796             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14797         }
14798     };
14799 }();
14800
14801  
14802
14803 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14804     var ml = new Roo.Element(document.createElement('div'));
14805     document.body.appendChild(ml.dom);
14806     ml.position('absolute');
14807     ml.setLeftTop(-1000, -1000);
14808     ml.hide();
14809
14810     if(fixedWidth){
14811         ml.setWidth(fixedWidth);
14812     }
14813      
14814     var instance = {
14815         /**
14816          * Returns the size of the specified text based on the internal element's style and width properties
14817          * @memberOf Roo.util.TextMetrics.Instance#
14818          * @param {String} text The text to measure
14819          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14820          */
14821         getSize : function(text){
14822             ml.update(text);
14823             var s = ml.getSize();
14824             ml.update('');
14825             return s;
14826         },
14827
14828         /**
14829          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14830          * that can affect the size of the rendered text
14831          * @memberOf Roo.util.TextMetrics.Instance#
14832          * @param {String/HTMLElement} el The element, dom node or id
14833          */
14834         bind : function(el){
14835             ml.setStyle(
14836                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14837             );
14838         },
14839
14840         /**
14841          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14842          * to set a fixed width in order to accurately measure the text height.
14843          * @memberOf Roo.util.TextMetrics.Instance#
14844          * @param {Number} width The width to set on the element
14845          */
14846         setFixedWidth : function(width){
14847             ml.setWidth(width);
14848         },
14849
14850         /**
14851          * Returns the measured width of the specified text
14852          * @memberOf Roo.util.TextMetrics.Instance#
14853          * @param {String} text The text to measure
14854          * @return {Number} width The width in pixels
14855          */
14856         getWidth : function(text){
14857             ml.dom.style.width = 'auto';
14858             return this.getSize(text).width;
14859         },
14860
14861         /**
14862          * Returns the measured height of the specified text.  For multiline text, be sure to call
14863          * {@link #setFixedWidth} if necessary.
14864          * @memberOf Roo.util.TextMetrics.Instance#
14865          * @param {String} text The text to measure
14866          * @return {Number} height The height in pixels
14867          */
14868         getHeight : function(text){
14869             return this.getSize(text).height;
14870         }
14871     };
14872
14873     instance.bind(bindTo);
14874
14875     return instance;
14876 };
14877
14878 // backwards compat
14879 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14880  * Based on:
14881  * Ext JS Library 1.1.1
14882  * Copyright(c) 2006-2007, Ext JS, LLC.
14883  *
14884  * Originally Released Under LGPL - original licence link has changed is not relivant.
14885  *
14886  * Fork - LGPL
14887  * <script type="text/javascript">
14888  */
14889
14890 /**
14891  * @class Roo.state.Provider
14892  * Abstract base class for state provider implementations. This class provides methods
14893  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14894  * Provider interface.
14895  */
14896 Roo.state.Provider = function(){
14897     /**
14898      * @event statechange
14899      * Fires when a state change occurs.
14900      * @param {Provider} this This state provider
14901      * @param {String} key The state key which was changed
14902      * @param {String} value The encoded value for the state
14903      */
14904     this.addEvents({
14905         "statechange": true
14906     });
14907     this.state = {};
14908     Roo.state.Provider.superclass.constructor.call(this);
14909 };
14910 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14911     /**
14912      * Returns the current value for a key
14913      * @param {String} name The key name
14914      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14915      * @return {Mixed} The state data
14916      */
14917     get : function(name, defaultValue){
14918         return typeof this.state[name] == "undefined" ?
14919             defaultValue : this.state[name];
14920     },
14921     
14922     /**
14923      * Clears a value from the state
14924      * @param {String} name The key name
14925      */
14926     clear : function(name){
14927         delete this.state[name];
14928         this.fireEvent("statechange", this, name, null);
14929     },
14930     
14931     /**
14932      * Sets the value for a key
14933      * @param {String} name The key name
14934      * @param {Mixed} value The value to set
14935      */
14936     set : function(name, value){
14937         this.state[name] = value;
14938         this.fireEvent("statechange", this, name, value);
14939     },
14940     
14941     /**
14942      * Decodes a string previously encoded with {@link #encodeValue}.
14943      * @param {String} value The value to decode
14944      * @return {Mixed} The decoded value
14945      */
14946     decodeValue : function(cookie){
14947         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14948         var matches = re.exec(unescape(cookie));
14949         if(!matches || !matches[1]) {
14950             return; // non state cookie
14951         }
14952         var type = matches[1];
14953         var v = matches[2];
14954         switch(type){
14955             case "n":
14956                 return parseFloat(v);
14957             case "d":
14958                 return new Date(Date.parse(v));
14959             case "b":
14960                 return (v == "1");
14961             case "a":
14962                 var all = [];
14963                 var values = v.split("^");
14964                 for(var i = 0, len = values.length; i < len; i++){
14965                     all.push(this.decodeValue(values[i]));
14966                 }
14967                 return all;
14968            case "o":
14969                 var all = {};
14970                 var values = v.split("^");
14971                 for(var i = 0, len = values.length; i < len; i++){
14972                     var kv = values[i].split("=");
14973                     all[kv[0]] = this.decodeValue(kv[1]);
14974                 }
14975                 return all;
14976            default:
14977                 return v;
14978         }
14979     },
14980     
14981     /**
14982      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14983      * @param {Mixed} value The value to encode
14984      * @return {String} The encoded value
14985      */
14986     encodeValue : function(v){
14987         var enc;
14988         if(typeof v == "number"){
14989             enc = "n:" + v;
14990         }else if(typeof v == "boolean"){
14991             enc = "b:" + (v ? "1" : "0");
14992         }else if(v instanceof Date){
14993             enc = "d:" + v.toGMTString();
14994         }else if(v instanceof Array){
14995             var flat = "";
14996             for(var i = 0, len = v.length; i < len; i++){
14997                 flat += this.encodeValue(v[i]);
14998                 if(i != len-1) {
14999                     flat += "^";
15000                 }
15001             }
15002             enc = "a:" + flat;
15003         }else if(typeof v == "object"){
15004             var flat = "";
15005             for(var key in v){
15006                 if(typeof v[key] != "function"){
15007                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15008                 }
15009             }
15010             enc = "o:" + flat.substring(0, flat.length-1);
15011         }else{
15012             enc = "s:" + v;
15013         }
15014         return escape(enc);        
15015     }
15016 });
15017
15018 /*
15019  * Based on:
15020  * Ext JS Library 1.1.1
15021  * Copyright(c) 2006-2007, Ext JS, LLC.
15022  *
15023  * Originally Released Under LGPL - original licence link has changed is not relivant.
15024  *
15025  * Fork - LGPL
15026  * <script type="text/javascript">
15027  */
15028 /**
15029  * @class Roo.state.Manager
15030  * This is the global state manager. By default all components that are "state aware" check this class
15031  * for state information if you don't pass them a custom state provider. In order for this class
15032  * to be useful, it must be initialized with a provider when your application initializes.
15033  <pre><code>
15034 // in your initialization function
15035 init : function(){
15036    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15037    ...
15038    // supposed you have a {@link Roo.BorderLayout}
15039    var layout = new Roo.BorderLayout(...);
15040    layout.restoreState();
15041    // or a {Roo.BasicDialog}
15042    var dialog = new Roo.BasicDialog(...);
15043    dialog.restoreState();
15044  </code></pre>
15045  * @singleton
15046  */
15047 Roo.state.Manager = function(){
15048     var provider = new Roo.state.Provider();
15049     
15050     return {
15051         /**
15052          * Configures the default state provider for your application
15053          * @param {Provider} stateProvider The state provider to set
15054          */
15055         setProvider : function(stateProvider){
15056             provider = stateProvider;
15057         },
15058         
15059         /**
15060          * Returns the current value for a key
15061          * @param {String} name The key name
15062          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15063          * @return {Mixed} The state data
15064          */
15065         get : function(key, defaultValue){
15066             return provider.get(key, defaultValue);
15067         },
15068         
15069         /**
15070          * Sets the value for a key
15071          * @param {String} name The key name
15072          * @param {Mixed} value The state data
15073          */
15074          set : function(key, value){
15075             provider.set(key, value);
15076         },
15077         
15078         /**
15079          * Clears a value from the state
15080          * @param {String} name The key name
15081          */
15082         clear : function(key){
15083             provider.clear(key);
15084         },
15085         
15086         /**
15087          * Gets the currently configured state provider
15088          * @return {Provider} The state provider
15089          */
15090         getProvider : function(){
15091             return provider;
15092         }
15093     };
15094 }();
15095 /*
15096  * Based on:
15097  * Ext JS Library 1.1.1
15098  * Copyright(c) 2006-2007, Ext JS, LLC.
15099  *
15100  * Originally Released Under LGPL - original licence link has changed is not relivant.
15101  *
15102  * Fork - LGPL
15103  * <script type="text/javascript">
15104  */
15105 /**
15106  * @class Roo.state.CookieProvider
15107  * @extends Roo.state.Provider
15108  * The default Provider implementation which saves state via cookies.
15109  * <br />Usage:
15110  <pre><code>
15111    var cp = new Roo.state.CookieProvider({
15112        path: "/cgi-bin/",
15113        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15114        domain: "roojs.com"
15115    })
15116    Roo.state.Manager.setProvider(cp);
15117  </code></pre>
15118  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15119  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15120  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15121  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15122  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15123  * domain the page is running on including the 'www' like 'www.roojs.com')
15124  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15125  * @constructor
15126  * Create a new CookieProvider
15127  * @param {Object} config The configuration object
15128  */
15129 Roo.state.CookieProvider = function(config){
15130     Roo.state.CookieProvider.superclass.constructor.call(this);
15131     this.path = "/";
15132     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15133     this.domain = null;
15134     this.secure = false;
15135     Roo.apply(this, config);
15136     this.state = this.readCookies();
15137 };
15138
15139 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15140     // private
15141     set : function(name, value){
15142         if(typeof value == "undefined" || value === null){
15143             this.clear(name);
15144             return;
15145         }
15146         this.setCookie(name, value);
15147         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15148     },
15149
15150     // private
15151     clear : function(name){
15152         this.clearCookie(name);
15153         Roo.state.CookieProvider.superclass.clear.call(this, name);
15154     },
15155
15156     // private
15157     readCookies : function(){
15158         var cookies = {};
15159         var c = document.cookie + ";";
15160         var re = /\s?(.*?)=(.*?);/g;
15161         var matches;
15162         while((matches = re.exec(c)) != null){
15163             var name = matches[1];
15164             var value = matches[2];
15165             if(name && name.substring(0,3) == "ys-"){
15166                 cookies[name.substr(3)] = this.decodeValue(value);
15167             }
15168         }
15169         return cookies;
15170     },
15171
15172     // private
15173     setCookie : function(name, value){
15174         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15175            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15176            ((this.path == null) ? "" : ("; path=" + this.path)) +
15177            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15178            ((this.secure == true) ? "; secure" : "");
15179     },
15180
15181     // private
15182     clearCookie : function(name){
15183         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15184            ((this.path == null) ? "" : ("; path=" + this.path)) +
15185            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15186            ((this.secure == true) ? "; secure" : "");
15187     }
15188 });/*
15189  * Based on:
15190  * Ext JS Library 1.1.1
15191  * Copyright(c) 2006-2007, Ext JS, LLC.
15192  *
15193  * Originally Released Under LGPL - original licence link has changed is not relivant.
15194  *
15195  * Fork - LGPL
15196  * <script type="text/javascript">
15197  */
15198  
15199
15200 /**
15201  * @class Roo.ComponentMgr
15202  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15203  * @singleton
15204  */
15205 Roo.ComponentMgr = function(){
15206     var all = new Roo.util.MixedCollection();
15207
15208     return {
15209         /**
15210          * Registers a component.
15211          * @param {Roo.Component} c The component
15212          */
15213         register : function(c){
15214             all.add(c);
15215         },
15216
15217         /**
15218          * Unregisters a component.
15219          * @param {Roo.Component} c The component
15220          */
15221         unregister : function(c){
15222             all.remove(c);
15223         },
15224
15225         /**
15226          * Returns a component by id
15227          * @param {String} id The component id
15228          */
15229         get : function(id){
15230             return all.get(id);
15231         },
15232
15233         /**
15234          * Registers a function that will be called when a specified component is added to ComponentMgr
15235          * @param {String} id The component id
15236          * @param {Funtction} fn The callback function
15237          * @param {Object} scope The scope of the callback
15238          */
15239         onAvailable : function(id, fn, scope){
15240             all.on("add", function(index, o){
15241                 if(o.id == id){
15242                     fn.call(scope || o, o);
15243                     all.un("add", fn, scope);
15244                 }
15245             });
15246         }
15247     };
15248 }();/*
15249  * Based on:
15250  * Ext JS Library 1.1.1
15251  * Copyright(c) 2006-2007, Ext JS, LLC.
15252  *
15253  * Originally Released Under LGPL - original licence link has changed is not relivant.
15254  *
15255  * Fork - LGPL
15256  * <script type="text/javascript">
15257  */
15258  
15259 /**
15260  * @class Roo.Component
15261  * @extends Roo.util.Observable
15262  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15263  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15264  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15265  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15266  * All visual components (widgets) that require rendering into a layout should subclass Component.
15267  * @constructor
15268  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15269  * 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
15270  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15271  */
15272 Roo.Component = function(config){
15273     config = config || {};
15274     if(config.tagName || config.dom || typeof config == "string"){ // element object
15275         config = {el: config, id: config.id || config};
15276     }
15277     this.initialConfig = config;
15278
15279     Roo.apply(this, config);
15280     this.addEvents({
15281         /**
15282          * @event disable
15283          * Fires after the component is disabled.
15284              * @param {Roo.Component} this
15285              */
15286         disable : true,
15287         /**
15288          * @event enable
15289          * Fires after the component is enabled.
15290              * @param {Roo.Component} this
15291              */
15292         enable : true,
15293         /**
15294          * @event beforeshow
15295          * Fires before the component is shown.  Return false to stop the show.
15296              * @param {Roo.Component} this
15297              */
15298         beforeshow : true,
15299         /**
15300          * @event show
15301          * Fires after the component is shown.
15302              * @param {Roo.Component} this
15303              */
15304         show : true,
15305         /**
15306          * @event beforehide
15307          * Fires before the component is hidden. Return false to stop the hide.
15308              * @param {Roo.Component} this
15309              */
15310         beforehide : true,
15311         /**
15312          * @event hide
15313          * Fires after the component is hidden.
15314              * @param {Roo.Component} this
15315              */
15316         hide : true,
15317         /**
15318          * @event beforerender
15319          * Fires before the component is rendered. Return false to stop the render.
15320              * @param {Roo.Component} this
15321              */
15322         beforerender : true,
15323         /**
15324          * @event render
15325          * Fires after the component is rendered.
15326              * @param {Roo.Component} this
15327              */
15328         render : true,
15329         /**
15330          * @event beforedestroy
15331          * Fires before the component is destroyed. Return false to stop the destroy.
15332              * @param {Roo.Component} this
15333              */
15334         beforedestroy : true,
15335         /**
15336          * @event destroy
15337          * Fires after the component is destroyed.
15338              * @param {Roo.Component} this
15339              */
15340         destroy : true
15341     });
15342     if(!this.id){
15343         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15344     }
15345     Roo.ComponentMgr.register(this);
15346     Roo.Component.superclass.constructor.call(this);
15347     this.initComponent();
15348     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15349         this.render(this.renderTo);
15350         delete this.renderTo;
15351     }
15352 };
15353
15354 /** @private */
15355 Roo.Component.AUTO_ID = 1000;
15356
15357 Roo.extend(Roo.Component, Roo.util.Observable, {
15358     /**
15359      * @scope Roo.Component.prototype
15360      * @type {Boolean}
15361      * true if this component is hidden. Read-only.
15362      */
15363     hidden : false,
15364     /**
15365      * @type {Boolean}
15366      * true if this component is disabled. Read-only.
15367      */
15368     disabled : false,
15369     /**
15370      * @type {Boolean}
15371      * true if this component has been rendered. Read-only.
15372      */
15373     rendered : false,
15374     
15375     /** @cfg {String} disableClass
15376      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15377      */
15378     disabledClass : "x-item-disabled",
15379         /** @cfg {Boolean} allowDomMove
15380          * Whether the component can move the Dom node when rendering (defaults to true).
15381          */
15382     allowDomMove : true,
15383     /** @cfg {String} hideMode (display|visibility)
15384      * How this component should hidden. Supported values are
15385      * "visibility" (css visibility), "offsets" (negative offset position) and
15386      * "display" (css display) - defaults to "display".
15387      */
15388     hideMode: 'display',
15389
15390     /** @private */
15391     ctype : "Roo.Component",
15392
15393     /**
15394      * @cfg {String} actionMode 
15395      * which property holds the element that used for  hide() / show() / disable() / enable()
15396      * default is 'el' 
15397      */
15398     actionMode : "el",
15399
15400     /** @private */
15401     getActionEl : function(){
15402         return this[this.actionMode];
15403     },
15404
15405     initComponent : Roo.emptyFn,
15406     /**
15407      * If this is a lazy rendering component, render it to its container element.
15408      * @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.
15409      */
15410     render : function(container, position){
15411         
15412         if(this.rendered){
15413             return this;
15414         }
15415         
15416         if(this.fireEvent("beforerender", this) === false){
15417             return false;
15418         }
15419         
15420         if(!container && this.el){
15421             this.el = Roo.get(this.el);
15422             container = this.el.dom.parentNode;
15423             this.allowDomMove = false;
15424         }
15425         this.container = Roo.get(container);
15426         this.rendered = true;
15427         if(position !== undefined){
15428             if(typeof position == 'number'){
15429                 position = this.container.dom.childNodes[position];
15430             }else{
15431                 position = Roo.getDom(position);
15432             }
15433         }
15434         this.onRender(this.container, position || null);
15435         if(this.cls){
15436             this.el.addClass(this.cls);
15437             delete this.cls;
15438         }
15439         if(this.style){
15440             this.el.applyStyles(this.style);
15441             delete this.style;
15442         }
15443         this.fireEvent("render", this);
15444         this.afterRender(this.container);
15445         if(this.hidden){
15446             this.hide();
15447         }
15448         if(this.disabled){
15449             this.disable();
15450         }
15451
15452         return this;
15453         
15454     },
15455
15456     /** @private */
15457     // default function is not really useful
15458     onRender : function(ct, position){
15459         if(this.el){
15460             this.el = Roo.get(this.el);
15461             if(this.allowDomMove !== false){
15462                 ct.dom.insertBefore(this.el.dom, position);
15463             }
15464         }
15465     },
15466
15467     /** @private */
15468     getAutoCreate : function(){
15469         var cfg = typeof this.autoCreate == "object" ?
15470                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15471         if(this.id && !cfg.id){
15472             cfg.id = this.id;
15473         }
15474         return cfg;
15475     },
15476
15477     /** @private */
15478     afterRender : Roo.emptyFn,
15479
15480     /**
15481      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15482      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15483      */
15484     destroy : function(){
15485         if(this.fireEvent("beforedestroy", this) !== false){
15486             this.purgeListeners();
15487             this.beforeDestroy();
15488             if(this.rendered){
15489                 this.el.removeAllListeners();
15490                 this.el.remove();
15491                 if(this.actionMode == "container"){
15492                     this.container.remove();
15493                 }
15494             }
15495             this.onDestroy();
15496             Roo.ComponentMgr.unregister(this);
15497             this.fireEvent("destroy", this);
15498         }
15499     },
15500
15501         /** @private */
15502     beforeDestroy : function(){
15503
15504     },
15505
15506         /** @private */
15507         onDestroy : function(){
15508
15509     },
15510
15511     /**
15512      * Returns the underlying {@link Roo.Element}.
15513      * @return {Roo.Element} The element
15514      */
15515     getEl : function(){
15516         return this.el;
15517     },
15518
15519     /**
15520      * Returns the id of this component.
15521      * @return {String}
15522      */
15523     getId : function(){
15524         return this.id;
15525     },
15526
15527     /**
15528      * Try to focus this component.
15529      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15530      * @return {Roo.Component} this
15531      */
15532     focus : function(selectText){
15533         if(this.rendered){
15534             this.el.focus();
15535             if(selectText === true){
15536                 this.el.dom.select();
15537             }
15538         }
15539         return this;
15540     },
15541
15542     /** @private */
15543     blur : function(){
15544         if(this.rendered){
15545             this.el.blur();
15546         }
15547         return this;
15548     },
15549
15550     /**
15551      * Disable this component.
15552      * @return {Roo.Component} this
15553      */
15554     disable : function(){
15555         if(this.rendered){
15556             this.onDisable();
15557         }
15558         this.disabled = true;
15559         this.fireEvent("disable", this);
15560         return this;
15561     },
15562
15563         // private
15564     onDisable : function(){
15565         this.getActionEl().addClass(this.disabledClass);
15566         this.el.dom.disabled = true;
15567     },
15568
15569     /**
15570      * Enable this component.
15571      * @return {Roo.Component} this
15572      */
15573     enable : function(){
15574         if(this.rendered){
15575             this.onEnable();
15576         }
15577         this.disabled = false;
15578         this.fireEvent("enable", this);
15579         return this;
15580     },
15581
15582         // private
15583     onEnable : function(){
15584         this.getActionEl().removeClass(this.disabledClass);
15585         this.el.dom.disabled = false;
15586     },
15587
15588     /**
15589      * Convenience function for setting disabled/enabled by boolean.
15590      * @param {Boolean} disabled
15591      */
15592     setDisabled : function(disabled){
15593         this[disabled ? "disable" : "enable"]();
15594     },
15595
15596     /**
15597      * Show this component.
15598      * @return {Roo.Component} this
15599      */
15600     show: function(){
15601         if(this.fireEvent("beforeshow", this) !== false){
15602             this.hidden = false;
15603             if(this.rendered){
15604                 this.onShow();
15605             }
15606             this.fireEvent("show", this);
15607         }
15608         return this;
15609     },
15610
15611     // private
15612     onShow : function(){
15613         var ae = this.getActionEl();
15614         if(this.hideMode == 'visibility'){
15615             ae.dom.style.visibility = "visible";
15616         }else if(this.hideMode == 'offsets'){
15617             ae.removeClass('x-hidden');
15618         }else{
15619             ae.dom.style.display = "";
15620         }
15621     },
15622
15623     /**
15624      * Hide this component.
15625      * @return {Roo.Component} this
15626      */
15627     hide: function(){
15628         if(this.fireEvent("beforehide", this) !== false){
15629             this.hidden = true;
15630             if(this.rendered){
15631                 this.onHide();
15632             }
15633             this.fireEvent("hide", this);
15634         }
15635         return this;
15636     },
15637
15638     // private
15639     onHide : function(){
15640         var ae = this.getActionEl();
15641         if(this.hideMode == 'visibility'){
15642             ae.dom.style.visibility = "hidden";
15643         }else if(this.hideMode == 'offsets'){
15644             ae.addClass('x-hidden');
15645         }else{
15646             ae.dom.style.display = "none";
15647         }
15648     },
15649
15650     /**
15651      * Convenience function to hide or show this component by boolean.
15652      * @param {Boolean} visible True to show, false to hide
15653      * @return {Roo.Component} this
15654      */
15655     setVisible: function(visible){
15656         if(visible) {
15657             this.show();
15658         }else{
15659             this.hide();
15660         }
15661         return this;
15662     },
15663
15664     /**
15665      * Returns true if this component is visible.
15666      */
15667     isVisible : function(){
15668         return this.getActionEl().isVisible();
15669     },
15670
15671     cloneConfig : function(overrides){
15672         overrides = overrides || {};
15673         var id = overrides.id || Roo.id();
15674         var cfg = Roo.applyIf(overrides, this.initialConfig);
15675         cfg.id = id; // prevent dup id
15676         return new this.constructor(cfg);
15677     }
15678 });/*
15679  * Based on:
15680  * Ext JS Library 1.1.1
15681  * Copyright(c) 2006-2007, Ext JS, LLC.
15682  *
15683  * Originally Released Under LGPL - original licence link has changed is not relivant.
15684  *
15685  * Fork - LGPL
15686  * <script type="text/javascript">
15687  */
15688
15689 /**
15690  * @class Roo.BoxComponent
15691  * @extends Roo.Component
15692  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15693  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15694  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15695  * layout containers.
15696  * @constructor
15697  * @param {Roo.Element/String/Object} config The configuration options.
15698  */
15699 Roo.BoxComponent = function(config){
15700     Roo.Component.call(this, config);
15701     this.addEvents({
15702         /**
15703          * @event resize
15704          * Fires after the component is resized.
15705              * @param {Roo.Component} this
15706              * @param {Number} adjWidth The box-adjusted width that was set
15707              * @param {Number} adjHeight The box-adjusted height that was set
15708              * @param {Number} rawWidth The width that was originally specified
15709              * @param {Number} rawHeight The height that was originally specified
15710              */
15711         resize : true,
15712         /**
15713          * @event move
15714          * Fires after the component is moved.
15715              * @param {Roo.Component} this
15716              * @param {Number} x The new x position
15717              * @param {Number} y The new y position
15718              */
15719         move : true
15720     });
15721 };
15722
15723 Roo.extend(Roo.BoxComponent, Roo.Component, {
15724     // private, set in afterRender to signify that the component has been rendered
15725     boxReady : false,
15726     // private, used to defer height settings to subclasses
15727     deferHeight: false,
15728     /** @cfg {Number} width
15729      * width (optional) size of component
15730      */
15731      /** @cfg {Number} height
15732      * height (optional) size of component
15733      */
15734      
15735     /**
15736      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15737      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15738      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15739      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15740      * @return {Roo.BoxComponent} this
15741      */
15742     setSize : function(w, h){
15743         // support for standard size objects
15744         if(typeof w == 'object'){
15745             h = w.height;
15746             w = w.width;
15747         }
15748         // not rendered
15749         if(!this.boxReady){
15750             this.width = w;
15751             this.height = h;
15752             return this;
15753         }
15754
15755         // prevent recalcs when not needed
15756         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15757             return this;
15758         }
15759         this.lastSize = {width: w, height: h};
15760
15761         var adj = this.adjustSize(w, h);
15762         var aw = adj.width, ah = adj.height;
15763         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15764             var rz = this.getResizeEl();
15765             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15766                 rz.setSize(aw, ah);
15767             }else if(!this.deferHeight && ah !== undefined){
15768                 rz.setHeight(ah);
15769             }else if(aw !== undefined){
15770                 rz.setWidth(aw);
15771             }
15772             this.onResize(aw, ah, w, h);
15773             this.fireEvent('resize', this, aw, ah, w, h);
15774         }
15775         return this;
15776     },
15777
15778     /**
15779      * Gets the current size of the component's underlying element.
15780      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15781      */
15782     getSize : function(){
15783         return this.el.getSize();
15784     },
15785
15786     /**
15787      * Gets the current XY position of the component's underlying element.
15788      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15789      * @return {Array} The XY position of the element (e.g., [100, 200])
15790      */
15791     getPosition : function(local){
15792         if(local === true){
15793             return [this.el.getLeft(true), this.el.getTop(true)];
15794         }
15795         return this.xy || this.el.getXY();
15796     },
15797
15798     /**
15799      * Gets the current box measurements of the component's underlying element.
15800      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15801      * @returns {Object} box An object in the format {x, y, width, height}
15802      */
15803     getBox : function(local){
15804         var s = this.el.getSize();
15805         if(local){
15806             s.x = this.el.getLeft(true);
15807             s.y = this.el.getTop(true);
15808         }else{
15809             var xy = this.xy || this.el.getXY();
15810             s.x = xy[0];
15811             s.y = xy[1];
15812         }
15813         return s;
15814     },
15815
15816     /**
15817      * Sets the current box measurements of the component's underlying element.
15818      * @param {Object} box An object in the format {x, y, width, height}
15819      * @returns {Roo.BoxComponent} this
15820      */
15821     updateBox : function(box){
15822         this.setSize(box.width, box.height);
15823         this.setPagePosition(box.x, box.y);
15824         return this;
15825     },
15826
15827     // protected
15828     getResizeEl : function(){
15829         return this.resizeEl || this.el;
15830     },
15831
15832     // protected
15833     getPositionEl : function(){
15834         return this.positionEl || this.el;
15835     },
15836
15837     /**
15838      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15839      * This method fires the move event.
15840      * @param {Number} left The new left
15841      * @param {Number} top The new top
15842      * @returns {Roo.BoxComponent} this
15843      */
15844     setPosition : function(x, y){
15845         this.x = x;
15846         this.y = y;
15847         if(!this.boxReady){
15848             return this;
15849         }
15850         var adj = this.adjustPosition(x, y);
15851         var ax = adj.x, ay = adj.y;
15852
15853         var el = this.getPositionEl();
15854         if(ax !== undefined || ay !== undefined){
15855             if(ax !== undefined && ay !== undefined){
15856                 el.setLeftTop(ax, ay);
15857             }else if(ax !== undefined){
15858                 el.setLeft(ax);
15859             }else if(ay !== undefined){
15860                 el.setTop(ay);
15861             }
15862             this.onPosition(ax, ay);
15863             this.fireEvent('move', this, ax, ay);
15864         }
15865         return this;
15866     },
15867
15868     /**
15869      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15870      * This method fires the move event.
15871      * @param {Number} x The new x position
15872      * @param {Number} y The new y position
15873      * @returns {Roo.BoxComponent} this
15874      */
15875     setPagePosition : function(x, y){
15876         this.pageX = x;
15877         this.pageY = y;
15878         if(!this.boxReady){
15879             return;
15880         }
15881         if(x === undefined || y === undefined){ // cannot translate undefined points
15882             return;
15883         }
15884         var p = this.el.translatePoints(x, y);
15885         this.setPosition(p.left, p.top);
15886         return this;
15887     },
15888
15889     // private
15890     onRender : function(ct, position){
15891         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15892         if(this.resizeEl){
15893             this.resizeEl = Roo.get(this.resizeEl);
15894         }
15895         if(this.positionEl){
15896             this.positionEl = Roo.get(this.positionEl);
15897         }
15898     },
15899
15900     // private
15901     afterRender : function(){
15902         Roo.BoxComponent.superclass.afterRender.call(this);
15903         this.boxReady = true;
15904         this.setSize(this.width, this.height);
15905         if(this.x || this.y){
15906             this.setPosition(this.x, this.y);
15907         }
15908         if(this.pageX || this.pageY){
15909             this.setPagePosition(this.pageX, this.pageY);
15910         }
15911     },
15912
15913     /**
15914      * Force the component's size to recalculate based on the underlying element's current height and width.
15915      * @returns {Roo.BoxComponent} this
15916      */
15917     syncSize : function(){
15918         delete this.lastSize;
15919         this.setSize(this.el.getWidth(), this.el.getHeight());
15920         return this;
15921     },
15922
15923     /**
15924      * Called after the component is resized, this method is empty by default but can be implemented by any
15925      * subclass that needs to perform custom logic after a resize occurs.
15926      * @param {Number} adjWidth The box-adjusted width that was set
15927      * @param {Number} adjHeight The box-adjusted height that was set
15928      * @param {Number} rawWidth The width that was originally specified
15929      * @param {Number} rawHeight The height that was originally specified
15930      */
15931     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15932
15933     },
15934
15935     /**
15936      * Called after the component is moved, this method is empty by default but can be implemented by any
15937      * subclass that needs to perform custom logic after a move occurs.
15938      * @param {Number} x The new x position
15939      * @param {Number} y The new y position
15940      */
15941     onPosition : function(x, y){
15942
15943     },
15944
15945     // private
15946     adjustSize : function(w, h){
15947         if(this.autoWidth){
15948             w = 'auto';
15949         }
15950         if(this.autoHeight){
15951             h = 'auto';
15952         }
15953         return {width : w, height: h};
15954     },
15955
15956     // private
15957     adjustPosition : function(x, y){
15958         return {x : x, y: y};
15959     }
15960 });/*
15961  * Original code for Roojs - LGPL
15962  * <script type="text/javascript">
15963  */
15964  
15965 /**
15966  * @class Roo.XComponent
15967  * A delayed Element creator...
15968  * Or a way to group chunks of interface together.
15969  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15970  *  used in conjunction with XComponent.build() it will create an instance of each element,
15971  *  then call addxtype() to build the User interface.
15972  * 
15973  * Mypart.xyx = new Roo.XComponent({
15974
15975     parent : 'Mypart.xyz', // empty == document.element.!!
15976     order : '001',
15977     name : 'xxxx'
15978     region : 'xxxx'
15979     disabled : function() {} 
15980      
15981     tree : function() { // return an tree of xtype declared components
15982         var MODULE = this;
15983         return 
15984         {
15985             xtype : 'NestedLayoutPanel',
15986             // technicall
15987         }
15988      ]
15989  *})
15990  *
15991  *
15992  * It can be used to build a big heiracy, with parent etc.
15993  * or you can just use this to render a single compoent to a dom element
15994  * MYPART.render(Roo.Element | String(id) | dom_element )
15995  *
15996  *
15997  * Usage patterns.
15998  *
15999  * Classic Roo
16000  *
16001  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16002  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16003  *
16004  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16005  *
16006  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16007  * - if mulitple topModules exist, the last one is defined as the top module.
16008  *
16009  * Embeded Roo
16010  * 
16011  * When the top level or multiple modules are to embedded into a existing HTML page,
16012  * the parent element can container '#id' of the element where the module will be drawn.
16013  *
16014  * Bootstrap Roo
16015  *
16016  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16017  * it relies more on a include mechanism, where sub modules are included into an outer page.
16018  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16019  * 
16020  * Bootstrap Roo Included elements
16021  *
16022  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16023  * hence confusing the component builder as it thinks there are multiple top level elements. 
16024  *
16025  * String Over-ride & Translations
16026  *
16027  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16028  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16029  * are needed. @see Roo.XComponent.overlayString  
16030  * 
16031  * 
16032  * 
16033  * @extends Roo.util.Observable
16034  * @constructor
16035  * @param cfg {Object} configuration of component
16036  * 
16037  */
16038 Roo.XComponent = function(cfg) {
16039     Roo.apply(this, cfg);
16040     this.addEvents({ 
16041         /**
16042              * @event built
16043              * Fires when this the componnt is built
16044              * @param {Roo.XComponent} c the component
16045              */
16046         'built' : true
16047         
16048     });
16049     this.region = this.region || 'center'; // default..
16050     Roo.XComponent.register(this);
16051     this.modules = false;
16052     this.el = false; // where the layout goes..
16053     
16054     
16055 }
16056 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16057     /**
16058      * @property el
16059      * The created element (with Roo.factory())
16060      * @type {Roo.Layout}
16061      */
16062     el  : false,
16063     
16064     /**
16065      * @property el
16066      * for BC  - use el in new code
16067      * @type {Roo.Layout}
16068      */
16069     panel : false,
16070     
16071     /**
16072      * @property layout
16073      * for BC  - use el in new code
16074      * @type {Roo.Layout}
16075      */
16076     layout : false,
16077     
16078      /**
16079      * @cfg {Function|boolean} disabled
16080      * If this module is disabled by some rule, return true from the funtion
16081      */
16082     disabled : false,
16083     
16084     /**
16085      * @cfg {String} parent 
16086      * Name of parent element which it get xtype added to..
16087      */
16088     parent: false,
16089     
16090     /**
16091      * @cfg {String} order
16092      * Used to set the order in which elements are created (usefull for multiple tabs)
16093      */
16094     
16095     order : false,
16096     /**
16097      * @cfg {String} name
16098      * String to display while loading.
16099      */
16100     name : false,
16101     /**
16102      * @cfg {String} region
16103      * Region to render component to (defaults to center)
16104      */
16105     region : 'center',
16106     
16107     /**
16108      * @cfg {Array} items
16109      * A single item array - the first element is the root of the tree..
16110      * It's done this way to stay compatible with the Xtype system...
16111      */
16112     items : false,
16113     
16114     /**
16115      * @property _tree
16116      * The method that retuns the tree of parts that make up this compoennt 
16117      * @type {function}
16118      */
16119     _tree  : false,
16120     
16121      /**
16122      * render
16123      * render element to dom or tree
16124      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16125      */
16126     
16127     render : function(el)
16128     {
16129         
16130         el = el || false;
16131         var hp = this.parent ? 1 : 0;
16132         Roo.debug &&  Roo.log(this);
16133         
16134         var tree = this._tree ? this._tree() : this.tree();
16135
16136         
16137         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16138             // if parent is a '#.....' string, then let's use that..
16139             var ename = this.parent.substr(1);
16140             this.parent = false;
16141             Roo.debug && Roo.log(ename);
16142             switch (ename) {
16143                 case 'bootstrap-body':
16144                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16145                         // this is the BorderLayout standard?
16146                        this.parent = { el : true };
16147                        break;
16148                     }
16149                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16150                         // need to insert stuff...
16151                         this.parent =  {
16152                              el : new Roo.bootstrap.layout.Border({
16153                                  el : document.body, 
16154                      
16155                                  center: {
16156                                     titlebar: false,
16157                                     autoScroll:false,
16158                                     closeOnTab: true,
16159                                     tabPosition: 'top',
16160                                       //resizeTabs: true,
16161                                     alwaysShowTabs: true,
16162                                     hideTabs: false
16163                                      //minTabWidth: 140
16164                                  }
16165                              })
16166                         
16167                          };
16168                          break;
16169                     }
16170                          
16171                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16172                         this.parent = { el :  new  Roo.bootstrap.Body() };
16173                         Roo.debug && Roo.log("setting el to doc body");
16174                          
16175                     } else {
16176                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16177                     }
16178                     break;
16179                 case 'bootstrap':
16180                     this.parent = { el : true};
16181                     // fall through
16182                 default:
16183                     el = Roo.get(ename);
16184                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16185                         this.parent = { el : true};
16186                     }
16187                     
16188                     break;
16189             }
16190                 
16191             
16192             if (!el && !this.parent) {
16193                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16194                 return;
16195             }
16196         }
16197         
16198         Roo.debug && Roo.log("EL:");
16199         Roo.debug && Roo.log(el);
16200         Roo.debug && Roo.log("this.parent.el:");
16201         Roo.debug && Roo.log(this.parent.el);
16202         
16203
16204         // altertive root elements ??? - we need a better way to indicate these.
16205         var is_alt = Roo.XComponent.is_alt ||
16206                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16207                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16208                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16209         
16210         
16211         
16212         if (!this.parent && is_alt) {
16213             //el = Roo.get(document.body);
16214             this.parent = { el : true };
16215         }
16216             
16217             
16218         
16219         if (!this.parent) {
16220             
16221             Roo.debug && Roo.log("no parent - creating one");
16222             
16223             el = el ? Roo.get(el) : false;      
16224             
16225             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16226                 
16227                 this.parent =  {
16228                     el : new Roo.bootstrap.layout.Border({
16229                         el: el || document.body,
16230                     
16231                         center: {
16232                             titlebar: false,
16233                             autoScroll:false,
16234                             closeOnTab: true,
16235                             tabPosition: 'top',
16236                              //resizeTabs: true,
16237                             alwaysShowTabs: false,
16238                             hideTabs: true,
16239                             minTabWidth: 140,
16240                             overflow: 'visible'
16241                          }
16242                      })
16243                 };
16244             } else {
16245             
16246                 // it's a top level one..
16247                 this.parent =  {
16248                     el : new Roo.BorderLayout(el || document.body, {
16249                         center: {
16250                             titlebar: false,
16251                             autoScroll:false,
16252                             closeOnTab: true,
16253                             tabPosition: 'top',
16254                              //resizeTabs: true,
16255                             alwaysShowTabs: el && hp? false :  true,
16256                             hideTabs: el || !hp ? true :  false,
16257                             minTabWidth: 140
16258                          }
16259                     })
16260                 };
16261             }
16262         }
16263         
16264         if (!this.parent.el) {
16265                 // probably an old style ctor, which has been disabled.
16266                 return;
16267
16268         }
16269                 // The 'tree' method is  '_tree now' 
16270             
16271         tree.region = tree.region || this.region;
16272         var is_body = false;
16273         if (this.parent.el === true) {
16274             // bootstrap... - body..
16275             if (el) {
16276                 tree.el = el;
16277             }
16278             this.parent.el = Roo.factory(tree);
16279             is_body = true;
16280         }
16281         
16282         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16283         this.fireEvent('built', this);
16284         
16285         this.panel = this.el;
16286         this.layout = this.panel.layout;
16287         this.parentLayout = this.parent.layout  || false;  
16288          
16289     }
16290     
16291 });
16292
16293 Roo.apply(Roo.XComponent, {
16294     /**
16295      * @property  hideProgress
16296      * true to disable the building progress bar.. usefull on single page renders.
16297      * @type Boolean
16298      */
16299     hideProgress : false,
16300     /**
16301      * @property  buildCompleted
16302      * True when the builder has completed building the interface.
16303      * @type Boolean
16304      */
16305     buildCompleted : false,
16306      
16307     /**
16308      * @property  topModule
16309      * the upper most module - uses document.element as it's constructor.
16310      * @type Object
16311      */
16312      
16313     topModule  : false,
16314       
16315     /**
16316      * @property  modules
16317      * array of modules to be created by registration system.
16318      * @type {Array} of Roo.XComponent
16319      */
16320     
16321     modules : [],
16322     /**
16323      * @property  elmodules
16324      * array of modules to be created by which use #ID 
16325      * @type {Array} of Roo.XComponent
16326      */
16327      
16328     elmodules : [],
16329
16330      /**
16331      * @property  is_alt
16332      * Is an alternative Root - normally used by bootstrap or other systems,
16333      *    where the top element in the tree can wrap 'body' 
16334      * @type {boolean}  (default false)
16335      */
16336      
16337     is_alt : false,
16338     /**
16339      * @property  build_from_html
16340      * Build elements from html - used by bootstrap HTML stuff 
16341      *    - this is cleared after build is completed
16342      * @type {boolean}    (default false)
16343      */
16344      
16345     build_from_html : false,
16346     /**
16347      * Register components to be built later.
16348      *
16349      * This solves the following issues
16350      * - Building is not done on page load, but after an authentication process has occured.
16351      * - Interface elements are registered on page load
16352      * - Parent Interface elements may not be loaded before child, so this handles that..
16353      * 
16354      *
16355      * example:
16356      * 
16357      * MyApp.register({
16358           order : '000001',
16359           module : 'Pman.Tab.projectMgr',
16360           region : 'center',
16361           parent : 'Pman.layout',
16362           disabled : false,  // or use a function..
16363         })
16364      
16365      * * @param {Object} details about module
16366      */
16367     register : function(obj) {
16368                 
16369         Roo.XComponent.event.fireEvent('register', obj);
16370         switch(typeof(obj.disabled) ) {
16371                 
16372             case 'undefined':
16373                 break;
16374             
16375             case 'function':
16376                 if ( obj.disabled() ) {
16377                         return;
16378                 }
16379                 break;
16380             
16381             default:
16382                 if (obj.disabled) {
16383                         return;
16384                 }
16385                 break;
16386         }
16387                 
16388         this.modules.push(obj);
16389          
16390     },
16391     /**
16392      * convert a string to an object..
16393      * eg. 'AAA.BBB' -> finds AAA.BBB
16394
16395      */
16396     
16397     toObject : function(str)
16398     {
16399         if (!str || typeof(str) == 'object') {
16400             return str;
16401         }
16402         if (str.substring(0,1) == '#') {
16403             return str;
16404         }
16405
16406         var ar = str.split('.');
16407         var rt, o;
16408         rt = ar.shift();
16409             /** eval:var:o */
16410         try {
16411             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16412         } catch (e) {
16413             throw "Module not found : " + str;
16414         }
16415         
16416         if (o === false) {
16417             throw "Module not found : " + str;
16418         }
16419         Roo.each(ar, function(e) {
16420             if (typeof(o[e]) == 'undefined') {
16421                 throw "Module not found : " + str;
16422             }
16423             o = o[e];
16424         });
16425         
16426         return o;
16427         
16428     },
16429     
16430     
16431     /**
16432      * move modules into their correct place in the tree..
16433      * 
16434      */
16435     preBuild : function ()
16436     {
16437         var _t = this;
16438         Roo.each(this.modules , function (obj)
16439         {
16440             Roo.XComponent.event.fireEvent('beforebuild', obj);
16441             
16442             var opar = obj.parent;
16443             try { 
16444                 obj.parent = this.toObject(opar);
16445             } catch(e) {
16446                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16447                 return;
16448             }
16449             
16450             if (!obj.parent) {
16451                 Roo.debug && Roo.log("GOT top level module");
16452                 Roo.debug && Roo.log(obj);
16453                 obj.modules = new Roo.util.MixedCollection(false, 
16454                     function(o) { return o.order + '' }
16455                 );
16456                 this.topModule = obj;
16457                 return;
16458             }
16459                         // parent is a string (usually a dom element name..)
16460             if (typeof(obj.parent) == 'string') {
16461                 this.elmodules.push(obj);
16462                 return;
16463             }
16464             if (obj.parent.constructor != Roo.XComponent) {
16465                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16466             }
16467             if (!obj.parent.modules) {
16468                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16469                     function(o) { return o.order + '' }
16470                 );
16471             }
16472             if (obj.parent.disabled) {
16473                 obj.disabled = true;
16474             }
16475             obj.parent.modules.add(obj);
16476         }, this);
16477     },
16478     
16479      /**
16480      * make a list of modules to build.
16481      * @return {Array} list of modules. 
16482      */ 
16483     
16484     buildOrder : function()
16485     {
16486         var _this = this;
16487         var cmp = function(a,b) {   
16488             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16489         };
16490         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16491             throw "No top level modules to build";
16492         }
16493         
16494         // make a flat list in order of modules to build.
16495         var mods = this.topModule ? [ this.topModule ] : [];
16496                 
16497         
16498         // elmodules (is a list of DOM based modules )
16499         Roo.each(this.elmodules, function(e) {
16500             mods.push(e);
16501             if (!this.topModule &&
16502                 typeof(e.parent) == 'string' &&
16503                 e.parent.substring(0,1) == '#' &&
16504                 Roo.get(e.parent.substr(1))
16505                ) {
16506                 
16507                 _this.topModule = e;
16508             }
16509             
16510         });
16511
16512         
16513         // add modules to their parents..
16514         var addMod = function(m) {
16515             Roo.debug && Roo.log("build Order: add: " + m.name);
16516                 
16517             mods.push(m);
16518             if (m.modules && !m.disabled) {
16519                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16520                 m.modules.keySort('ASC',  cmp );
16521                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16522     
16523                 m.modules.each(addMod);
16524             } else {
16525                 Roo.debug && Roo.log("build Order: no child modules");
16526             }
16527             // not sure if this is used any more..
16528             if (m.finalize) {
16529                 m.finalize.name = m.name + " (clean up) ";
16530                 mods.push(m.finalize);
16531             }
16532             
16533         }
16534         if (this.topModule && this.topModule.modules) { 
16535             this.topModule.modules.keySort('ASC',  cmp );
16536             this.topModule.modules.each(addMod);
16537         } 
16538         return mods;
16539     },
16540     
16541      /**
16542      * Build the registered modules.
16543      * @param {Object} parent element.
16544      * @param {Function} optional method to call after module has been added.
16545      * 
16546      */ 
16547    
16548     build : function(opts) 
16549     {
16550         
16551         if (typeof(opts) != 'undefined') {
16552             Roo.apply(this,opts);
16553         }
16554         
16555         this.preBuild();
16556         var mods = this.buildOrder();
16557       
16558         //this.allmods = mods;
16559         //Roo.debug && Roo.log(mods);
16560         //return;
16561         if (!mods.length) { // should not happen
16562             throw "NO modules!!!";
16563         }
16564         
16565         
16566         var msg = "Building Interface...";
16567         // flash it up as modal - so we store the mask!?
16568         if (!this.hideProgress && Roo.MessageBox) {
16569             Roo.MessageBox.show({ title: 'loading' });
16570             Roo.MessageBox.show({
16571                title: "Please wait...",
16572                msg: msg,
16573                width:450,
16574                progress:true,
16575                closable:false,
16576                modal: false
16577               
16578             });
16579         }
16580         var total = mods.length;
16581         
16582         var _this = this;
16583         var progressRun = function() {
16584             if (!mods.length) {
16585                 Roo.debug && Roo.log('hide?');
16586                 if (!this.hideProgress && Roo.MessageBox) {
16587                     Roo.MessageBox.hide();
16588                 }
16589                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16590                 
16591                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16592                 
16593                 // THE END...
16594                 return false;   
16595             }
16596             
16597             var m = mods.shift();
16598             
16599             
16600             Roo.debug && Roo.log(m);
16601             // not sure if this is supported any more.. - modules that are are just function
16602             if (typeof(m) == 'function') { 
16603                 m.call(this);
16604                 return progressRun.defer(10, _this);
16605             } 
16606             
16607             
16608             msg = "Building Interface " + (total  - mods.length) + 
16609                     " of " + total + 
16610                     (m.name ? (' - ' + m.name) : '');
16611                         Roo.debug && Roo.log(msg);
16612             if (!_this.hideProgress &&  Roo.MessageBox) { 
16613                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16614             }
16615             
16616          
16617             // is the module disabled?
16618             var disabled = (typeof(m.disabled) == 'function') ?
16619                 m.disabled.call(m.module.disabled) : m.disabled;    
16620             
16621             
16622             if (disabled) {
16623                 return progressRun(); // we do not update the display!
16624             }
16625             
16626             // now build 
16627             
16628                         
16629                         
16630             m.render();
16631             // it's 10 on top level, and 1 on others??? why...
16632             return progressRun.defer(10, _this);
16633              
16634         }
16635         progressRun.defer(1, _this);
16636      
16637         
16638         
16639     },
16640     /**
16641      * Overlay a set of modified strings onto a component
16642      * This is dependant on our builder exporting the strings and 'named strings' elements.
16643      * 
16644      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16645      * @param {Object} associative array of 'named' string and it's new value.
16646      * 
16647      */
16648         overlayStrings : function( component, strings )
16649     {
16650         if (typeof(component['_named_strings']) == 'undefined') {
16651             throw "ERROR: component does not have _named_strings";
16652         }
16653         for ( var k in strings ) {
16654             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16655             if (md !== false) {
16656                 component['_strings'][md] = strings[k];
16657             } else {
16658                 Roo.log('could not find named string: ' + k + ' in');
16659                 Roo.log(component);
16660             }
16661             
16662         }
16663         
16664     },
16665     
16666         
16667         /**
16668          * Event Object.
16669          *
16670          *
16671          */
16672         event: false, 
16673     /**
16674          * wrapper for event.on - aliased later..  
16675          * Typically use to register a event handler for register:
16676          *
16677          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16678          *
16679          */
16680     on : false
16681    
16682     
16683     
16684 });
16685
16686 Roo.XComponent.event = new Roo.util.Observable({
16687                 events : { 
16688                         /**
16689                          * @event register
16690                          * Fires when an Component is registered,
16691                          * set the disable property on the Component to stop registration.
16692                          * @param {Roo.XComponent} c the component being registerd.
16693                          * 
16694                          */
16695                         'register' : true,
16696             /**
16697                          * @event beforebuild
16698                          * Fires before each Component is built
16699                          * can be used to apply permissions.
16700                          * @param {Roo.XComponent} c the component being registerd.
16701                          * 
16702                          */
16703                         'beforebuild' : true,
16704                         /**
16705                          * @event buildcomplete
16706                          * Fires on the top level element when all elements have been built
16707                          * @param {Roo.XComponent} the top level component.
16708                          */
16709                         'buildcomplete' : true
16710                         
16711                 }
16712 });
16713
16714 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16715  //
16716  /**
16717  * marked - a markdown parser
16718  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16719  * https://github.com/chjj/marked
16720  */
16721
16722
16723 /**
16724  *
16725  * Roo.Markdown - is a very crude wrapper around marked..
16726  *
16727  * usage:
16728  * 
16729  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16730  * 
16731  * Note: move the sample code to the bottom of this
16732  * file before uncommenting it.
16733  *
16734  */
16735
16736 Roo.Markdown = {};
16737 Roo.Markdown.toHtml = function(text) {
16738     
16739     var c = new Roo.Markdown.marked.setOptions({
16740             renderer: new Roo.Markdown.marked.Renderer(),
16741             gfm: true,
16742             tables: true,
16743             breaks: false,
16744             pedantic: false,
16745             sanitize: false,
16746             smartLists: true,
16747             smartypants: false
16748           });
16749     // A FEW HACKS!!?
16750     
16751     text = text.replace(/\\\n/g,' ');
16752     return Roo.Markdown.marked(text);
16753 };
16754 //
16755 // converter
16756 //
16757 // Wraps all "globals" so that the only thing
16758 // exposed is makeHtml().
16759 //
16760 (function() {
16761     
16762     /**
16763      * Block-Level Grammar
16764      */
16765     
16766     var block = {
16767       newline: /^\n+/,
16768       code: /^( {4}[^\n]+\n*)+/,
16769       fences: noop,
16770       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16771       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16772       nptable: noop,
16773       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16774       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16775       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16776       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16777       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16778       table: noop,
16779       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16780       text: /^[^\n]+/
16781     };
16782     
16783     block.bullet = /(?:[*+-]|\d+\.)/;
16784     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16785     block.item = replace(block.item, 'gm')
16786       (/bull/g, block.bullet)
16787       ();
16788     
16789     block.list = replace(block.list)
16790       (/bull/g, block.bullet)
16791       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16792       ('def', '\\n+(?=' + block.def.source + ')')
16793       ();
16794     
16795     block.blockquote = replace(block.blockquote)
16796       ('def', block.def)
16797       ();
16798     
16799     block._tag = '(?!(?:'
16800       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16801       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16802       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16803     
16804     block.html = replace(block.html)
16805       ('comment', /<!--[\s\S]*?-->/)
16806       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16807       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16808       (/tag/g, block._tag)
16809       ();
16810     
16811     block.paragraph = replace(block.paragraph)
16812       ('hr', block.hr)
16813       ('heading', block.heading)
16814       ('lheading', block.lheading)
16815       ('blockquote', block.blockquote)
16816       ('tag', '<' + block._tag)
16817       ('def', block.def)
16818       ();
16819     
16820     /**
16821      * Normal Block Grammar
16822      */
16823     
16824     block.normal = merge({}, block);
16825     
16826     /**
16827      * GFM Block Grammar
16828      */
16829     
16830     block.gfm = merge({}, block.normal, {
16831       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16832       paragraph: /^/,
16833       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16834     });
16835     
16836     block.gfm.paragraph = replace(block.paragraph)
16837       ('(?!', '(?!'
16838         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16839         + block.list.source.replace('\\1', '\\3') + '|')
16840       ();
16841     
16842     /**
16843      * GFM + Tables Block Grammar
16844      */
16845     
16846     block.tables = merge({}, block.gfm, {
16847       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16848       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16849     });
16850     
16851     /**
16852      * Block Lexer
16853      */
16854     
16855     function Lexer(options) {
16856       this.tokens = [];
16857       this.tokens.links = {};
16858       this.options = options || marked.defaults;
16859       this.rules = block.normal;
16860     
16861       if (this.options.gfm) {
16862         if (this.options.tables) {
16863           this.rules = block.tables;
16864         } else {
16865           this.rules = block.gfm;
16866         }
16867       }
16868     }
16869     
16870     /**
16871      * Expose Block Rules
16872      */
16873     
16874     Lexer.rules = block;
16875     
16876     /**
16877      * Static Lex Method
16878      */
16879     
16880     Lexer.lex = function(src, options) {
16881       var lexer = new Lexer(options);
16882       return lexer.lex(src);
16883     };
16884     
16885     /**
16886      * Preprocessing
16887      */
16888     
16889     Lexer.prototype.lex = function(src) {
16890       src = src
16891         .replace(/\r\n|\r/g, '\n')
16892         .replace(/\t/g, '    ')
16893         .replace(/\u00a0/g, ' ')
16894         .replace(/\u2424/g, '\n');
16895     
16896       return this.token(src, true);
16897     };
16898     
16899     /**
16900      * Lexing
16901      */
16902     
16903     Lexer.prototype.token = function(src, top, bq) {
16904       var src = src.replace(/^ +$/gm, '')
16905         , next
16906         , loose
16907         , cap
16908         , bull
16909         , b
16910         , item
16911         , space
16912         , i
16913         , l;
16914     
16915       while (src) {
16916         // newline
16917         if (cap = this.rules.newline.exec(src)) {
16918           src = src.substring(cap[0].length);
16919           if (cap[0].length > 1) {
16920             this.tokens.push({
16921               type: 'space'
16922             });
16923           }
16924         }
16925     
16926         // code
16927         if (cap = this.rules.code.exec(src)) {
16928           src = src.substring(cap[0].length);
16929           cap = cap[0].replace(/^ {4}/gm, '');
16930           this.tokens.push({
16931             type: 'code',
16932             text: !this.options.pedantic
16933               ? cap.replace(/\n+$/, '')
16934               : cap
16935           });
16936           continue;
16937         }
16938     
16939         // fences (gfm)
16940         if (cap = this.rules.fences.exec(src)) {
16941           src = src.substring(cap[0].length);
16942           this.tokens.push({
16943             type: 'code',
16944             lang: cap[2],
16945             text: cap[3] || ''
16946           });
16947           continue;
16948         }
16949     
16950         // heading
16951         if (cap = this.rules.heading.exec(src)) {
16952           src = src.substring(cap[0].length);
16953           this.tokens.push({
16954             type: 'heading',
16955             depth: cap[1].length,
16956             text: cap[2]
16957           });
16958           continue;
16959         }
16960     
16961         // table no leading pipe (gfm)
16962         if (top && (cap = this.rules.nptable.exec(src))) {
16963           src = src.substring(cap[0].length);
16964     
16965           item = {
16966             type: 'table',
16967             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16968             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16969             cells: cap[3].replace(/\n$/, '').split('\n')
16970           };
16971     
16972           for (i = 0; i < item.align.length; i++) {
16973             if (/^ *-+: *$/.test(item.align[i])) {
16974               item.align[i] = 'right';
16975             } else if (/^ *:-+: *$/.test(item.align[i])) {
16976               item.align[i] = 'center';
16977             } else if (/^ *:-+ *$/.test(item.align[i])) {
16978               item.align[i] = 'left';
16979             } else {
16980               item.align[i] = null;
16981             }
16982           }
16983     
16984           for (i = 0; i < item.cells.length; i++) {
16985             item.cells[i] = item.cells[i].split(/ *\| */);
16986           }
16987     
16988           this.tokens.push(item);
16989     
16990           continue;
16991         }
16992     
16993         // lheading
16994         if (cap = this.rules.lheading.exec(src)) {
16995           src = src.substring(cap[0].length);
16996           this.tokens.push({
16997             type: 'heading',
16998             depth: cap[2] === '=' ? 1 : 2,
16999             text: cap[1]
17000           });
17001           continue;
17002         }
17003     
17004         // hr
17005         if (cap = this.rules.hr.exec(src)) {
17006           src = src.substring(cap[0].length);
17007           this.tokens.push({
17008             type: 'hr'
17009           });
17010           continue;
17011         }
17012     
17013         // blockquote
17014         if (cap = this.rules.blockquote.exec(src)) {
17015           src = src.substring(cap[0].length);
17016     
17017           this.tokens.push({
17018             type: 'blockquote_start'
17019           });
17020     
17021           cap = cap[0].replace(/^ *> ?/gm, '');
17022     
17023           // Pass `top` to keep the current
17024           // "toplevel" state. This is exactly
17025           // how markdown.pl works.
17026           this.token(cap, top, true);
17027     
17028           this.tokens.push({
17029             type: 'blockquote_end'
17030           });
17031     
17032           continue;
17033         }
17034     
17035         // list
17036         if (cap = this.rules.list.exec(src)) {
17037           src = src.substring(cap[0].length);
17038           bull = cap[2];
17039     
17040           this.tokens.push({
17041             type: 'list_start',
17042             ordered: bull.length > 1
17043           });
17044     
17045           // Get each top-level item.
17046           cap = cap[0].match(this.rules.item);
17047     
17048           next = false;
17049           l = cap.length;
17050           i = 0;
17051     
17052           for (; i < l; i++) {
17053             item = cap[i];
17054     
17055             // Remove the list item's bullet
17056             // so it is seen as the next token.
17057             space = item.length;
17058             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17059     
17060             // Outdent whatever the
17061             // list item contains. Hacky.
17062             if (~item.indexOf('\n ')) {
17063               space -= item.length;
17064               item = !this.options.pedantic
17065                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17066                 : item.replace(/^ {1,4}/gm, '');
17067             }
17068     
17069             // Determine whether the next list item belongs here.
17070             // Backpedal if it does not belong in this list.
17071             if (this.options.smartLists && i !== l - 1) {
17072               b = block.bullet.exec(cap[i + 1])[0];
17073               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17074                 src = cap.slice(i + 1).join('\n') + src;
17075                 i = l - 1;
17076               }
17077             }
17078     
17079             // Determine whether item is loose or not.
17080             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17081             // for discount behavior.
17082             loose = next || /\n\n(?!\s*$)/.test(item);
17083             if (i !== l - 1) {
17084               next = item.charAt(item.length - 1) === '\n';
17085               if (!loose) { loose = next; }
17086             }
17087     
17088             this.tokens.push({
17089               type: loose
17090                 ? 'loose_item_start'
17091                 : 'list_item_start'
17092             });
17093     
17094             // Recurse.
17095             this.token(item, false, bq);
17096     
17097             this.tokens.push({
17098               type: 'list_item_end'
17099             });
17100           }
17101     
17102           this.tokens.push({
17103             type: 'list_end'
17104           });
17105     
17106           continue;
17107         }
17108     
17109         // html
17110         if (cap = this.rules.html.exec(src)) {
17111           src = src.substring(cap[0].length);
17112           this.tokens.push({
17113             type: this.options.sanitize
17114               ? 'paragraph'
17115               : 'html',
17116             pre: !this.options.sanitizer
17117               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17118             text: cap[0]
17119           });
17120           continue;
17121         }
17122     
17123         // def
17124         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17125           src = src.substring(cap[0].length);
17126           this.tokens.links[cap[1].toLowerCase()] = {
17127             href: cap[2],
17128             title: cap[3]
17129           };
17130           continue;
17131         }
17132     
17133         // table (gfm)
17134         if (top && (cap = this.rules.table.exec(src))) {
17135           src = src.substring(cap[0].length);
17136     
17137           item = {
17138             type: 'table',
17139             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17140             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17141             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17142           };
17143     
17144           for (i = 0; i < item.align.length; i++) {
17145             if (/^ *-+: *$/.test(item.align[i])) {
17146               item.align[i] = 'right';
17147             } else if (/^ *:-+: *$/.test(item.align[i])) {
17148               item.align[i] = 'center';
17149             } else if (/^ *:-+ *$/.test(item.align[i])) {
17150               item.align[i] = 'left';
17151             } else {
17152               item.align[i] = null;
17153             }
17154           }
17155     
17156           for (i = 0; i < item.cells.length; i++) {
17157             item.cells[i] = item.cells[i]
17158               .replace(/^ *\| *| *\| *$/g, '')
17159               .split(/ *\| */);
17160           }
17161     
17162           this.tokens.push(item);
17163     
17164           continue;
17165         }
17166     
17167         // top-level paragraph
17168         if (top && (cap = this.rules.paragraph.exec(src))) {
17169           src = src.substring(cap[0].length);
17170           this.tokens.push({
17171             type: 'paragraph',
17172             text: cap[1].charAt(cap[1].length - 1) === '\n'
17173               ? cap[1].slice(0, -1)
17174               : cap[1]
17175           });
17176           continue;
17177         }
17178     
17179         // text
17180         if (cap = this.rules.text.exec(src)) {
17181           // Top-level should never reach here.
17182           src = src.substring(cap[0].length);
17183           this.tokens.push({
17184             type: 'text',
17185             text: cap[0]
17186           });
17187           continue;
17188         }
17189     
17190         if (src) {
17191           throw new
17192             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17193         }
17194       }
17195     
17196       return this.tokens;
17197     };
17198     
17199     /**
17200      * Inline-Level Grammar
17201      */
17202     
17203     var inline = {
17204       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17205       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17206       url: noop,
17207       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17208       link: /^!?\[(inside)\]\(href\)/,
17209       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17210       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17211       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17212       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17213       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17214       br: /^ {2,}\n(?!\s*$)/,
17215       del: noop,
17216       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17217     };
17218     
17219     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17220     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17221     
17222     inline.link = replace(inline.link)
17223       ('inside', inline._inside)
17224       ('href', inline._href)
17225       ();
17226     
17227     inline.reflink = replace(inline.reflink)
17228       ('inside', inline._inside)
17229       ();
17230     
17231     /**
17232      * Normal Inline Grammar
17233      */
17234     
17235     inline.normal = merge({}, inline);
17236     
17237     /**
17238      * Pedantic Inline Grammar
17239      */
17240     
17241     inline.pedantic = merge({}, inline.normal, {
17242       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17243       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17244     });
17245     
17246     /**
17247      * GFM Inline Grammar
17248      */
17249     
17250     inline.gfm = merge({}, inline.normal, {
17251       escape: replace(inline.escape)('])', '~|])')(),
17252       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17253       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17254       text: replace(inline.text)
17255         (']|', '~]|')
17256         ('|', '|https?://|')
17257         ()
17258     });
17259     
17260     /**
17261      * GFM + Line Breaks Inline Grammar
17262      */
17263     
17264     inline.breaks = merge({}, inline.gfm, {
17265       br: replace(inline.br)('{2,}', '*')(),
17266       text: replace(inline.gfm.text)('{2,}', '*')()
17267     });
17268     
17269     /**
17270      * Inline Lexer & Compiler
17271      */
17272     
17273     function InlineLexer(links, options) {
17274       this.options = options || marked.defaults;
17275       this.links = links;
17276       this.rules = inline.normal;
17277       this.renderer = this.options.renderer || new Renderer;
17278       this.renderer.options = this.options;
17279     
17280       if (!this.links) {
17281         throw new
17282           Error('Tokens array requires a `links` property.');
17283       }
17284     
17285       if (this.options.gfm) {
17286         if (this.options.breaks) {
17287           this.rules = inline.breaks;
17288         } else {
17289           this.rules = inline.gfm;
17290         }
17291       } else if (this.options.pedantic) {
17292         this.rules = inline.pedantic;
17293       }
17294     }
17295     
17296     /**
17297      * Expose Inline Rules
17298      */
17299     
17300     InlineLexer.rules = inline;
17301     
17302     /**
17303      * Static Lexing/Compiling Method
17304      */
17305     
17306     InlineLexer.output = function(src, links, options) {
17307       var inline = new InlineLexer(links, options);
17308       return inline.output(src);
17309     };
17310     
17311     /**
17312      * Lexing/Compiling
17313      */
17314     
17315     InlineLexer.prototype.output = function(src) {
17316       var out = ''
17317         , link
17318         , text
17319         , href
17320         , cap;
17321     
17322       while (src) {
17323         // escape
17324         if (cap = this.rules.escape.exec(src)) {
17325           src = src.substring(cap[0].length);
17326           out += cap[1];
17327           continue;
17328         }
17329     
17330         // autolink
17331         if (cap = this.rules.autolink.exec(src)) {
17332           src = src.substring(cap[0].length);
17333           if (cap[2] === '@') {
17334             text = cap[1].charAt(6) === ':'
17335               ? this.mangle(cap[1].substring(7))
17336               : this.mangle(cap[1]);
17337             href = this.mangle('mailto:') + text;
17338           } else {
17339             text = escape(cap[1]);
17340             href = text;
17341           }
17342           out += this.renderer.link(href, null, text);
17343           continue;
17344         }
17345     
17346         // url (gfm)
17347         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17348           src = src.substring(cap[0].length);
17349           text = escape(cap[1]);
17350           href = text;
17351           out += this.renderer.link(href, null, text);
17352           continue;
17353         }
17354     
17355         // tag
17356         if (cap = this.rules.tag.exec(src)) {
17357           if (!this.inLink && /^<a /i.test(cap[0])) {
17358             this.inLink = true;
17359           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17360             this.inLink = false;
17361           }
17362           src = src.substring(cap[0].length);
17363           out += this.options.sanitize
17364             ? this.options.sanitizer
17365               ? this.options.sanitizer(cap[0])
17366               : escape(cap[0])
17367             : cap[0];
17368           continue;
17369         }
17370     
17371         // link
17372         if (cap = this.rules.link.exec(src)) {
17373           src = src.substring(cap[0].length);
17374           this.inLink = true;
17375           out += this.outputLink(cap, {
17376             href: cap[2],
17377             title: cap[3]
17378           });
17379           this.inLink = false;
17380           continue;
17381         }
17382     
17383         // reflink, nolink
17384         if ((cap = this.rules.reflink.exec(src))
17385             || (cap = this.rules.nolink.exec(src))) {
17386           src = src.substring(cap[0].length);
17387           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17388           link = this.links[link.toLowerCase()];
17389           if (!link || !link.href) {
17390             out += cap[0].charAt(0);
17391             src = cap[0].substring(1) + src;
17392             continue;
17393           }
17394           this.inLink = true;
17395           out += this.outputLink(cap, link);
17396           this.inLink = false;
17397           continue;
17398         }
17399     
17400         // strong
17401         if (cap = this.rules.strong.exec(src)) {
17402           src = src.substring(cap[0].length);
17403           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17404           continue;
17405         }
17406     
17407         // em
17408         if (cap = this.rules.em.exec(src)) {
17409           src = src.substring(cap[0].length);
17410           out += this.renderer.em(this.output(cap[2] || cap[1]));
17411           continue;
17412         }
17413     
17414         // code
17415         if (cap = this.rules.code.exec(src)) {
17416           src = src.substring(cap[0].length);
17417           out += this.renderer.codespan(escape(cap[2], true));
17418           continue;
17419         }
17420     
17421         // br
17422         if (cap = this.rules.br.exec(src)) {
17423           src = src.substring(cap[0].length);
17424           out += this.renderer.br();
17425           continue;
17426         }
17427     
17428         // del (gfm)
17429         if (cap = this.rules.del.exec(src)) {
17430           src = src.substring(cap[0].length);
17431           out += this.renderer.del(this.output(cap[1]));
17432           continue;
17433         }
17434     
17435         // text
17436         if (cap = this.rules.text.exec(src)) {
17437           src = src.substring(cap[0].length);
17438           out += this.renderer.text(escape(this.smartypants(cap[0])));
17439           continue;
17440         }
17441     
17442         if (src) {
17443           throw new
17444             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17445         }
17446       }
17447     
17448       return out;
17449     };
17450     
17451     /**
17452      * Compile Link
17453      */
17454     
17455     InlineLexer.prototype.outputLink = function(cap, link) {
17456       var href = escape(link.href)
17457         , title = link.title ? escape(link.title) : null;
17458     
17459       return cap[0].charAt(0) !== '!'
17460         ? this.renderer.link(href, title, this.output(cap[1]))
17461         : this.renderer.image(href, title, escape(cap[1]));
17462     };
17463     
17464     /**
17465      * Smartypants Transformations
17466      */
17467     
17468     InlineLexer.prototype.smartypants = function(text) {
17469       if (!this.options.smartypants)  { return text; }
17470       return text
17471         // em-dashes
17472         .replace(/---/g, '\u2014')
17473         // en-dashes
17474         .replace(/--/g, '\u2013')
17475         // opening singles
17476         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17477         // closing singles & apostrophes
17478         .replace(/'/g, '\u2019')
17479         // opening doubles
17480         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17481         // closing doubles
17482         .replace(/"/g, '\u201d')
17483         // ellipses
17484         .replace(/\.{3}/g, '\u2026');
17485     };
17486     
17487     /**
17488      * Mangle Links
17489      */
17490     
17491     InlineLexer.prototype.mangle = function(text) {
17492       if (!this.options.mangle) { return text; }
17493       var out = ''
17494         , l = text.length
17495         , i = 0
17496         , ch;
17497     
17498       for (; i < l; i++) {
17499         ch = text.charCodeAt(i);
17500         if (Math.random() > 0.5) {
17501           ch = 'x' + ch.toString(16);
17502         }
17503         out += '&#' + ch + ';';
17504       }
17505     
17506       return out;
17507     };
17508     
17509     /**
17510      * Renderer
17511      */
17512     
17513     function Renderer(options) {
17514       this.options = options || {};
17515     }
17516     
17517     Renderer.prototype.code = function(code, lang, escaped) {
17518       if (this.options.highlight) {
17519         var out = this.options.highlight(code, lang);
17520         if (out != null && out !== code) {
17521           escaped = true;
17522           code = out;
17523         }
17524       } else {
17525             // hack!!! - it's already escapeD?
17526             escaped = true;
17527       }
17528     
17529       if (!lang) {
17530         return '<pre><code>'
17531           + (escaped ? code : escape(code, true))
17532           + '\n</code></pre>';
17533       }
17534     
17535       return '<pre><code class="'
17536         + this.options.langPrefix
17537         + escape(lang, true)
17538         + '">'
17539         + (escaped ? code : escape(code, true))
17540         + '\n</code></pre>\n';
17541     };
17542     
17543     Renderer.prototype.blockquote = function(quote) {
17544       return '<blockquote>\n' + quote + '</blockquote>\n';
17545     };
17546     
17547     Renderer.prototype.html = function(html) {
17548       return html;
17549     };
17550     
17551     Renderer.prototype.heading = function(text, level, raw) {
17552       return '<h'
17553         + level
17554         + ' id="'
17555         + this.options.headerPrefix
17556         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17557         + '">'
17558         + text
17559         + '</h'
17560         + level
17561         + '>\n';
17562     };
17563     
17564     Renderer.prototype.hr = function() {
17565       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17566     };
17567     
17568     Renderer.prototype.list = function(body, ordered) {
17569       var type = ordered ? 'ol' : 'ul';
17570       return '<' + type + '>\n' + body + '</' + type + '>\n';
17571     };
17572     
17573     Renderer.prototype.listitem = function(text) {
17574       return '<li>' + text + '</li>\n';
17575     };
17576     
17577     Renderer.prototype.paragraph = function(text) {
17578       return '<p>' + text + '</p>\n';
17579     };
17580     
17581     Renderer.prototype.table = function(header, body) {
17582       return '<table class="table table-striped">\n'
17583         + '<thead>\n'
17584         + header
17585         + '</thead>\n'
17586         + '<tbody>\n'
17587         + body
17588         + '</tbody>\n'
17589         + '</table>\n';
17590     };
17591     
17592     Renderer.prototype.tablerow = function(content) {
17593       return '<tr>\n' + content + '</tr>\n';
17594     };
17595     
17596     Renderer.prototype.tablecell = function(content, flags) {
17597       var type = flags.header ? 'th' : 'td';
17598       var tag = flags.align
17599         ? '<' + type + ' style="text-align:' + flags.align + '">'
17600         : '<' + type + '>';
17601       return tag + content + '</' + type + '>\n';
17602     };
17603     
17604     // span level renderer
17605     Renderer.prototype.strong = function(text) {
17606       return '<strong>' + text + '</strong>';
17607     };
17608     
17609     Renderer.prototype.em = function(text) {
17610       return '<em>' + text + '</em>';
17611     };
17612     
17613     Renderer.prototype.codespan = function(text) {
17614       return '<code>' + text + '</code>';
17615     };
17616     
17617     Renderer.prototype.br = function() {
17618       return this.options.xhtml ? '<br/>' : '<br>';
17619     };
17620     
17621     Renderer.prototype.del = function(text) {
17622       return '<del>' + text + '</del>';
17623     };
17624     
17625     Renderer.prototype.link = function(href, title, text) {
17626       if (this.options.sanitize) {
17627         try {
17628           var prot = decodeURIComponent(unescape(href))
17629             .replace(/[^\w:]/g, '')
17630             .toLowerCase();
17631         } catch (e) {
17632           return '';
17633         }
17634         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17635           return '';
17636         }
17637       }
17638       var out = '<a href="' + href + '"';
17639       if (title) {
17640         out += ' title="' + title + '"';
17641       }
17642       out += '>' + text + '</a>';
17643       return out;
17644     };
17645     
17646     Renderer.prototype.image = function(href, title, text) {
17647       var out = '<img src="' + href + '" alt="' + text + '"';
17648       if (title) {
17649         out += ' title="' + title + '"';
17650       }
17651       out += this.options.xhtml ? '/>' : '>';
17652       return out;
17653     };
17654     
17655     Renderer.prototype.text = function(text) {
17656       return text;
17657     };
17658     
17659     /**
17660      * Parsing & Compiling
17661      */
17662     
17663     function Parser(options) {
17664       this.tokens = [];
17665       this.token = null;
17666       this.options = options || marked.defaults;
17667       this.options.renderer = this.options.renderer || new Renderer;
17668       this.renderer = this.options.renderer;
17669       this.renderer.options = this.options;
17670     }
17671     
17672     /**
17673      * Static Parse Method
17674      */
17675     
17676     Parser.parse = function(src, options, renderer) {
17677       var parser = new Parser(options, renderer);
17678       return parser.parse(src);
17679     };
17680     
17681     /**
17682      * Parse Loop
17683      */
17684     
17685     Parser.prototype.parse = function(src) {
17686       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17687       this.tokens = src.reverse();
17688     
17689       var out = '';
17690       while (this.next()) {
17691         out += this.tok();
17692       }
17693     
17694       return out;
17695     };
17696     
17697     /**
17698      * Next Token
17699      */
17700     
17701     Parser.prototype.next = function() {
17702       return this.token = this.tokens.pop();
17703     };
17704     
17705     /**
17706      * Preview Next Token
17707      */
17708     
17709     Parser.prototype.peek = function() {
17710       return this.tokens[this.tokens.length - 1] || 0;
17711     };
17712     
17713     /**
17714      * Parse Text Tokens
17715      */
17716     
17717     Parser.prototype.parseText = function() {
17718       var body = this.token.text;
17719     
17720       while (this.peek().type === 'text') {
17721         body += '\n' + this.next().text;
17722       }
17723     
17724       return this.inline.output(body);
17725     };
17726     
17727     /**
17728      * Parse Current Token
17729      */
17730     
17731     Parser.prototype.tok = function() {
17732       switch (this.token.type) {
17733         case 'space': {
17734           return '';
17735         }
17736         case 'hr': {
17737           return this.renderer.hr();
17738         }
17739         case 'heading': {
17740           return this.renderer.heading(
17741             this.inline.output(this.token.text),
17742             this.token.depth,
17743             this.token.text);
17744         }
17745         case 'code': {
17746           return this.renderer.code(this.token.text,
17747             this.token.lang,
17748             this.token.escaped);
17749         }
17750         case 'table': {
17751           var header = ''
17752             , body = ''
17753             , i
17754             , row
17755             , cell
17756             , flags
17757             , j;
17758     
17759           // header
17760           cell = '';
17761           for (i = 0; i < this.token.header.length; i++) {
17762             flags = { header: true, align: this.token.align[i] };
17763             cell += this.renderer.tablecell(
17764               this.inline.output(this.token.header[i]),
17765               { header: true, align: this.token.align[i] }
17766             );
17767           }
17768           header += this.renderer.tablerow(cell);
17769     
17770           for (i = 0; i < this.token.cells.length; i++) {
17771             row = this.token.cells[i];
17772     
17773             cell = '';
17774             for (j = 0; j < row.length; j++) {
17775               cell += this.renderer.tablecell(
17776                 this.inline.output(row[j]),
17777                 { header: false, align: this.token.align[j] }
17778               );
17779             }
17780     
17781             body += this.renderer.tablerow(cell);
17782           }
17783           return this.renderer.table(header, body);
17784         }
17785         case 'blockquote_start': {
17786           var body = '';
17787     
17788           while (this.next().type !== 'blockquote_end') {
17789             body += this.tok();
17790           }
17791     
17792           return this.renderer.blockquote(body);
17793         }
17794         case 'list_start': {
17795           var body = ''
17796             , ordered = this.token.ordered;
17797     
17798           while (this.next().type !== 'list_end') {
17799             body += this.tok();
17800           }
17801     
17802           return this.renderer.list(body, ordered);
17803         }
17804         case 'list_item_start': {
17805           var body = '';
17806     
17807           while (this.next().type !== 'list_item_end') {
17808             body += this.token.type === 'text'
17809               ? this.parseText()
17810               : this.tok();
17811           }
17812     
17813           return this.renderer.listitem(body);
17814         }
17815         case 'loose_item_start': {
17816           var body = '';
17817     
17818           while (this.next().type !== 'list_item_end') {
17819             body += this.tok();
17820           }
17821     
17822           return this.renderer.listitem(body);
17823         }
17824         case 'html': {
17825           var html = !this.token.pre && !this.options.pedantic
17826             ? this.inline.output(this.token.text)
17827             : this.token.text;
17828           return this.renderer.html(html);
17829         }
17830         case 'paragraph': {
17831           return this.renderer.paragraph(this.inline.output(this.token.text));
17832         }
17833         case 'text': {
17834           return this.renderer.paragraph(this.parseText());
17835         }
17836       }
17837     };
17838     
17839     /**
17840      * Helpers
17841      */
17842     
17843     function escape(html, encode) {
17844       return html
17845         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17846         .replace(/</g, '&lt;')
17847         .replace(/>/g, '&gt;')
17848         .replace(/"/g, '&quot;')
17849         .replace(/'/g, '&#39;');
17850     }
17851     
17852     function unescape(html) {
17853         // explicitly match decimal, hex, and named HTML entities 
17854       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17855         n = n.toLowerCase();
17856         if (n === 'colon') { return ':'; }
17857         if (n.charAt(0) === '#') {
17858           return n.charAt(1) === 'x'
17859             ? String.fromCharCode(parseInt(n.substring(2), 16))
17860             : String.fromCharCode(+n.substring(1));
17861         }
17862         return '';
17863       });
17864     }
17865     
17866     function replace(regex, opt) {
17867       regex = regex.source;
17868       opt = opt || '';
17869       return function self(name, val) {
17870         if (!name) { return new RegExp(regex, opt); }
17871         val = val.source || val;
17872         val = val.replace(/(^|[^\[])\^/g, '$1');
17873         regex = regex.replace(name, val);
17874         return self;
17875       };
17876     }
17877     
17878     function noop() {}
17879     noop.exec = noop;
17880     
17881     function merge(obj) {
17882       var i = 1
17883         , target
17884         , key;
17885     
17886       for (; i < arguments.length; i++) {
17887         target = arguments[i];
17888         for (key in target) {
17889           if (Object.prototype.hasOwnProperty.call(target, key)) {
17890             obj[key] = target[key];
17891           }
17892         }
17893       }
17894     
17895       return obj;
17896     }
17897     
17898     
17899     /**
17900      * Marked
17901      */
17902     
17903     function marked(src, opt, callback) {
17904       if (callback || typeof opt === 'function') {
17905         if (!callback) {
17906           callback = opt;
17907           opt = null;
17908         }
17909     
17910         opt = merge({}, marked.defaults, opt || {});
17911     
17912         var highlight = opt.highlight
17913           , tokens
17914           , pending
17915           , i = 0;
17916     
17917         try {
17918           tokens = Lexer.lex(src, opt)
17919         } catch (e) {
17920           return callback(e);
17921         }
17922     
17923         pending = tokens.length;
17924     
17925         var done = function(err) {
17926           if (err) {
17927             opt.highlight = highlight;
17928             return callback(err);
17929           }
17930     
17931           var out;
17932     
17933           try {
17934             out = Parser.parse(tokens, opt);
17935           } catch (e) {
17936             err = e;
17937           }
17938     
17939           opt.highlight = highlight;
17940     
17941           return err
17942             ? callback(err)
17943             : callback(null, out);
17944         };
17945     
17946         if (!highlight || highlight.length < 3) {
17947           return done();
17948         }
17949     
17950         delete opt.highlight;
17951     
17952         if (!pending) { return done(); }
17953     
17954         for (; i < tokens.length; i++) {
17955           (function(token) {
17956             if (token.type !== 'code') {
17957               return --pending || done();
17958             }
17959             return highlight(token.text, token.lang, function(err, code) {
17960               if (err) { return done(err); }
17961               if (code == null || code === token.text) {
17962                 return --pending || done();
17963               }
17964               token.text = code;
17965               token.escaped = true;
17966               --pending || done();
17967             });
17968           })(tokens[i]);
17969         }
17970     
17971         return;
17972       }
17973       try {
17974         if (opt) { opt = merge({}, marked.defaults, opt); }
17975         return Parser.parse(Lexer.lex(src, opt), opt);
17976       } catch (e) {
17977         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17978         if ((opt || marked.defaults).silent) {
17979           return '<p>An error occured:</p><pre>'
17980             + escape(e.message + '', true)
17981             + '</pre>';
17982         }
17983         throw e;
17984       }
17985     }
17986     
17987     /**
17988      * Options
17989      */
17990     
17991     marked.options =
17992     marked.setOptions = function(opt) {
17993       merge(marked.defaults, opt);
17994       return marked;
17995     };
17996     
17997     marked.defaults = {
17998       gfm: true,
17999       tables: true,
18000       breaks: false,
18001       pedantic: false,
18002       sanitize: false,
18003       sanitizer: null,
18004       mangle: true,
18005       smartLists: false,
18006       silent: false,
18007       highlight: null,
18008       langPrefix: 'lang-',
18009       smartypants: false,
18010       headerPrefix: '',
18011       renderer: new Renderer,
18012       xhtml: false
18013     };
18014     
18015     /**
18016      * Expose
18017      */
18018     
18019     marked.Parser = Parser;
18020     marked.parser = Parser.parse;
18021     
18022     marked.Renderer = Renderer;
18023     
18024     marked.Lexer = Lexer;
18025     marked.lexer = Lexer.lex;
18026     
18027     marked.InlineLexer = InlineLexer;
18028     marked.inlineLexer = InlineLexer.output;
18029     
18030     marked.parse = marked;
18031     
18032     Roo.Markdown.marked = marked;
18033
18034 })();/*
18035  * Based on:
18036  * Ext JS Library 1.1.1
18037  * Copyright(c) 2006-2007, Ext JS, LLC.
18038  *
18039  * Originally Released Under LGPL - original licence link has changed is not relivant.
18040  *
18041  * Fork - LGPL
18042  * <script type="text/javascript">
18043  */
18044
18045
18046
18047 /*
18048  * These classes are derivatives of the similarly named classes in the YUI Library.
18049  * The original license:
18050  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18051  * Code licensed under the BSD License:
18052  * http://developer.yahoo.net/yui/license.txt
18053  */
18054
18055 (function() {
18056
18057 var Event=Roo.EventManager;
18058 var Dom=Roo.lib.Dom;
18059
18060 /**
18061  * @class Roo.dd.DragDrop
18062  * @extends Roo.util.Observable
18063  * Defines the interface and base operation of items that that can be
18064  * dragged or can be drop targets.  It was designed to be extended, overriding
18065  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18066  * Up to three html elements can be associated with a DragDrop instance:
18067  * <ul>
18068  * <li>linked element: the element that is passed into the constructor.
18069  * This is the element which defines the boundaries for interaction with
18070  * other DragDrop objects.</li>
18071  * <li>handle element(s): The drag operation only occurs if the element that
18072  * was clicked matches a handle element.  By default this is the linked
18073  * element, but there are times that you will want only a portion of the
18074  * linked element to initiate the drag operation, and the setHandleElId()
18075  * method provides a way to define this.</li>
18076  * <li>drag element: this represents the element that would be moved along
18077  * with the cursor during a drag operation.  By default, this is the linked
18078  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18079  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18080  * </li>
18081  * </ul>
18082  * This class should not be instantiated until the onload event to ensure that
18083  * the associated elements are available.
18084  * The following would define a DragDrop obj that would interact with any
18085  * other DragDrop obj in the "group1" group:
18086  * <pre>
18087  *  dd = new Roo.dd.DragDrop("div1", "group1");
18088  * </pre>
18089  * Since none of the event handlers have been implemented, nothing would
18090  * actually happen if you were to run the code above.  Normally you would
18091  * override this class or one of the default implementations, but you can
18092  * also override the methods you want on an instance of the class...
18093  * <pre>
18094  *  dd.onDragDrop = function(e, id) {
18095  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18096  *  }
18097  * </pre>
18098  * @constructor
18099  * @param {String} id of the element that is linked to this instance
18100  * @param {String} sGroup the group of related DragDrop objects
18101  * @param {object} config an object containing configurable attributes
18102  *                Valid properties for DragDrop:
18103  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18104  */
18105 Roo.dd.DragDrop = function(id, sGroup, config) {
18106     if (id) {
18107         this.init(id, sGroup, config);
18108     }
18109     
18110 };
18111
18112 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18113
18114     /**
18115      * The id of the element associated with this object.  This is what we
18116      * refer to as the "linked element" because the size and position of
18117      * this element is used to determine when the drag and drop objects have
18118      * interacted.
18119      * @property id
18120      * @type String
18121      */
18122     id: null,
18123
18124     /**
18125      * Configuration attributes passed into the constructor
18126      * @property config
18127      * @type object
18128      */
18129     config: null,
18130
18131     /**
18132      * The id of the element that will be dragged.  By default this is same
18133      * as the linked element , but could be changed to another element. Ex:
18134      * Roo.dd.DDProxy
18135      * @property dragElId
18136      * @type String
18137      * @private
18138      */
18139     dragElId: null,
18140
18141     /**
18142      * the id of the element that initiates the drag operation.  By default
18143      * this is the linked element, but could be changed to be a child of this
18144      * element.  This lets us do things like only starting the drag when the
18145      * header element within the linked html element is clicked.
18146      * @property handleElId
18147      * @type String
18148      * @private
18149      */
18150     handleElId: null,
18151
18152     /**
18153      * An associative array of HTML tags that will be ignored if clicked.
18154      * @property invalidHandleTypes
18155      * @type {string: string}
18156      */
18157     invalidHandleTypes: null,
18158
18159     /**
18160      * An associative array of ids for elements that will be ignored if clicked
18161      * @property invalidHandleIds
18162      * @type {string: string}
18163      */
18164     invalidHandleIds: null,
18165
18166     /**
18167      * An indexted array of css class names for elements that will be ignored
18168      * if clicked.
18169      * @property invalidHandleClasses
18170      * @type string[]
18171      */
18172     invalidHandleClasses: null,
18173
18174     /**
18175      * The linked element's absolute X position at the time the drag was
18176      * started
18177      * @property startPageX
18178      * @type int
18179      * @private
18180      */
18181     startPageX: 0,
18182
18183     /**
18184      * The linked element's absolute X position at the time the drag was
18185      * started
18186      * @property startPageY
18187      * @type int
18188      * @private
18189      */
18190     startPageY: 0,
18191
18192     /**
18193      * The group defines a logical collection of DragDrop objects that are
18194      * related.  Instances only get events when interacting with other
18195      * DragDrop object in the same group.  This lets us define multiple
18196      * groups using a single DragDrop subclass if we want.
18197      * @property groups
18198      * @type {string: string}
18199      */
18200     groups: null,
18201
18202     /**
18203      * Individual drag/drop instances can be locked.  This will prevent
18204      * onmousedown start drag.
18205      * @property locked
18206      * @type boolean
18207      * @private
18208      */
18209     locked: false,
18210
18211     /**
18212      * Lock this instance
18213      * @method lock
18214      */
18215     lock: function() { this.locked = true; },
18216
18217     /**
18218      * Unlock this instace
18219      * @method unlock
18220      */
18221     unlock: function() { this.locked = false; },
18222
18223     /**
18224      * By default, all insances can be a drop target.  This can be disabled by
18225      * setting isTarget to false.
18226      * @method isTarget
18227      * @type boolean
18228      */
18229     isTarget: true,
18230
18231     /**
18232      * The padding configured for this drag and drop object for calculating
18233      * the drop zone intersection with this object.
18234      * @method padding
18235      * @type int[]
18236      */
18237     padding: null,
18238
18239     /**
18240      * Cached reference to the linked element
18241      * @property _domRef
18242      * @private
18243      */
18244     _domRef: null,
18245
18246     /**
18247      * Internal typeof flag
18248      * @property __ygDragDrop
18249      * @private
18250      */
18251     __ygDragDrop: true,
18252
18253     /**
18254      * Set to true when horizontal contraints are applied
18255      * @property constrainX
18256      * @type boolean
18257      * @private
18258      */
18259     constrainX: false,
18260
18261     /**
18262      * Set to true when vertical contraints are applied
18263      * @property constrainY
18264      * @type boolean
18265      * @private
18266      */
18267     constrainY: false,
18268
18269     /**
18270      * The left constraint
18271      * @property minX
18272      * @type int
18273      * @private
18274      */
18275     minX: 0,
18276
18277     /**
18278      * The right constraint
18279      * @property maxX
18280      * @type int
18281      * @private
18282      */
18283     maxX: 0,
18284
18285     /**
18286      * The up constraint
18287      * @property minY
18288      * @type int
18289      * @type int
18290      * @private
18291      */
18292     minY: 0,
18293
18294     /**
18295      * The down constraint
18296      * @property maxY
18297      * @type int
18298      * @private
18299      */
18300     maxY: 0,
18301
18302     /**
18303      * Maintain offsets when we resetconstraints.  Set to true when you want
18304      * the position of the element relative to its parent to stay the same
18305      * when the page changes
18306      *
18307      * @property maintainOffset
18308      * @type boolean
18309      */
18310     maintainOffset: false,
18311
18312     /**
18313      * Array of pixel locations the element will snap to if we specified a
18314      * horizontal graduation/interval.  This array is generated automatically
18315      * when you define a tick interval.
18316      * @property xTicks
18317      * @type int[]
18318      */
18319     xTicks: null,
18320
18321     /**
18322      * Array of pixel locations the element will snap to if we specified a
18323      * vertical graduation/interval.  This array is generated automatically
18324      * when you define a tick interval.
18325      * @property yTicks
18326      * @type int[]
18327      */
18328     yTicks: null,
18329
18330     /**
18331      * By default the drag and drop instance will only respond to the primary
18332      * button click (left button for a right-handed mouse).  Set to true to
18333      * allow drag and drop to start with any mouse click that is propogated
18334      * by the browser
18335      * @property primaryButtonOnly
18336      * @type boolean
18337      */
18338     primaryButtonOnly: true,
18339
18340     /**
18341      * The availabe property is false until the linked dom element is accessible.
18342      * @property available
18343      * @type boolean
18344      */
18345     available: false,
18346
18347     /**
18348      * By default, drags can only be initiated if the mousedown occurs in the
18349      * region the linked element is.  This is done in part to work around a
18350      * bug in some browsers that mis-report the mousedown if the previous
18351      * mouseup happened outside of the window.  This property is set to true
18352      * if outer handles are defined.
18353      *
18354      * @property hasOuterHandles
18355      * @type boolean
18356      * @default false
18357      */
18358     hasOuterHandles: false,
18359
18360     /**
18361      * Code that executes immediately before the startDrag event
18362      * @method b4StartDrag
18363      * @private
18364      */
18365     b4StartDrag: function(x, y) { },
18366
18367     /**
18368      * Abstract method called after a drag/drop object is clicked
18369      * and the drag or mousedown time thresholds have beeen met.
18370      * @method startDrag
18371      * @param {int} X click location
18372      * @param {int} Y click location
18373      */
18374     startDrag: function(x, y) { /* override this */ },
18375
18376     /**
18377      * Code that executes immediately before the onDrag event
18378      * @method b4Drag
18379      * @private
18380      */
18381     b4Drag: function(e) { },
18382
18383     /**
18384      * Abstract method called during the onMouseMove event while dragging an
18385      * object.
18386      * @method onDrag
18387      * @param {Event} e the mousemove event
18388      */
18389     onDrag: function(e) { /* override this */ },
18390
18391     /**
18392      * Abstract method called when this element fist begins hovering over
18393      * another DragDrop obj
18394      * @method onDragEnter
18395      * @param {Event} e the mousemove event
18396      * @param {String|DragDrop[]} id In POINT mode, the element
18397      * id this is hovering over.  In INTERSECT mode, an array of one or more
18398      * dragdrop items being hovered over.
18399      */
18400     onDragEnter: function(e, id) { /* override this */ },
18401
18402     /**
18403      * Code that executes immediately before the onDragOver event
18404      * @method b4DragOver
18405      * @private
18406      */
18407     b4DragOver: function(e) { },
18408
18409     /**
18410      * Abstract method called when this element is hovering over another
18411      * DragDrop obj
18412      * @method onDragOver
18413      * @param {Event} e the mousemove event
18414      * @param {String|DragDrop[]} id In POINT mode, the element
18415      * id this is hovering over.  In INTERSECT mode, an array of dd items
18416      * being hovered over.
18417      */
18418     onDragOver: function(e, id) { /* override this */ },
18419
18420     /**
18421      * Code that executes immediately before the onDragOut event
18422      * @method b4DragOut
18423      * @private
18424      */
18425     b4DragOut: function(e) { },
18426
18427     /**
18428      * Abstract method called when we are no longer hovering over an element
18429      * @method onDragOut
18430      * @param {Event} e the mousemove event
18431      * @param {String|DragDrop[]} id In POINT mode, the element
18432      * id this was hovering over.  In INTERSECT mode, an array of dd items
18433      * that the mouse is no longer over.
18434      */
18435     onDragOut: function(e, id) { /* override this */ },
18436
18437     /**
18438      * Code that executes immediately before the onDragDrop event
18439      * @method b4DragDrop
18440      * @private
18441      */
18442     b4DragDrop: function(e) { },
18443
18444     /**
18445      * Abstract method called when this item is dropped on another DragDrop
18446      * obj
18447      * @method onDragDrop
18448      * @param {Event} e the mouseup event
18449      * @param {String|DragDrop[]} id In POINT mode, the element
18450      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18451      * was dropped on.
18452      */
18453     onDragDrop: function(e, id) { /* override this */ },
18454
18455     /**
18456      * Abstract method called when this item is dropped on an area with no
18457      * drop target
18458      * @method onInvalidDrop
18459      * @param {Event} e the mouseup event
18460      */
18461     onInvalidDrop: function(e) { /* override this */ },
18462
18463     /**
18464      * Code that executes immediately before the endDrag event
18465      * @method b4EndDrag
18466      * @private
18467      */
18468     b4EndDrag: function(e) { },
18469
18470     /**
18471      * Fired when we are done dragging the object
18472      * @method endDrag
18473      * @param {Event} e the mouseup event
18474      */
18475     endDrag: function(e) { /* override this */ },
18476
18477     /**
18478      * Code executed immediately before the onMouseDown event
18479      * @method b4MouseDown
18480      * @param {Event} e the mousedown event
18481      * @private
18482      */
18483     b4MouseDown: function(e) {  },
18484
18485     /**
18486      * Event handler that fires when a drag/drop obj gets a mousedown
18487      * @method onMouseDown
18488      * @param {Event} e the mousedown event
18489      */
18490     onMouseDown: function(e) { /* override this */ },
18491
18492     /**
18493      * Event handler that fires when a drag/drop obj gets a mouseup
18494      * @method onMouseUp
18495      * @param {Event} e the mouseup event
18496      */
18497     onMouseUp: function(e) { /* override this */ },
18498
18499     /**
18500      * Override the onAvailable method to do what is needed after the initial
18501      * position was determined.
18502      * @method onAvailable
18503      */
18504     onAvailable: function () {
18505     },
18506
18507     /*
18508      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18509      * @type Object
18510      */
18511     defaultPadding : {left:0, right:0, top:0, bottom:0},
18512
18513     /*
18514      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18515  *
18516  * Usage:
18517  <pre><code>
18518  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18519                 { dragElId: "existingProxyDiv" });
18520  dd.startDrag = function(){
18521      this.constrainTo("parent-id");
18522  };
18523  </code></pre>
18524  * Or you can initalize it using the {@link Roo.Element} object:
18525  <pre><code>
18526  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18527      startDrag : function(){
18528          this.constrainTo("parent-id");
18529      }
18530  });
18531  </code></pre>
18532      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18533      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18534      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18535      * an object containing the sides to pad. For example: {right:10, bottom:10}
18536      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18537      */
18538     constrainTo : function(constrainTo, pad, inContent){
18539         if(typeof pad == "number"){
18540             pad = {left: pad, right:pad, top:pad, bottom:pad};
18541         }
18542         pad = pad || this.defaultPadding;
18543         var b = Roo.get(this.getEl()).getBox();
18544         var ce = Roo.get(constrainTo);
18545         var s = ce.getScroll();
18546         var c, cd = ce.dom;
18547         if(cd == document.body){
18548             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18549         }else{
18550             xy = ce.getXY();
18551             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18552         }
18553
18554
18555         var topSpace = b.y - c.y;
18556         var leftSpace = b.x - c.x;
18557
18558         this.resetConstraints();
18559         this.setXConstraint(leftSpace - (pad.left||0), // left
18560                 c.width - leftSpace - b.width - (pad.right||0) //right
18561         );
18562         this.setYConstraint(topSpace - (pad.top||0), //top
18563                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18564         );
18565     },
18566
18567     /**
18568      * Returns a reference to the linked element
18569      * @method getEl
18570      * @return {HTMLElement} the html element
18571      */
18572     getEl: function() {
18573         if (!this._domRef) {
18574             this._domRef = Roo.getDom(this.id);
18575         }
18576
18577         return this._domRef;
18578     },
18579
18580     /**
18581      * Returns a reference to the actual element to drag.  By default this is
18582      * the same as the html element, but it can be assigned to another
18583      * element. An example of this can be found in Roo.dd.DDProxy
18584      * @method getDragEl
18585      * @return {HTMLElement} the html element
18586      */
18587     getDragEl: function() {
18588         return Roo.getDom(this.dragElId);
18589     },
18590
18591     /**
18592      * Sets up the DragDrop object.  Must be called in the constructor of any
18593      * Roo.dd.DragDrop subclass
18594      * @method init
18595      * @param id the id of the linked element
18596      * @param {String} sGroup the group of related items
18597      * @param {object} config configuration attributes
18598      */
18599     init: function(id, sGroup, config) {
18600         this.initTarget(id, sGroup, config);
18601         if (!Roo.isTouch) {
18602             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18603         }
18604         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18605         // Event.on(this.id, "selectstart", Event.preventDefault);
18606     },
18607
18608     /**
18609      * Initializes Targeting functionality only... the object does not
18610      * get a mousedown handler.
18611      * @method initTarget
18612      * @param id the id of the linked element
18613      * @param {String} sGroup the group of related items
18614      * @param {object} config configuration attributes
18615      */
18616     initTarget: function(id, sGroup, config) {
18617
18618         // configuration attributes
18619         this.config = config || {};
18620
18621         // create a local reference to the drag and drop manager
18622         this.DDM = Roo.dd.DDM;
18623         // initialize the groups array
18624         this.groups = {};
18625
18626         // assume that we have an element reference instead of an id if the
18627         // parameter is not a string
18628         if (typeof id !== "string") {
18629             id = Roo.id(id);
18630         }
18631
18632         // set the id
18633         this.id = id;
18634
18635         // add to an interaction group
18636         this.addToGroup((sGroup) ? sGroup : "default");
18637
18638         // We don't want to register this as the handle with the manager
18639         // so we just set the id rather than calling the setter.
18640         this.handleElId = id;
18641
18642         // the linked element is the element that gets dragged by default
18643         this.setDragElId(id);
18644
18645         // by default, clicked anchors will not start drag operations.
18646         this.invalidHandleTypes = { A: "A" };
18647         this.invalidHandleIds = {};
18648         this.invalidHandleClasses = [];
18649
18650         this.applyConfig();
18651
18652         this.handleOnAvailable();
18653     },
18654
18655     /**
18656      * Applies the configuration parameters that were passed into the constructor.
18657      * This is supposed to happen at each level through the inheritance chain.  So
18658      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18659      * DragDrop in order to get all of the parameters that are available in
18660      * each object.
18661      * @method applyConfig
18662      */
18663     applyConfig: function() {
18664
18665         // configurable properties:
18666         //    padding, isTarget, maintainOffset, primaryButtonOnly
18667         this.padding           = this.config.padding || [0, 0, 0, 0];
18668         this.isTarget          = (this.config.isTarget !== false);
18669         this.maintainOffset    = (this.config.maintainOffset);
18670         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18671
18672     },
18673
18674     /**
18675      * Executed when the linked element is available
18676      * @method handleOnAvailable
18677      * @private
18678      */
18679     handleOnAvailable: function() {
18680         this.available = true;
18681         this.resetConstraints();
18682         this.onAvailable();
18683     },
18684
18685      /**
18686      * Configures the padding for the target zone in px.  Effectively expands
18687      * (or reduces) the virtual object size for targeting calculations.
18688      * Supports css-style shorthand; if only one parameter is passed, all sides
18689      * will have that padding, and if only two are passed, the top and bottom
18690      * will have the first param, the left and right the second.
18691      * @method setPadding
18692      * @param {int} iTop    Top pad
18693      * @param {int} iRight  Right pad
18694      * @param {int} iBot    Bot pad
18695      * @param {int} iLeft   Left pad
18696      */
18697     setPadding: function(iTop, iRight, iBot, iLeft) {
18698         // this.padding = [iLeft, iRight, iTop, iBot];
18699         if (!iRight && 0 !== iRight) {
18700             this.padding = [iTop, iTop, iTop, iTop];
18701         } else if (!iBot && 0 !== iBot) {
18702             this.padding = [iTop, iRight, iTop, iRight];
18703         } else {
18704             this.padding = [iTop, iRight, iBot, iLeft];
18705         }
18706     },
18707
18708     /**
18709      * Stores the initial placement of the linked element.
18710      * @method setInitialPosition
18711      * @param {int} diffX   the X offset, default 0
18712      * @param {int} diffY   the Y offset, default 0
18713      */
18714     setInitPosition: function(diffX, diffY) {
18715         var el = this.getEl();
18716
18717         if (!this.DDM.verifyEl(el)) {
18718             return;
18719         }
18720
18721         var dx = diffX || 0;
18722         var dy = diffY || 0;
18723
18724         var p = Dom.getXY( el );
18725
18726         this.initPageX = p[0] - dx;
18727         this.initPageY = p[1] - dy;
18728
18729         this.lastPageX = p[0];
18730         this.lastPageY = p[1];
18731
18732
18733         this.setStartPosition(p);
18734     },
18735
18736     /**
18737      * Sets the start position of the element.  This is set when the obj
18738      * is initialized, the reset when a drag is started.
18739      * @method setStartPosition
18740      * @param pos current position (from previous lookup)
18741      * @private
18742      */
18743     setStartPosition: function(pos) {
18744         var p = pos || Dom.getXY( this.getEl() );
18745         this.deltaSetXY = null;
18746
18747         this.startPageX = p[0];
18748         this.startPageY = p[1];
18749     },
18750
18751     /**
18752      * Add this instance to a group of related drag/drop objects.  All
18753      * instances belong to at least one group, and can belong to as many
18754      * groups as needed.
18755      * @method addToGroup
18756      * @param sGroup {string} the name of the group
18757      */
18758     addToGroup: function(sGroup) {
18759         this.groups[sGroup] = true;
18760         this.DDM.regDragDrop(this, sGroup);
18761     },
18762
18763     /**
18764      * Remove's this instance from the supplied interaction group
18765      * @method removeFromGroup
18766      * @param {string}  sGroup  The group to drop
18767      */
18768     removeFromGroup: function(sGroup) {
18769         if (this.groups[sGroup]) {
18770             delete this.groups[sGroup];
18771         }
18772
18773         this.DDM.removeDDFromGroup(this, sGroup);
18774     },
18775
18776     /**
18777      * Allows you to specify that an element other than the linked element
18778      * will be moved with the cursor during a drag
18779      * @method setDragElId
18780      * @param id {string} the id of the element that will be used to initiate the drag
18781      */
18782     setDragElId: function(id) {
18783         this.dragElId = id;
18784     },
18785
18786     /**
18787      * Allows you to specify a child of the linked element that should be
18788      * used to initiate the drag operation.  An example of this would be if
18789      * you have a content div with text and links.  Clicking anywhere in the
18790      * content area would normally start the drag operation.  Use this method
18791      * to specify that an element inside of the content div is the element
18792      * that starts the drag operation.
18793      * @method setHandleElId
18794      * @param id {string} the id of the element that will be used to
18795      * initiate the drag.
18796      */
18797     setHandleElId: function(id) {
18798         if (typeof id !== "string") {
18799             id = Roo.id(id);
18800         }
18801         this.handleElId = id;
18802         this.DDM.regHandle(this.id, id);
18803     },
18804
18805     /**
18806      * Allows you to set an element outside of the linked element as a drag
18807      * handle
18808      * @method setOuterHandleElId
18809      * @param id the id of the element that will be used to initiate the drag
18810      */
18811     setOuterHandleElId: function(id) {
18812         if (typeof id !== "string") {
18813             id = Roo.id(id);
18814         }
18815         Event.on(id, "mousedown",
18816                 this.handleMouseDown, this);
18817         this.setHandleElId(id);
18818
18819         this.hasOuterHandles = true;
18820     },
18821
18822     /**
18823      * Remove all drag and drop hooks for this element
18824      * @method unreg
18825      */
18826     unreg: function() {
18827         Event.un(this.id, "mousedown",
18828                 this.handleMouseDown);
18829         Event.un(this.id, "touchstart",
18830                 this.handleMouseDown);
18831         this._domRef = null;
18832         this.DDM._remove(this);
18833     },
18834
18835     destroy : function(){
18836         this.unreg();
18837     },
18838
18839     /**
18840      * Returns true if this instance is locked, or the drag drop mgr is locked
18841      * (meaning that all drag/drop is disabled on the page.)
18842      * @method isLocked
18843      * @return {boolean} true if this obj or all drag/drop is locked, else
18844      * false
18845      */
18846     isLocked: function() {
18847         return (this.DDM.isLocked() || this.locked);
18848     },
18849
18850     /**
18851      * Fired when this object is clicked
18852      * @method handleMouseDown
18853      * @param {Event} e
18854      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18855      * @private
18856      */
18857     handleMouseDown: function(e, oDD){
18858      
18859         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18860             //Roo.log('not touch/ button !=0');
18861             return;
18862         }
18863         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18864             return; // double touch..
18865         }
18866         
18867
18868         if (this.isLocked()) {
18869             //Roo.log('locked');
18870             return;
18871         }
18872
18873         this.DDM.refreshCache(this.groups);
18874 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18875         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18876         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18877             //Roo.log('no outer handes or not over target');
18878                 // do nothing.
18879         } else {
18880 //            Roo.log('check validator');
18881             if (this.clickValidator(e)) {
18882 //                Roo.log('validate success');
18883                 // set the initial element position
18884                 this.setStartPosition();
18885
18886
18887                 this.b4MouseDown(e);
18888                 this.onMouseDown(e);
18889
18890                 this.DDM.handleMouseDown(e, this);
18891
18892                 this.DDM.stopEvent(e);
18893             } else {
18894
18895
18896             }
18897         }
18898     },
18899
18900     clickValidator: function(e) {
18901         var target = e.getTarget();
18902         return ( this.isValidHandleChild(target) &&
18903                     (this.id == this.handleElId ||
18904                         this.DDM.handleWasClicked(target, this.id)) );
18905     },
18906
18907     /**
18908      * Allows you to specify a tag name that should not start a drag operation
18909      * when clicked.  This is designed to facilitate embedding links within a
18910      * drag handle that do something other than start the drag.
18911      * @method addInvalidHandleType
18912      * @param {string} tagName the type of element to exclude
18913      */
18914     addInvalidHandleType: function(tagName) {
18915         var type = tagName.toUpperCase();
18916         this.invalidHandleTypes[type] = type;
18917     },
18918
18919     /**
18920      * Lets you to specify an element id for a child of a drag handle
18921      * that should not initiate a drag
18922      * @method addInvalidHandleId
18923      * @param {string} id the element id of the element you wish to ignore
18924      */
18925     addInvalidHandleId: function(id) {
18926         if (typeof id !== "string") {
18927             id = Roo.id(id);
18928         }
18929         this.invalidHandleIds[id] = id;
18930     },
18931
18932     /**
18933      * Lets you specify a css class of elements that will not initiate a drag
18934      * @method addInvalidHandleClass
18935      * @param {string} cssClass the class of the elements you wish to ignore
18936      */
18937     addInvalidHandleClass: function(cssClass) {
18938         this.invalidHandleClasses.push(cssClass);
18939     },
18940
18941     /**
18942      * Unsets an excluded tag name set by addInvalidHandleType
18943      * @method removeInvalidHandleType
18944      * @param {string} tagName the type of element to unexclude
18945      */
18946     removeInvalidHandleType: function(tagName) {
18947         var type = tagName.toUpperCase();
18948         // this.invalidHandleTypes[type] = null;
18949         delete this.invalidHandleTypes[type];
18950     },
18951
18952     /**
18953      * Unsets an invalid handle id
18954      * @method removeInvalidHandleId
18955      * @param {string} id the id of the element to re-enable
18956      */
18957     removeInvalidHandleId: function(id) {
18958         if (typeof id !== "string") {
18959             id = Roo.id(id);
18960         }
18961         delete this.invalidHandleIds[id];
18962     },
18963
18964     /**
18965      * Unsets an invalid css class
18966      * @method removeInvalidHandleClass
18967      * @param {string} cssClass the class of the element(s) you wish to
18968      * re-enable
18969      */
18970     removeInvalidHandleClass: function(cssClass) {
18971         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18972             if (this.invalidHandleClasses[i] == cssClass) {
18973                 delete this.invalidHandleClasses[i];
18974             }
18975         }
18976     },
18977
18978     /**
18979      * Checks the tag exclusion list to see if this click should be ignored
18980      * @method isValidHandleChild
18981      * @param {HTMLElement} node the HTMLElement to evaluate
18982      * @return {boolean} true if this is a valid tag type, false if not
18983      */
18984     isValidHandleChild: function(node) {
18985
18986         var valid = true;
18987         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18988         var nodeName;
18989         try {
18990             nodeName = node.nodeName.toUpperCase();
18991         } catch(e) {
18992             nodeName = node.nodeName;
18993         }
18994         valid = valid && !this.invalidHandleTypes[nodeName];
18995         valid = valid && !this.invalidHandleIds[node.id];
18996
18997         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18998             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18999         }
19000
19001
19002         return valid;
19003
19004     },
19005
19006     /**
19007      * Create the array of horizontal tick marks if an interval was specified
19008      * in setXConstraint().
19009      * @method setXTicks
19010      * @private
19011      */
19012     setXTicks: function(iStartX, iTickSize) {
19013         this.xTicks = [];
19014         this.xTickSize = iTickSize;
19015
19016         var tickMap = {};
19017
19018         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19019             if (!tickMap[i]) {
19020                 this.xTicks[this.xTicks.length] = i;
19021                 tickMap[i] = true;
19022             }
19023         }
19024
19025         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19026             if (!tickMap[i]) {
19027                 this.xTicks[this.xTicks.length] = i;
19028                 tickMap[i] = true;
19029             }
19030         }
19031
19032         this.xTicks.sort(this.DDM.numericSort) ;
19033     },
19034
19035     /**
19036      * Create the array of vertical tick marks if an interval was specified in
19037      * setYConstraint().
19038      * @method setYTicks
19039      * @private
19040      */
19041     setYTicks: function(iStartY, iTickSize) {
19042         this.yTicks = [];
19043         this.yTickSize = iTickSize;
19044
19045         var tickMap = {};
19046
19047         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19048             if (!tickMap[i]) {
19049                 this.yTicks[this.yTicks.length] = i;
19050                 tickMap[i] = true;
19051             }
19052         }
19053
19054         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19055             if (!tickMap[i]) {
19056                 this.yTicks[this.yTicks.length] = i;
19057                 tickMap[i] = true;
19058             }
19059         }
19060
19061         this.yTicks.sort(this.DDM.numericSort) ;
19062     },
19063
19064     /**
19065      * By default, the element can be dragged any place on the screen.  Use
19066      * this method to limit the horizontal travel of the element.  Pass in
19067      * 0,0 for the parameters if you want to lock the drag to the y axis.
19068      * @method setXConstraint
19069      * @param {int} iLeft the number of pixels the element can move to the left
19070      * @param {int} iRight the number of pixels the element can move to the
19071      * right
19072      * @param {int} iTickSize optional parameter for specifying that the
19073      * element
19074      * should move iTickSize pixels at a time.
19075      */
19076     setXConstraint: function(iLeft, iRight, iTickSize) {
19077         this.leftConstraint = iLeft;
19078         this.rightConstraint = iRight;
19079
19080         this.minX = this.initPageX - iLeft;
19081         this.maxX = this.initPageX + iRight;
19082         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19083
19084         this.constrainX = true;
19085     },
19086
19087     /**
19088      * Clears any constraints applied to this instance.  Also clears ticks
19089      * since they can't exist independent of a constraint at this time.
19090      * @method clearConstraints
19091      */
19092     clearConstraints: function() {
19093         this.constrainX = false;
19094         this.constrainY = false;
19095         this.clearTicks();
19096     },
19097
19098     /**
19099      * Clears any tick interval defined for this instance
19100      * @method clearTicks
19101      */
19102     clearTicks: function() {
19103         this.xTicks = null;
19104         this.yTicks = null;
19105         this.xTickSize = 0;
19106         this.yTickSize = 0;
19107     },
19108
19109     /**
19110      * By default, the element can be dragged any place on the screen.  Set
19111      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19112      * parameters if you want to lock the drag to the x axis.
19113      * @method setYConstraint
19114      * @param {int} iUp the number of pixels the element can move up
19115      * @param {int} iDown the number of pixels the element can move down
19116      * @param {int} iTickSize optional parameter for specifying that the
19117      * element should move iTickSize pixels at a time.
19118      */
19119     setYConstraint: function(iUp, iDown, iTickSize) {
19120         this.topConstraint = iUp;
19121         this.bottomConstraint = iDown;
19122
19123         this.minY = this.initPageY - iUp;
19124         this.maxY = this.initPageY + iDown;
19125         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19126
19127         this.constrainY = true;
19128
19129     },
19130
19131     /**
19132      * resetConstraints must be called if you manually reposition a dd element.
19133      * @method resetConstraints
19134      * @param {boolean} maintainOffset
19135      */
19136     resetConstraints: function() {
19137
19138
19139         // Maintain offsets if necessary
19140         if (this.initPageX || this.initPageX === 0) {
19141             // figure out how much this thing has moved
19142             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19143             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19144
19145             this.setInitPosition(dx, dy);
19146
19147         // This is the first time we have detected the element's position
19148         } else {
19149             this.setInitPosition();
19150         }
19151
19152         if (this.constrainX) {
19153             this.setXConstraint( this.leftConstraint,
19154                                  this.rightConstraint,
19155                                  this.xTickSize        );
19156         }
19157
19158         if (this.constrainY) {
19159             this.setYConstraint( this.topConstraint,
19160                                  this.bottomConstraint,
19161                                  this.yTickSize         );
19162         }
19163     },
19164
19165     /**
19166      * Normally the drag element is moved pixel by pixel, but we can specify
19167      * that it move a number of pixels at a time.  This method resolves the
19168      * location when we have it set up like this.
19169      * @method getTick
19170      * @param {int} val where we want to place the object
19171      * @param {int[]} tickArray sorted array of valid points
19172      * @return {int} the closest tick
19173      * @private
19174      */
19175     getTick: function(val, tickArray) {
19176
19177         if (!tickArray) {
19178             // If tick interval is not defined, it is effectively 1 pixel,
19179             // so we return the value passed to us.
19180             return val;
19181         } else if (tickArray[0] >= val) {
19182             // The value is lower than the first tick, so we return the first
19183             // tick.
19184             return tickArray[0];
19185         } else {
19186             for (var i=0, len=tickArray.length; i<len; ++i) {
19187                 var next = i + 1;
19188                 if (tickArray[next] && tickArray[next] >= val) {
19189                     var diff1 = val - tickArray[i];
19190                     var diff2 = tickArray[next] - val;
19191                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19192                 }
19193             }
19194
19195             // The value is larger than the last tick, so we return the last
19196             // tick.
19197             return tickArray[tickArray.length - 1];
19198         }
19199     },
19200
19201     /**
19202      * toString method
19203      * @method toString
19204      * @return {string} string representation of the dd obj
19205      */
19206     toString: function() {
19207         return ("DragDrop " + this.id);
19208     }
19209
19210 });
19211
19212 })();
19213 /*
19214  * Based on:
19215  * Ext JS Library 1.1.1
19216  * Copyright(c) 2006-2007, Ext JS, LLC.
19217  *
19218  * Originally Released Under LGPL - original licence link has changed is not relivant.
19219  *
19220  * Fork - LGPL
19221  * <script type="text/javascript">
19222  */
19223
19224
19225 /**
19226  * The drag and drop utility provides a framework for building drag and drop
19227  * applications.  In addition to enabling drag and drop for specific elements,
19228  * the drag and drop elements are tracked by the manager class, and the
19229  * interactions between the various elements are tracked during the drag and
19230  * the implementing code is notified about these important moments.
19231  */
19232
19233 // Only load the library once.  Rewriting the manager class would orphan
19234 // existing drag and drop instances.
19235 if (!Roo.dd.DragDropMgr) {
19236
19237 /**
19238  * @class Roo.dd.DragDropMgr
19239  * DragDropMgr is a singleton that tracks the element interaction for
19240  * all DragDrop items in the window.  Generally, you will not call
19241  * this class directly, but it does have helper methods that could
19242  * be useful in your DragDrop implementations.
19243  * @singleton
19244  */
19245 Roo.dd.DragDropMgr = function() {
19246
19247     var Event = Roo.EventManager;
19248
19249     return {
19250
19251         /**
19252          * Two dimensional Array of registered DragDrop objects.  The first
19253          * dimension is the DragDrop item group, the second the DragDrop
19254          * object.
19255          * @property ids
19256          * @type {string: string}
19257          * @private
19258          * @static
19259          */
19260         ids: {},
19261
19262         /**
19263          * Array of element ids defined as drag handles.  Used to determine
19264          * if the element that generated the mousedown event is actually the
19265          * handle and not the html element itself.
19266          * @property handleIds
19267          * @type {string: string}
19268          * @private
19269          * @static
19270          */
19271         handleIds: {},
19272
19273         /**
19274          * the DragDrop object that is currently being dragged
19275          * @property dragCurrent
19276          * @type DragDrop
19277          * @private
19278          * @static
19279          **/
19280         dragCurrent: null,
19281
19282         /**
19283          * the DragDrop object(s) that are being hovered over
19284          * @property dragOvers
19285          * @type Array
19286          * @private
19287          * @static
19288          */
19289         dragOvers: {},
19290
19291         /**
19292          * the X distance between the cursor and the object being dragged
19293          * @property deltaX
19294          * @type int
19295          * @private
19296          * @static
19297          */
19298         deltaX: 0,
19299
19300         /**
19301          * the Y distance between the cursor and the object being dragged
19302          * @property deltaY
19303          * @type int
19304          * @private
19305          * @static
19306          */
19307         deltaY: 0,
19308
19309         /**
19310          * Flag to determine if we should prevent the default behavior of the
19311          * events we define. By default this is true, but this can be set to
19312          * false if you need the default behavior (not recommended)
19313          * @property preventDefault
19314          * @type boolean
19315          * @static
19316          */
19317         preventDefault: true,
19318
19319         /**
19320          * Flag to determine if we should stop the propagation of the events
19321          * we generate. This is true by default but you may want to set it to
19322          * false if the html element contains other features that require the
19323          * mouse click.
19324          * @property stopPropagation
19325          * @type boolean
19326          * @static
19327          */
19328         stopPropagation: true,
19329
19330         /**
19331          * Internal flag that is set to true when drag and drop has been
19332          * intialized
19333          * @property initialized
19334          * @private
19335          * @static
19336          */
19337         initalized: false,
19338
19339         /**
19340          * All drag and drop can be disabled.
19341          * @property locked
19342          * @private
19343          * @static
19344          */
19345         locked: false,
19346
19347         /**
19348          * Called the first time an element is registered.
19349          * @method init
19350          * @private
19351          * @static
19352          */
19353         init: function() {
19354             this.initialized = true;
19355         },
19356
19357         /**
19358          * In point mode, drag and drop interaction is defined by the
19359          * location of the cursor during the drag/drop
19360          * @property POINT
19361          * @type int
19362          * @static
19363          */
19364         POINT: 0,
19365
19366         /**
19367          * In intersect mode, drag and drop interactio nis defined by the
19368          * overlap of two or more drag and drop objects.
19369          * @property INTERSECT
19370          * @type int
19371          * @static
19372          */
19373         INTERSECT: 1,
19374
19375         /**
19376          * The current drag and drop mode.  Default: POINT
19377          * @property mode
19378          * @type int
19379          * @static
19380          */
19381         mode: 0,
19382
19383         /**
19384          * Runs method on all drag and drop objects
19385          * @method _execOnAll
19386          * @private
19387          * @static
19388          */
19389         _execOnAll: function(sMethod, args) {
19390             for (var i in this.ids) {
19391                 for (var j in this.ids[i]) {
19392                     var oDD = this.ids[i][j];
19393                     if (! this.isTypeOfDD(oDD)) {
19394                         continue;
19395                     }
19396                     oDD[sMethod].apply(oDD, args);
19397                 }
19398             }
19399         },
19400
19401         /**
19402          * Drag and drop initialization.  Sets up the global event handlers
19403          * @method _onLoad
19404          * @private
19405          * @static
19406          */
19407         _onLoad: function() {
19408
19409             this.init();
19410
19411             if (!Roo.isTouch) {
19412                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19413                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19414             }
19415             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19416             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19417             
19418             Event.on(window,   "unload",    this._onUnload, this, true);
19419             Event.on(window,   "resize",    this._onResize, this, true);
19420             // Event.on(window,   "mouseout",    this._test);
19421
19422         },
19423
19424         /**
19425          * Reset constraints on all drag and drop objs
19426          * @method _onResize
19427          * @private
19428          * @static
19429          */
19430         _onResize: function(e) {
19431             this._execOnAll("resetConstraints", []);
19432         },
19433
19434         /**
19435          * Lock all drag and drop functionality
19436          * @method lock
19437          * @static
19438          */
19439         lock: function() { this.locked = true; },
19440
19441         /**
19442          * Unlock all drag and drop functionality
19443          * @method unlock
19444          * @static
19445          */
19446         unlock: function() { this.locked = false; },
19447
19448         /**
19449          * Is drag and drop locked?
19450          * @method isLocked
19451          * @return {boolean} True if drag and drop is locked, false otherwise.
19452          * @static
19453          */
19454         isLocked: function() { return this.locked; },
19455
19456         /**
19457          * Location cache that is set for all drag drop objects when a drag is
19458          * initiated, cleared when the drag is finished.
19459          * @property locationCache
19460          * @private
19461          * @static
19462          */
19463         locationCache: {},
19464
19465         /**
19466          * Set useCache to false if you want to force object the lookup of each
19467          * drag and drop linked element constantly during a drag.
19468          * @property useCache
19469          * @type boolean
19470          * @static
19471          */
19472         useCache: true,
19473
19474         /**
19475          * The number of pixels that the mouse needs to move after the
19476          * mousedown before the drag is initiated.  Default=3;
19477          * @property clickPixelThresh
19478          * @type int
19479          * @static
19480          */
19481         clickPixelThresh: 3,
19482
19483         /**
19484          * The number of milliseconds after the mousedown event to initiate the
19485          * drag if we don't get a mouseup event. Default=1000
19486          * @property clickTimeThresh
19487          * @type int
19488          * @static
19489          */
19490         clickTimeThresh: 350,
19491
19492         /**
19493          * Flag that indicates that either the drag pixel threshold or the
19494          * mousdown time threshold has been met
19495          * @property dragThreshMet
19496          * @type boolean
19497          * @private
19498          * @static
19499          */
19500         dragThreshMet: false,
19501
19502         /**
19503          * Timeout used for the click time threshold
19504          * @property clickTimeout
19505          * @type Object
19506          * @private
19507          * @static
19508          */
19509         clickTimeout: null,
19510
19511         /**
19512          * The X position of the mousedown event stored for later use when a
19513          * drag threshold is met.
19514          * @property startX
19515          * @type int
19516          * @private
19517          * @static
19518          */
19519         startX: 0,
19520
19521         /**
19522          * The Y position of the mousedown event stored for later use when a
19523          * drag threshold is met.
19524          * @property startY
19525          * @type int
19526          * @private
19527          * @static
19528          */
19529         startY: 0,
19530
19531         /**
19532          * Each DragDrop instance must be registered with the DragDropMgr.
19533          * This is executed in DragDrop.init()
19534          * @method regDragDrop
19535          * @param {DragDrop} oDD the DragDrop object to register
19536          * @param {String} sGroup the name of the group this element belongs to
19537          * @static
19538          */
19539         regDragDrop: function(oDD, sGroup) {
19540             if (!this.initialized) { this.init(); }
19541
19542             if (!this.ids[sGroup]) {
19543                 this.ids[sGroup] = {};
19544             }
19545             this.ids[sGroup][oDD.id] = oDD;
19546         },
19547
19548         /**
19549          * Removes the supplied dd instance from the supplied group. Executed
19550          * by DragDrop.removeFromGroup, so don't call this function directly.
19551          * @method removeDDFromGroup
19552          * @private
19553          * @static
19554          */
19555         removeDDFromGroup: function(oDD, sGroup) {
19556             if (!this.ids[sGroup]) {
19557                 this.ids[sGroup] = {};
19558             }
19559
19560             var obj = this.ids[sGroup];
19561             if (obj && obj[oDD.id]) {
19562                 delete obj[oDD.id];
19563             }
19564         },
19565
19566         /**
19567          * Unregisters a drag and drop item.  This is executed in
19568          * DragDrop.unreg, use that method instead of calling this directly.
19569          * @method _remove
19570          * @private
19571          * @static
19572          */
19573         _remove: function(oDD) {
19574             for (var g in oDD.groups) {
19575                 if (g && this.ids[g][oDD.id]) {
19576                     delete this.ids[g][oDD.id];
19577                 }
19578             }
19579             delete this.handleIds[oDD.id];
19580         },
19581
19582         /**
19583          * Each DragDrop handle element must be registered.  This is done
19584          * automatically when executing DragDrop.setHandleElId()
19585          * @method regHandle
19586          * @param {String} sDDId the DragDrop id this element is a handle for
19587          * @param {String} sHandleId the id of the element that is the drag
19588          * handle
19589          * @static
19590          */
19591         regHandle: function(sDDId, sHandleId) {
19592             if (!this.handleIds[sDDId]) {
19593                 this.handleIds[sDDId] = {};
19594             }
19595             this.handleIds[sDDId][sHandleId] = sHandleId;
19596         },
19597
19598         /**
19599          * Utility function to determine if a given element has been
19600          * registered as a drag drop item.
19601          * @method isDragDrop
19602          * @param {String} id the element id to check
19603          * @return {boolean} true if this element is a DragDrop item,
19604          * false otherwise
19605          * @static
19606          */
19607         isDragDrop: function(id) {
19608             return ( this.getDDById(id) ) ? true : false;
19609         },
19610
19611         /**
19612          * Returns the drag and drop instances that are in all groups the
19613          * passed in instance belongs to.
19614          * @method getRelated
19615          * @param {DragDrop} p_oDD the obj to get related data for
19616          * @param {boolean} bTargetsOnly if true, only return targetable objs
19617          * @return {DragDrop[]} the related instances
19618          * @static
19619          */
19620         getRelated: function(p_oDD, bTargetsOnly) {
19621             var oDDs = [];
19622             for (var i in p_oDD.groups) {
19623                 for (j in this.ids[i]) {
19624                     var dd = this.ids[i][j];
19625                     if (! this.isTypeOfDD(dd)) {
19626                         continue;
19627                     }
19628                     if (!bTargetsOnly || dd.isTarget) {
19629                         oDDs[oDDs.length] = dd;
19630                     }
19631                 }
19632             }
19633
19634             return oDDs;
19635         },
19636
19637         /**
19638          * Returns true if the specified dd target is a legal target for
19639          * the specifice drag obj
19640          * @method isLegalTarget
19641          * @param {DragDrop} the drag obj
19642          * @param {DragDrop} the target
19643          * @return {boolean} true if the target is a legal target for the
19644          * dd obj
19645          * @static
19646          */
19647         isLegalTarget: function (oDD, oTargetDD) {
19648             var targets = this.getRelated(oDD, true);
19649             for (var i=0, len=targets.length;i<len;++i) {
19650                 if (targets[i].id == oTargetDD.id) {
19651                     return true;
19652                 }
19653             }
19654
19655             return false;
19656         },
19657
19658         /**
19659          * My goal is to be able to transparently determine if an object is
19660          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19661          * returns "object", oDD.constructor.toString() always returns
19662          * "DragDrop" and not the name of the subclass.  So for now it just
19663          * evaluates a well-known variable in DragDrop.
19664          * @method isTypeOfDD
19665          * @param {Object} the object to evaluate
19666          * @return {boolean} true if typeof oDD = DragDrop
19667          * @static
19668          */
19669         isTypeOfDD: function (oDD) {
19670             return (oDD && oDD.__ygDragDrop);
19671         },
19672
19673         /**
19674          * Utility function to determine if a given element has been
19675          * registered as a drag drop handle for the given Drag Drop object.
19676          * @method isHandle
19677          * @param {String} id the element id to check
19678          * @return {boolean} true if this element is a DragDrop handle, false
19679          * otherwise
19680          * @static
19681          */
19682         isHandle: function(sDDId, sHandleId) {
19683             return ( this.handleIds[sDDId] &&
19684                             this.handleIds[sDDId][sHandleId] );
19685         },
19686
19687         /**
19688          * Returns the DragDrop instance for a given id
19689          * @method getDDById
19690          * @param {String} id the id of the DragDrop object
19691          * @return {DragDrop} the drag drop object, null if it is not found
19692          * @static
19693          */
19694         getDDById: function(id) {
19695             for (var i in this.ids) {
19696                 if (this.ids[i][id]) {
19697                     return this.ids[i][id];
19698                 }
19699             }
19700             return null;
19701         },
19702
19703         /**
19704          * Fired after a registered DragDrop object gets the mousedown event.
19705          * Sets up the events required to track the object being dragged
19706          * @method handleMouseDown
19707          * @param {Event} e the event
19708          * @param oDD the DragDrop object being dragged
19709          * @private
19710          * @static
19711          */
19712         handleMouseDown: function(e, oDD) {
19713             if(Roo.QuickTips){
19714                 Roo.QuickTips.disable();
19715             }
19716             this.currentTarget = e.getTarget();
19717
19718             this.dragCurrent = oDD;
19719
19720             var el = oDD.getEl();
19721
19722             // track start position
19723             this.startX = e.getPageX();
19724             this.startY = e.getPageY();
19725
19726             this.deltaX = this.startX - el.offsetLeft;
19727             this.deltaY = this.startY - el.offsetTop;
19728
19729             this.dragThreshMet = false;
19730
19731             this.clickTimeout = setTimeout(
19732                     function() {
19733                         var DDM = Roo.dd.DDM;
19734                         DDM.startDrag(DDM.startX, DDM.startY);
19735                     },
19736                     this.clickTimeThresh );
19737         },
19738
19739         /**
19740          * Fired when either the drag pixel threshol or the mousedown hold
19741          * time threshold has been met.
19742          * @method startDrag
19743          * @param x {int} the X position of the original mousedown
19744          * @param y {int} the Y position of the original mousedown
19745          * @static
19746          */
19747         startDrag: function(x, y) {
19748             clearTimeout(this.clickTimeout);
19749             if (this.dragCurrent) {
19750                 this.dragCurrent.b4StartDrag(x, y);
19751                 this.dragCurrent.startDrag(x, y);
19752             }
19753             this.dragThreshMet = true;
19754         },
19755
19756         /**
19757          * Internal function to handle the mouseup event.  Will be invoked
19758          * from the context of the document.
19759          * @method handleMouseUp
19760          * @param {Event} e the event
19761          * @private
19762          * @static
19763          */
19764         handleMouseUp: function(e) {
19765
19766             if(Roo.QuickTips){
19767                 Roo.QuickTips.enable();
19768             }
19769             if (! this.dragCurrent) {
19770                 return;
19771             }
19772
19773             clearTimeout(this.clickTimeout);
19774
19775             if (this.dragThreshMet) {
19776                 this.fireEvents(e, true);
19777             } else {
19778             }
19779
19780             this.stopDrag(e);
19781
19782             this.stopEvent(e);
19783         },
19784
19785         /**
19786          * Utility to stop event propagation and event default, if these
19787          * features are turned on.
19788          * @method stopEvent
19789          * @param {Event} e the event as returned by this.getEvent()
19790          * @static
19791          */
19792         stopEvent: function(e){
19793             if(this.stopPropagation) {
19794                 e.stopPropagation();
19795             }
19796
19797             if (this.preventDefault) {
19798                 e.preventDefault();
19799             }
19800         },
19801
19802         /**
19803          * Internal function to clean up event handlers after the drag
19804          * operation is complete
19805          * @method stopDrag
19806          * @param {Event} e the event
19807          * @private
19808          * @static
19809          */
19810         stopDrag: function(e) {
19811             // Fire the drag end event for the item that was dragged
19812             if (this.dragCurrent) {
19813                 if (this.dragThreshMet) {
19814                     this.dragCurrent.b4EndDrag(e);
19815                     this.dragCurrent.endDrag(e);
19816                 }
19817
19818                 this.dragCurrent.onMouseUp(e);
19819             }
19820
19821             this.dragCurrent = null;
19822             this.dragOvers = {};
19823         },
19824
19825         /**
19826          * Internal function to handle the mousemove event.  Will be invoked
19827          * from the context of the html element.
19828          *
19829          * @TODO figure out what we can do about mouse events lost when the
19830          * user drags objects beyond the window boundary.  Currently we can
19831          * detect this in internet explorer by verifying that the mouse is
19832          * down during the mousemove event.  Firefox doesn't give us the
19833          * button state on the mousemove event.
19834          * @method handleMouseMove
19835          * @param {Event} e the event
19836          * @private
19837          * @static
19838          */
19839         handleMouseMove: function(e) {
19840             if (! this.dragCurrent) {
19841                 return true;
19842             }
19843
19844             // var button = e.which || e.button;
19845
19846             // check for IE mouseup outside of page boundary
19847             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19848                 this.stopEvent(e);
19849                 return this.handleMouseUp(e);
19850             }
19851
19852             if (!this.dragThreshMet) {
19853                 var diffX = Math.abs(this.startX - e.getPageX());
19854                 var diffY = Math.abs(this.startY - e.getPageY());
19855                 if (diffX > this.clickPixelThresh ||
19856                             diffY > this.clickPixelThresh) {
19857                     this.startDrag(this.startX, this.startY);
19858                 }
19859             }
19860
19861             if (this.dragThreshMet) {
19862                 this.dragCurrent.b4Drag(e);
19863                 this.dragCurrent.onDrag(e);
19864                 if(!this.dragCurrent.moveOnly){
19865                     this.fireEvents(e, false);
19866                 }
19867             }
19868
19869             this.stopEvent(e);
19870
19871             return true;
19872         },
19873
19874         /**
19875          * Iterates over all of the DragDrop elements to find ones we are
19876          * hovering over or dropping on
19877          * @method fireEvents
19878          * @param {Event} e the event
19879          * @param {boolean} isDrop is this a drop op or a mouseover op?
19880          * @private
19881          * @static
19882          */
19883         fireEvents: function(e, isDrop) {
19884             var dc = this.dragCurrent;
19885
19886             // If the user did the mouse up outside of the window, we could
19887             // get here even though we have ended the drag.
19888             if (!dc || dc.isLocked()) {
19889                 return;
19890             }
19891
19892             var pt = e.getPoint();
19893
19894             // cache the previous dragOver array
19895             var oldOvers = [];
19896
19897             var outEvts   = [];
19898             var overEvts  = [];
19899             var dropEvts  = [];
19900             var enterEvts = [];
19901
19902             // Check to see if the object(s) we were hovering over is no longer
19903             // being hovered over so we can fire the onDragOut event
19904             for (var i in this.dragOvers) {
19905
19906                 var ddo = this.dragOvers[i];
19907
19908                 if (! this.isTypeOfDD(ddo)) {
19909                     continue;
19910                 }
19911
19912                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19913                     outEvts.push( ddo );
19914                 }
19915
19916                 oldOvers[i] = true;
19917                 delete this.dragOvers[i];
19918             }
19919
19920             for (var sGroup in dc.groups) {
19921
19922                 if ("string" != typeof sGroup) {
19923                     continue;
19924                 }
19925
19926                 for (i in this.ids[sGroup]) {
19927                     var oDD = this.ids[sGroup][i];
19928                     if (! this.isTypeOfDD(oDD)) {
19929                         continue;
19930                     }
19931
19932                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19933                         if (this.isOverTarget(pt, oDD, this.mode)) {
19934                             // look for drop interactions
19935                             if (isDrop) {
19936                                 dropEvts.push( oDD );
19937                             // look for drag enter and drag over interactions
19938                             } else {
19939
19940                                 // initial drag over: dragEnter fires
19941                                 if (!oldOvers[oDD.id]) {
19942                                     enterEvts.push( oDD );
19943                                 // subsequent drag overs: dragOver fires
19944                                 } else {
19945                                     overEvts.push( oDD );
19946                                 }
19947
19948                                 this.dragOvers[oDD.id] = oDD;
19949                             }
19950                         }
19951                     }
19952                 }
19953             }
19954
19955             if (this.mode) {
19956                 if (outEvts.length) {
19957                     dc.b4DragOut(e, outEvts);
19958                     dc.onDragOut(e, outEvts);
19959                 }
19960
19961                 if (enterEvts.length) {
19962                     dc.onDragEnter(e, enterEvts);
19963                 }
19964
19965                 if (overEvts.length) {
19966                     dc.b4DragOver(e, overEvts);
19967                     dc.onDragOver(e, overEvts);
19968                 }
19969
19970                 if (dropEvts.length) {
19971                     dc.b4DragDrop(e, dropEvts);
19972                     dc.onDragDrop(e, dropEvts);
19973                 }
19974
19975             } else {
19976                 // fire dragout events
19977                 var len = 0;
19978                 for (i=0, len=outEvts.length; i<len; ++i) {
19979                     dc.b4DragOut(e, outEvts[i].id);
19980                     dc.onDragOut(e, outEvts[i].id);
19981                 }
19982
19983                 // fire enter events
19984                 for (i=0,len=enterEvts.length; i<len; ++i) {
19985                     // dc.b4DragEnter(e, oDD.id);
19986                     dc.onDragEnter(e, enterEvts[i].id);
19987                 }
19988
19989                 // fire over events
19990                 for (i=0,len=overEvts.length; i<len; ++i) {
19991                     dc.b4DragOver(e, overEvts[i].id);
19992                     dc.onDragOver(e, overEvts[i].id);
19993                 }
19994
19995                 // fire drop events
19996                 for (i=0, len=dropEvts.length; i<len; ++i) {
19997                     dc.b4DragDrop(e, dropEvts[i].id);
19998                     dc.onDragDrop(e, dropEvts[i].id);
19999                 }
20000
20001             }
20002
20003             // notify about a drop that did not find a target
20004             if (isDrop && !dropEvts.length) {
20005                 dc.onInvalidDrop(e);
20006             }
20007
20008         },
20009
20010         /**
20011          * Helper function for getting the best match from the list of drag
20012          * and drop objects returned by the drag and drop events when we are
20013          * in INTERSECT mode.  It returns either the first object that the
20014          * cursor is over, or the object that has the greatest overlap with
20015          * the dragged element.
20016          * @method getBestMatch
20017          * @param  {DragDrop[]} dds The array of drag and drop objects
20018          * targeted
20019          * @return {DragDrop}       The best single match
20020          * @static
20021          */
20022         getBestMatch: function(dds) {
20023             var winner = null;
20024             // Return null if the input is not what we expect
20025             //if (!dds || !dds.length || dds.length == 0) {
20026                // winner = null;
20027             // If there is only one item, it wins
20028             //} else if (dds.length == 1) {
20029
20030             var len = dds.length;
20031
20032             if (len == 1) {
20033                 winner = dds[0];
20034             } else {
20035                 // Loop through the targeted items
20036                 for (var i=0; i<len; ++i) {
20037                     var dd = dds[i];
20038                     // If the cursor is over the object, it wins.  If the
20039                     // cursor is over multiple matches, the first one we come
20040                     // to wins.
20041                     if (dd.cursorIsOver) {
20042                         winner = dd;
20043                         break;
20044                     // Otherwise the object with the most overlap wins
20045                     } else {
20046                         if (!winner ||
20047                             winner.overlap.getArea() < dd.overlap.getArea()) {
20048                             winner = dd;
20049                         }
20050                     }
20051                 }
20052             }
20053
20054             return winner;
20055         },
20056
20057         /**
20058          * Refreshes the cache of the top-left and bottom-right points of the
20059          * drag and drop objects in the specified group(s).  This is in the
20060          * format that is stored in the drag and drop instance, so typical
20061          * usage is:
20062          * <code>
20063          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20064          * </code>
20065          * Alternatively:
20066          * <code>
20067          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20068          * </code>
20069          * @TODO this really should be an indexed array.  Alternatively this
20070          * method could accept both.
20071          * @method refreshCache
20072          * @param {Object} groups an associative array of groups to refresh
20073          * @static
20074          */
20075         refreshCache: function(groups) {
20076             for (var sGroup in groups) {
20077                 if ("string" != typeof sGroup) {
20078                     continue;
20079                 }
20080                 for (var i in this.ids[sGroup]) {
20081                     var oDD = this.ids[sGroup][i];
20082
20083                     if (this.isTypeOfDD(oDD)) {
20084                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20085                         var loc = this.getLocation(oDD);
20086                         if (loc) {
20087                             this.locationCache[oDD.id] = loc;
20088                         } else {
20089                             delete this.locationCache[oDD.id];
20090                             // this will unregister the drag and drop object if
20091                             // the element is not in a usable state
20092                             // oDD.unreg();
20093                         }
20094                     }
20095                 }
20096             }
20097         },
20098
20099         /**
20100          * This checks to make sure an element exists and is in the DOM.  The
20101          * main purpose is to handle cases where innerHTML is used to remove
20102          * drag and drop objects from the DOM.  IE provides an 'unspecified
20103          * error' when trying to access the offsetParent of such an element
20104          * @method verifyEl
20105          * @param {HTMLElement} el the element to check
20106          * @return {boolean} true if the element looks usable
20107          * @static
20108          */
20109         verifyEl: function(el) {
20110             if (el) {
20111                 var parent;
20112                 if(Roo.isIE){
20113                     try{
20114                         parent = el.offsetParent;
20115                     }catch(e){}
20116                 }else{
20117                     parent = el.offsetParent;
20118                 }
20119                 if (parent) {
20120                     return true;
20121                 }
20122             }
20123
20124             return false;
20125         },
20126
20127         /**
20128          * Returns a Region object containing the drag and drop element's position
20129          * and size, including the padding configured for it
20130          * @method getLocation
20131          * @param {DragDrop} oDD the drag and drop object to get the
20132          *                       location for
20133          * @return {Roo.lib.Region} a Region object representing the total area
20134          *                             the element occupies, including any padding
20135          *                             the instance is configured for.
20136          * @static
20137          */
20138         getLocation: function(oDD) {
20139             if (! this.isTypeOfDD(oDD)) {
20140                 return null;
20141             }
20142
20143             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20144
20145             try {
20146                 pos= Roo.lib.Dom.getXY(el);
20147             } catch (e) { }
20148
20149             if (!pos) {
20150                 return null;
20151             }
20152
20153             x1 = pos[0];
20154             x2 = x1 + el.offsetWidth;
20155             y1 = pos[1];
20156             y2 = y1 + el.offsetHeight;
20157
20158             t = y1 - oDD.padding[0];
20159             r = x2 + oDD.padding[1];
20160             b = y2 + oDD.padding[2];
20161             l = x1 - oDD.padding[3];
20162
20163             return new Roo.lib.Region( t, r, b, l );
20164         },
20165
20166         /**
20167          * Checks the cursor location to see if it over the target
20168          * @method isOverTarget
20169          * @param {Roo.lib.Point} pt The point to evaluate
20170          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20171          * @return {boolean} true if the mouse is over the target
20172          * @private
20173          * @static
20174          */
20175         isOverTarget: function(pt, oTarget, intersect) {
20176             // use cache if available
20177             var loc = this.locationCache[oTarget.id];
20178             if (!loc || !this.useCache) {
20179                 loc = this.getLocation(oTarget);
20180                 this.locationCache[oTarget.id] = loc;
20181
20182             }
20183
20184             if (!loc) {
20185                 return false;
20186             }
20187
20188             oTarget.cursorIsOver = loc.contains( pt );
20189
20190             // DragDrop is using this as a sanity check for the initial mousedown
20191             // in this case we are done.  In POINT mode, if the drag obj has no
20192             // contraints, we are also done. Otherwise we need to evaluate the
20193             // location of the target as related to the actual location of the
20194             // dragged element.
20195             var dc = this.dragCurrent;
20196             if (!dc || !dc.getTargetCoord ||
20197                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20198                 return oTarget.cursorIsOver;
20199             }
20200
20201             oTarget.overlap = null;
20202
20203             // Get the current location of the drag element, this is the
20204             // location of the mouse event less the delta that represents
20205             // where the original mousedown happened on the element.  We
20206             // need to consider constraints and ticks as well.
20207             var pos = dc.getTargetCoord(pt.x, pt.y);
20208
20209             var el = dc.getDragEl();
20210             var curRegion = new Roo.lib.Region( pos.y,
20211                                                    pos.x + el.offsetWidth,
20212                                                    pos.y + el.offsetHeight,
20213                                                    pos.x );
20214
20215             var overlap = curRegion.intersect(loc);
20216
20217             if (overlap) {
20218                 oTarget.overlap = overlap;
20219                 return (intersect) ? true : oTarget.cursorIsOver;
20220             } else {
20221                 return false;
20222             }
20223         },
20224
20225         /**
20226          * unload event handler
20227          * @method _onUnload
20228          * @private
20229          * @static
20230          */
20231         _onUnload: function(e, me) {
20232             Roo.dd.DragDropMgr.unregAll();
20233         },
20234
20235         /**
20236          * Cleans up the drag and drop events and objects.
20237          * @method unregAll
20238          * @private
20239          * @static
20240          */
20241         unregAll: function() {
20242
20243             if (this.dragCurrent) {
20244                 this.stopDrag();
20245                 this.dragCurrent = null;
20246             }
20247
20248             this._execOnAll("unreg", []);
20249
20250             for (i in this.elementCache) {
20251                 delete this.elementCache[i];
20252             }
20253
20254             this.elementCache = {};
20255             this.ids = {};
20256         },
20257
20258         /**
20259          * A cache of DOM elements
20260          * @property elementCache
20261          * @private
20262          * @static
20263          */
20264         elementCache: {},
20265
20266         /**
20267          * Get the wrapper for the DOM element specified
20268          * @method getElWrapper
20269          * @param {String} id the id of the element to get
20270          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20271          * @private
20272          * @deprecated This wrapper isn't that useful
20273          * @static
20274          */
20275         getElWrapper: function(id) {
20276             var oWrapper = this.elementCache[id];
20277             if (!oWrapper || !oWrapper.el) {
20278                 oWrapper = this.elementCache[id] =
20279                     new this.ElementWrapper(Roo.getDom(id));
20280             }
20281             return oWrapper;
20282         },
20283
20284         /**
20285          * Returns the actual DOM element
20286          * @method getElement
20287          * @param {String} id the id of the elment to get
20288          * @return {Object} The element
20289          * @deprecated use Roo.getDom instead
20290          * @static
20291          */
20292         getElement: function(id) {
20293             return Roo.getDom(id);
20294         },
20295
20296         /**
20297          * Returns the style property for the DOM element (i.e.,
20298          * document.getElById(id).style)
20299          * @method getCss
20300          * @param {String} id the id of the elment to get
20301          * @return {Object} The style property of the element
20302          * @deprecated use Roo.getDom instead
20303          * @static
20304          */
20305         getCss: function(id) {
20306             var el = Roo.getDom(id);
20307             return (el) ? el.style : null;
20308         },
20309
20310         /**
20311          * Inner class for cached elements
20312          * @class DragDropMgr.ElementWrapper
20313          * @for DragDropMgr
20314          * @private
20315          * @deprecated
20316          */
20317         ElementWrapper: function(el) {
20318                 /**
20319                  * The element
20320                  * @property el
20321                  */
20322                 this.el = el || null;
20323                 /**
20324                  * The element id
20325                  * @property id
20326                  */
20327                 this.id = this.el && el.id;
20328                 /**
20329                  * A reference to the style property
20330                  * @property css
20331                  */
20332                 this.css = this.el && el.style;
20333             },
20334
20335         /**
20336          * Returns the X position of an html element
20337          * @method getPosX
20338          * @param el the element for which to get the position
20339          * @return {int} the X coordinate
20340          * @for DragDropMgr
20341          * @deprecated use Roo.lib.Dom.getX instead
20342          * @static
20343          */
20344         getPosX: function(el) {
20345             return Roo.lib.Dom.getX(el);
20346         },
20347
20348         /**
20349          * Returns the Y position of an html element
20350          * @method getPosY
20351          * @param el the element for which to get the position
20352          * @return {int} the Y coordinate
20353          * @deprecated use Roo.lib.Dom.getY instead
20354          * @static
20355          */
20356         getPosY: function(el) {
20357             return Roo.lib.Dom.getY(el);
20358         },
20359
20360         /**
20361          * Swap two nodes.  In IE, we use the native method, for others we
20362          * emulate the IE behavior
20363          * @method swapNode
20364          * @param n1 the first node to swap
20365          * @param n2 the other node to swap
20366          * @static
20367          */
20368         swapNode: function(n1, n2) {
20369             if (n1.swapNode) {
20370                 n1.swapNode(n2);
20371             } else {
20372                 var p = n2.parentNode;
20373                 var s = n2.nextSibling;
20374
20375                 if (s == n1) {
20376                     p.insertBefore(n1, n2);
20377                 } else if (n2 == n1.nextSibling) {
20378                     p.insertBefore(n2, n1);
20379                 } else {
20380                     n1.parentNode.replaceChild(n2, n1);
20381                     p.insertBefore(n1, s);
20382                 }
20383             }
20384         },
20385
20386         /**
20387          * Returns the current scroll position
20388          * @method getScroll
20389          * @private
20390          * @static
20391          */
20392         getScroll: function () {
20393             var t, l, dde=document.documentElement, db=document.body;
20394             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20395                 t = dde.scrollTop;
20396                 l = dde.scrollLeft;
20397             } else if (db) {
20398                 t = db.scrollTop;
20399                 l = db.scrollLeft;
20400             } else {
20401
20402             }
20403             return { top: t, left: l };
20404         },
20405
20406         /**
20407          * Returns the specified element style property
20408          * @method getStyle
20409          * @param {HTMLElement} el          the element
20410          * @param {string}      styleProp   the style property
20411          * @return {string} The value of the style property
20412          * @deprecated use Roo.lib.Dom.getStyle
20413          * @static
20414          */
20415         getStyle: function(el, styleProp) {
20416             return Roo.fly(el).getStyle(styleProp);
20417         },
20418
20419         /**
20420          * Gets the scrollTop
20421          * @method getScrollTop
20422          * @return {int} the document's scrollTop
20423          * @static
20424          */
20425         getScrollTop: function () { return this.getScroll().top; },
20426
20427         /**
20428          * Gets the scrollLeft
20429          * @method getScrollLeft
20430          * @return {int} the document's scrollTop
20431          * @static
20432          */
20433         getScrollLeft: function () { return this.getScroll().left; },
20434
20435         /**
20436          * Sets the x/y position of an element to the location of the
20437          * target element.
20438          * @method moveToEl
20439          * @param {HTMLElement} moveEl      The element to move
20440          * @param {HTMLElement} targetEl    The position reference element
20441          * @static
20442          */
20443         moveToEl: function (moveEl, targetEl) {
20444             var aCoord = Roo.lib.Dom.getXY(targetEl);
20445             Roo.lib.Dom.setXY(moveEl, aCoord);
20446         },
20447
20448         /**
20449          * Numeric array sort function
20450          * @method numericSort
20451          * @static
20452          */
20453         numericSort: function(a, b) { return (a - b); },
20454
20455         /**
20456          * Internal counter
20457          * @property _timeoutCount
20458          * @private
20459          * @static
20460          */
20461         _timeoutCount: 0,
20462
20463         /**
20464          * Trying to make the load order less important.  Without this we get
20465          * an error if this file is loaded before the Event Utility.
20466          * @method _addListeners
20467          * @private
20468          * @static
20469          */
20470         _addListeners: function() {
20471             var DDM = Roo.dd.DDM;
20472             if ( Roo.lib.Event && document ) {
20473                 DDM._onLoad();
20474             } else {
20475                 if (DDM._timeoutCount > 2000) {
20476                 } else {
20477                     setTimeout(DDM._addListeners, 10);
20478                     if (document && document.body) {
20479                         DDM._timeoutCount += 1;
20480                     }
20481                 }
20482             }
20483         },
20484
20485         /**
20486          * Recursively searches the immediate parent and all child nodes for
20487          * the handle element in order to determine wheter or not it was
20488          * clicked.
20489          * @method handleWasClicked
20490          * @param node the html element to inspect
20491          * @static
20492          */
20493         handleWasClicked: function(node, id) {
20494             if (this.isHandle(id, node.id)) {
20495                 return true;
20496             } else {
20497                 // check to see if this is a text node child of the one we want
20498                 var p = node.parentNode;
20499
20500                 while (p) {
20501                     if (this.isHandle(id, p.id)) {
20502                         return true;
20503                     } else {
20504                         p = p.parentNode;
20505                     }
20506                 }
20507             }
20508
20509             return false;
20510         }
20511
20512     };
20513
20514 }();
20515
20516 // shorter alias, save a few bytes
20517 Roo.dd.DDM = Roo.dd.DragDropMgr;
20518 Roo.dd.DDM._addListeners();
20519
20520 }/*
20521  * Based on:
20522  * Ext JS Library 1.1.1
20523  * Copyright(c) 2006-2007, Ext JS, LLC.
20524  *
20525  * Originally Released Under LGPL - original licence link has changed is not relivant.
20526  *
20527  * Fork - LGPL
20528  * <script type="text/javascript">
20529  */
20530
20531 /**
20532  * @class Roo.dd.DD
20533  * A DragDrop implementation where the linked element follows the
20534  * mouse cursor during a drag.
20535  * @extends Roo.dd.DragDrop
20536  * @constructor
20537  * @param {String} id the id of the linked element
20538  * @param {String} sGroup the group of related DragDrop items
20539  * @param {object} config an object containing configurable attributes
20540  *                Valid properties for DD:
20541  *                    scroll
20542  */
20543 Roo.dd.DD = function(id, sGroup, config) {
20544     if (id) {
20545         this.init(id, sGroup, config);
20546     }
20547 };
20548
20549 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20550
20551     /**
20552      * When set to true, the utility automatically tries to scroll the browser
20553      * window wehn a drag and drop element is dragged near the viewport boundary.
20554      * Defaults to true.
20555      * @property scroll
20556      * @type boolean
20557      */
20558     scroll: true,
20559
20560     /**
20561      * Sets the pointer offset to the distance between the linked element's top
20562      * left corner and the location the element was clicked
20563      * @method autoOffset
20564      * @param {int} iPageX the X coordinate of the click
20565      * @param {int} iPageY the Y coordinate of the click
20566      */
20567     autoOffset: function(iPageX, iPageY) {
20568         var x = iPageX - this.startPageX;
20569         var y = iPageY - this.startPageY;
20570         this.setDelta(x, y);
20571     },
20572
20573     /**
20574      * Sets the pointer offset.  You can call this directly to force the
20575      * offset to be in a particular location (e.g., pass in 0,0 to set it
20576      * to the center of the object)
20577      * @method setDelta
20578      * @param {int} iDeltaX the distance from the left
20579      * @param {int} iDeltaY the distance from the top
20580      */
20581     setDelta: function(iDeltaX, iDeltaY) {
20582         this.deltaX = iDeltaX;
20583         this.deltaY = iDeltaY;
20584     },
20585
20586     /**
20587      * Sets the drag element to the location of the mousedown or click event,
20588      * maintaining the cursor location relative to the location on the element
20589      * that was clicked.  Override this if you want to place the element in a
20590      * location other than where the cursor is.
20591      * @method setDragElPos
20592      * @param {int} iPageX the X coordinate of the mousedown or drag event
20593      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20594      */
20595     setDragElPos: function(iPageX, iPageY) {
20596         // the first time we do this, we are going to check to make sure
20597         // the element has css positioning
20598
20599         var el = this.getDragEl();
20600         this.alignElWithMouse(el, iPageX, iPageY);
20601     },
20602
20603     /**
20604      * Sets the element to the location of the mousedown or click event,
20605      * maintaining the cursor location relative to the location on the element
20606      * that was clicked.  Override this if you want to place the element in a
20607      * location other than where the cursor is.
20608      * @method alignElWithMouse
20609      * @param {HTMLElement} el the element to move
20610      * @param {int} iPageX the X coordinate of the mousedown or drag event
20611      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20612      */
20613     alignElWithMouse: function(el, iPageX, iPageY) {
20614         var oCoord = this.getTargetCoord(iPageX, iPageY);
20615         var fly = el.dom ? el : Roo.fly(el);
20616         if (!this.deltaSetXY) {
20617             var aCoord = [oCoord.x, oCoord.y];
20618             fly.setXY(aCoord);
20619             var newLeft = fly.getLeft(true);
20620             var newTop  = fly.getTop(true);
20621             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20622         } else {
20623             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20624         }
20625
20626         this.cachePosition(oCoord.x, oCoord.y);
20627         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20628         return oCoord;
20629     },
20630
20631     /**
20632      * Saves the most recent position so that we can reset the constraints and
20633      * tick marks on-demand.  We need to know this so that we can calculate the
20634      * number of pixels the element is offset from its original position.
20635      * @method cachePosition
20636      * @param iPageX the current x position (optional, this just makes it so we
20637      * don't have to look it up again)
20638      * @param iPageY the current y position (optional, this just makes it so we
20639      * don't have to look it up again)
20640      */
20641     cachePosition: function(iPageX, iPageY) {
20642         if (iPageX) {
20643             this.lastPageX = iPageX;
20644             this.lastPageY = iPageY;
20645         } else {
20646             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20647             this.lastPageX = aCoord[0];
20648             this.lastPageY = aCoord[1];
20649         }
20650     },
20651
20652     /**
20653      * Auto-scroll the window if the dragged object has been moved beyond the
20654      * visible window boundary.
20655      * @method autoScroll
20656      * @param {int} x the drag element's x position
20657      * @param {int} y the drag element's y position
20658      * @param {int} h the height of the drag element
20659      * @param {int} w the width of the drag element
20660      * @private
20661      */
20662     autoScroll: function(x, y, h, w) {
20663
20664         if (this.scroll) {
20665             // The client height
20666             var clientH = Roo.lib.Dom.getViewWidth();
20667
20668             // The client width
20669             var clientW = Roo.lib.Dom.getViewHeight();
20670
20671             // The amt scrolled down
20672             var st = this.DDM.getScrollTop();
20673
20674             // The amt scrolled right
20675             var sl = this.DDM.getScrollLeft();
20676
20677             // Location of the bottom of the element
20678             var bot = h + y;
20679
20680             // Location of the right of the element
20681             var right = w + x;
20682
20683             // The distance from the cursor to the bottom of the visible area,
20684             // adjusted so that we don't scroll if the cursor is beyond the
20685             // element drag constraints
20686             var toBot = (clientH + st - y - this.deltaY);
20687
20688             // The distance from the cursor to the right of the visible area
20689             var toRight = (clientW + sl - x - this.deltaX);
20690
20691
20692             // How close to the edge the cursor must be before we scroll
20693             // var thresh = (document.all) ? 100 : 40;
20694             var thresh = 40;
20695
20696             // How many pixels to scroll per autoscroll op.  This helps to reduce
20697             // clunky scrolling. IE is more sensitive about this ... it needs this
20698             // value to be higher.
20699             var scrAmt = (document.all) ? 80 : 30;
20700
20701             // Scroll down if we are near the bottom of the visible page and the
20702             // obj extends below the crease
20703             if ( bot > clientH && toBot < thresh ) {
20704                 window.scrollTo(sl, st + scrAmt);
20705             }
20706
20707             // Scroll up if the window is scrolled down and the top of the object
20708             // goes above the top border
20709             if ( y < st && st > 0 && y - st < thresh ) {
20710                 window.scrollTo(sl, st - scrAmt);
20711             }
20712
20713             // Scroll right if the obj is beyond the right border and the cursor is
20714             // near the border.
20715             if ( right > clientW && toRight < thresh ) {
20716                 window.scrollTo(sl + scrAmt, st);
20717             }
20718
20719             // Scroll left if the window has been scrolled to the right and the obj
20720             // extends past the left border
20721             if ( x < sl && sl > 0 && x - sl < thresh ) {
20722                 window.scrollTo(sl - scrAmt, st);
20723             }
20724         }
20725     },
20726
20727     /**
20728      * Finds the location the element should be placed if we want to move
20729      * it to where the mouse location less the click offset would place us.
20730      * @method getTargetCoord
20731      * @param {int} iPageX the X coordinate of the click
20732      * @param {int} iPageY the Y coordinate of the click
20733      * @return an object that contains the coordinates (Object.x and Object.y)
20734      * @private
20735      */
20736     getTargetCoord: function(iPageX, iPageY) {
20737
20738
20739         var x = iPageX - this.deltaX;
20740         var y = iPageY - this.deltaY;
20741
20742         if (this.constrainX) {
20743             if (x < this.minX) { x = this.minX; }
20744             if (x > this.maxX) { x = this.maxX; }
20745         }
20746
20747         if (this.constrainY) {
20748             if (y < this.minY) { y = this.minY; }
20749             if (y > this.maxY) { y = this.maxY; }
20750         }
20751
20752         x = this.getTick(x, this.xTicks);
20753         y = this.getTick(y, this.yTicks);
20754
20755
20756         return {x:x, y:y};
20757     },
20758
20759     /*
20760      * Sets up config options specific to this class. Overrides
20761      * Roo.dd.DragDrop, but all versions of this method through the
20762      * inheritance chain are called
20763      */
20764     applyConfig: function() {
20765         Roo.dd.DD.superclass.applyConfig.call(this);
20766         this.scroll = (this.config.scroll !== false);
20767     },
20768
20769     /*
20770      * Event that fires prior to the onMouseDown event.  Overrides
20771      * Roo.dd.DragDrop.
20772      */
20773     b4MouseDown: function(e) {
20774         // this.resetConstraints();
20775         this.autoOffset(e.getPageX(),
20776                             e.getPageY());
20777     },
20778
20779     /*
20780      * Event that fires prior to the onDrag event.  Overrides
20781      * Roo.dd.DragDrop.
20782      */
20783     b4Drag: function(e) {
20784         this.setDragElPos(e.getPageX(),
20785                             e.getPageY());
20786     },
20787
20788     toString: function() {
20789         return ("DD " + this.id);
20790     }
20791
20792     //////////////////////////////////////////////////////////////////////////
20793     // Debugging ygDragDrop events that can be overridden
20794     //////////////////////////////////////////////////////////////////////////
20795     /*
20796     startDrag: function(x, y) {
20797     },
20798
20799     onDrag: function(e) {
20800     },
20801
20802     onDragEnter: function(e, id) {
20803     },
20804
20805     onDragOver: function(e, id) {
20806     },
20807
20808     onDragOut: function(e, id) {
20809     },
20810
20811     onDragDrop: function(e, id) {
20812     },
20813
20814     endDrag: function(e) {
20815     }
20816
20817     */
20818
20819 });/*
20820  * Based on:
20821  * Ext JS Library 1.1.1
20822  * Copyright(c) 2006-2007, Ext JS, LLC.
20823  *
20824  * Originally Released Under LGPL - original licence link has changed is not relivant.
20825  *
20826  * Fork - LGPL
20827  * <script type="text/javascript">
20828  */
20829
20830 /**
20831  * @class Roo.dd.DDProxy
20832  * A DragDrop implementation that inserts an empty, bordered div into
20833  * the document that follows the cursor during drag operations.  At the time of
20834  * the click, the frame div is resized to the dimensions of the linked html
20835  * element, and moved to the exact location of the linked element.
20836  *
20837  * References to the "frame" element refer to the single proxy element that
20838  * was created to be dragged in place of all DDProxy elements on the
20839  * page.
20840  *
20841  * @extends Roo.dd.DD
20842  * @constructor
20843  * @param {String} id the id of the linked html element
20844  * @param {String} sGroup the group of related DragDrop objects
20845  * @param {object} config an object containing configurable attributes
20846  *                Valid properties for DDProxy in addition to those in DragDrop:
20847  *                   resizeFrame, centerFrame, dragElId
20848  */
20849 Roo.dd.DDProxy = function(id, sGroup, config) {
20850     if (id) {
20851         this.init(id, sGroup, config);
20852         this.initFrame();
20853     }
20854 };
20855
20856 /**
20857  * The default drag frame div id
20858  * @property Roo.dd.DDProxy.dragElId
20859  * @type String
20860  * @static
20861  */
20862 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20863
20864 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20865
20866     /**
20867      * By default we resize the drag frame to be the same size as the element
20868      * we want to drag (this is to get the frame effect).  We can turn it off
20869      * if we want a different behavior.
20870      * @property resizeFrame
20871      * @type boolean
20872      */
20873     resizeFrame: true,
20874
20875     /**
20876      * By default the frame is positioned exactly where the drag element is, so
20877      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20878      * you do not have constraints on the obj is to have the drag frame centered
20879      * around the cursor.  Set centerFrame to true for this effect.
20880      * @property centerFrame
20881      * @type boolean
20882      */
20883     centerFrame: false,
20884
20885     /**
20886      * Creates the proxy element if it does not yet exist
20887      * @method createFrame
20888      */
20889     createFrame: function() {
20890         var self = this;
20891         var body = document.body;
20892
20893         if (!body || !body.firstChild) {
20894             setTimeout( function() { self.createFrame(); }, 50 );
20895             return;
20896         }
20897
20898         var div = this.getDragEl();
20899
20900         if (!div) {
20901             div    = document.createElement("div");
20902             div.id = this.dragElId;
20903             var s  = div.style;
20904
20905             s.position   = "absolute";
20906             s.visibility = "hidden";
20907             s.cursor     = "move";
20908             s.border     = "2px solid #aaa";
20909             s.zIndex     = 999;
20910
20911             // appendChild can blow up IE if invoked prior to the window load event
20912             // while rendering a table.  It is possible there are other scenarios
20913             // that would cause this to happen as well.
20914             body.insertBefore(div, body.firstChild);
20915         }
20916     },
20917
20918     /**
20919      * Initialization for the drag frame element.  Must be called in the
20920      * constructor of all subclasses
20921      * @method initFrame
20922      */
20923     initFrame: function() {
20924         this.createFrame();
20925     },
20926
20927     applyConfig: function() {
20928         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20929
20930         this.resizeFrame = (this.config.resizeFrame !== false);
20931         this.centerFrame = (this.config.centerFrame);
20932         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20933     },
20934
20935     /**
20936      * Resizes the drag frame to the dimensions of the clicked object, positions
20937      * it over the object, and finally displays it
20938      * @method showFrame
20939      * @param {int} iPageX X click position
20940      * @param {int} iPageY Y click position
20941      * @private
20942      */
20943     showFrame: function(iPageX, iPageY) {
20944         var el = this.getEl();
20945         var dragEl = this.getDragEl();
20946         var s = dragEl.style;
20947
20948         this._resizeProxy();
20949
20950         if (this.centerFrame) {
20951             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20952                            Math.round(parseInt(s.height, 10)/2) );
20953         }
20954
20955         this.setDragElPos(iPageX, iPageY);
20956
20957         Roo.fly(dragEl).show();
20958     },
20959
20960     /**
20961      * The proxy is automatically resized to the dimensions of the linked
20962      * element when a drag is initiated, unless resizeFrame is set to false
20963      * @method _resizeProxy
20964      * @private
20965      */
20966     _resizeProxy: function() {
20967         if (this.resizeFrame) {
20968             var el = this.getEl();
20969             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20970         }
20971     },
20972
20973     // overrides Roo.dd.DragDrop
20974     b4MouseDown: function(e) {
20975         var x = e.getPageX();
20976         var y = e.getPageY();
20977         this.autoOffset(x, y);
20978         this.setDragElPos(x, y);
20979     },
20980
20981     // overrides Roo.dd.DragDrop
20982     b4StartDrag: function(x, y) {
20983         // show the drag frame
20984         this.showFrame(x, y);
20985     },
20986
20987     // overrides Roo.dd.DragDrop
20988     b4EndDrag: function(e) {
20989         Roo.fly(this.getDragEl()).hide();
20990     },
20991
20992     // overrides Roo.dd.DragDrop
20993     // By default we try to move the element to the last location of the frame.
20994     // This is so that the default behavior mirrors that of Roo.dd.DD.
20995     endDrag: function(e) {
20996
20997         var lel = this.getEl();
20998         var del = this.getDragEl();
20999
21000         // Show the drag frame briefly so we can get its position
21001         del.style.visibility = "";
21002
21003         this.beforeMove();
21004         // Hide the linked element before the move to get around a Safari
21005         // rendering bug.
21006         lel.style.visibility = "hidden";
21007         Roo.dd.DDM.moveToEl(lel, del);
21008         del.style.visibility = "hidden";
21009         lel.style.visibility = "";
21010
21011         this.afterDrag();
21012     },
21013
21014     beforeMove : function(){
21015
21016     },
21017
21018     afterDrag : function(){
21019
21020     },
21021
21022     toString: function() {
21023         return ("DDProxy " + this.id);
21024     }
21025
21026 });
21027 /*
21028  * Based on:
21029  * Ext JS Library 1.1.1
21030  * Copyright(c) 2006-2007, Ext JS, LLC.
21031  *
21032  * Originally Released Under LGPL - original licence link has changed is not relivant.
21033  *
21034  * Fork - LGPL
21035  * <script type="text/javascript">
21036  */
21037
21038  /**
21039  * @class Roo.dd.DDTarget
21040  * A DragDrop implementation that does not move, but can be a drop
21041  * target.  You would get the same result by simply omitting implementation
21042  * for the event callbacks, but this way we reduce the processing cost of the
21043  * event listener and the callbacks.
21044  * @extends Roo.dd.DragDrop
21045  * @constructor
21046  * @param {String} id the id of the element that is a drop target
21047  * @param {String} sGroup the group of related DragDrop objects
21048  * @param {object} config an object containing configurable attributes
21049  *                 Valid properties for DDTarget in addition to those in
21050  *                 DragDrop:
21051  *                    none
21052  */
21053 Roo.dd.DDTarget = function(id, sGroup, config) {
21054     if (id) {
21055         this.initTarget(id, sGroup, config);
21056     }
21057     if (config.listeners || config.events) { 
21058        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21059             listeners : config.listeners || {}, 
21060             events : config.events || {} 
21061         });    
21062     }
21063 };
21064
21065 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21066 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21067     toString: function() {
21068         return ("DDTarget " + this.id);
21069     }
21070 });
21071 /*
21072  * Based on:
21073  * Ext JS Library 1.1.1
21074  * Copyright(c) 2006-2007, Ext JS, LLC.
21075  *
21076  * Originally Released Under LGPL - original licence link has changed is not relivant.
21077  *
21078  * Fork - LGPL
21079  * <script type="text/javascript">
21080  */
21081  
21082
21083 /**
21084  * @class Roo.dd.ScrollManager
21085  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21086  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21087  * @singleton
21088  */
21089 Roo.dd.ScrollManager = function(){
21090     var ddm = Roo.dd.DragDropMgr;
21091     var els = {};
21092     var dragEl = null;
21093     var proc = {};
21094     
21095     
21096     
21097     var onStop = function(e){
21098         dragEl = null;
21099         clearProc();
21100     };
21101     
21102     var triggerRefresh = function(){
21103         if(ddm.dragCurrent){
21104              ddm.refreshCache(ddm.dragCurrent.groups);
21105         }
21106     };
21107     
21108     var doScroll = function(){
21109         if(ddm.dragCurrent){
21110             var dds = Roo.dd.ScrollManager;
21111             if(!dds.animate){
21112                 if(proc.el.scroll(proc.dir, dds.increment)){
21113                     triggerRefresh();
21114                 }
21115             }else{
21116                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21117             }
21118         }
21119     };
21120     
21121     var clearProc = function(){
21122         if(proc.id){
21123             clearInterval(proc.id);
21124         }
21125         proc.id = 0;
21126         proc.el = null;
21127         proc.dir = "";
21128     };
21129     
21130     var startProc = function(el, dir){
21131          Roo.log('scroll startproc');
21132         clearProc();
21133         proc.el = el;
21134         proc.dir = dir;
21135         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21136     };
21137     
21138     var onFire = function(e, isDrop){
21139        
21140         if(isDrop || !ddm.dragCurrent){ return; }
21141         var dds = Roo.dd.ScrollManager;
21142         if(!dragEl || dragEl != ddm.dragCurrent){
21143             dragEl = ddm.dragCurrent;
21144             // refresh regions on drag start
21145             dds.refreshCache();
21146         }
21147         
21148         var xy = Roo.lib.Event.getXY(e);
21149         var pt = new Roo.lib.Point(xy[0], xy[1]);
21150         for(var id in els){
21151             var el = els[id], r = el._region;
21152             if(r && r.contains(pt) && el.isScrollable()){
21153                 if(r.bottom - pt.y <= dds.thresh){
21154                     if(proc.el != el){
21155                         startProc(el, "down");
21156                     }
21157                     return;
21158                 }else if(r.right - pt.x <= dds.thresh){
21159                     if(proc.el != el){
21160                         startProc(el, "left");
21161                     }
21162                     return;
21163                 }else if(pt.y - r.top <= dds.thresh){
21164                     if(proc.el != el){
21165                         startProc(el, "up");
21166                     }
21167                     return;
21168                 }else if(pt.x - r.left <= dds.thresh){
21169                     if(proc.el != el){
21170                         startProc(el, "right");
21171                     }
21172                     return;
21173                 }
21174             }
21175         }
21176         clearProc();
21177     };
21178     
21179     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21180     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21181     
21182     return {
21183         /**
21184          * Registers new overflow element(s) to auto scroll
21185          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21186          */
21187         register : function(el){
21188             if(el instanceof Array){
21189                 for(var i = 0, len = el.length; i < len; i++) {
21190                         this.register(el[i]);
21191                 }
21192             }else{
21193                 el = Roo.get(el);
21194                 els[el.id] = el;
21195             }
21196             Roo.dd.ScrollManager.els = els;
21197         },
21198         
21199         /**
21200          * Unregisters overflow element(s) so they are no longer scrolled
21201          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21202          */
21203         unregister : function(el){
21204             if(el instanceof Array){
21205                 for(var i = 0, len = el.length; i < len; i++) {
21206                         this.unregister(el[i]);
21207                 }
21208             }else{
21209                 el = Roo.get(el);
21210                 delete els[el.id];
21211             }
21212         },
21213         
21214         /**
21215          * The number of pixels from the edge of a container the pointer needs to be to 
21216          * trigger scrolling (defaults to 25)
21217          * @type Number
21218          */
21219         thresh : 25,
21220         
21221         /**
21222          * The number of pixels to scroll in each scroll increment (defaults to 50)
21223          * @type Number
21224          */
21225         increment : 100,
21226         
21227         /**
21228          * The frequency of scrolls in milliseconds (defaults to 500)
21229          * @type Number
21230          */
21231         frequency : 500,
21232         
21233         /**
21234          * True to animate the scroll (defaults to true)
21235          * @type Boolean
21236          */
21237         animate: true,
21238         
21239         /**
21240          * The animation duration in seconds - 
21241          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21242          * @type Number
21243          */
21244         animDuration: .4,
21245         
21246         /**
21247          * Manually trigger a cache refresh.
21248          */
21249         refreshCache : function(){
21250             for(var id in els){
21251                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21252                     els[id]._region = els[id].getRegion();
21253                 }
21254             }
21255         }
21256     };
21257 }();/*
21258  * Based on:
21259  * Ext JS Library 1.1.1
21260  * Copyright(c) 2006-2007, Ext JS, LLC.
21261  *
21262  * Originally Released Under LGPL - original licence link has changed is not relivant.
21263  *
21264  * Fork - LGPL
21265  * <script type="text/javascript">
21266  */
21267  
21268
21269 /**
21270  * @class Roo.dd.Registry
21271  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21272  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21273  * @singleton
21274  */
21275 Roo.dd.Registry = function(){
21276     var elements = {}; 
21277     var handles = {}; 
21278     var autoIdSeed = 0;
21279
21280     var getId = function(el, autogen){
21281         if(typeof el == "string"){
21282             return el;
21283         }
21284         var id = el.id;
21285         if(!id && autogen !== false){
21286             id = "roodd-" + (++autoIdSeed);
21287             el.id = id;
21288         }
21289         return id;
21290     };
21291     
21292     return {
21293     /**
21294      * Register a drag drop element
21295      * @param {String|HTMLElement} element The id or DOM node to register
21296      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21297      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21298      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21299      * populated in the data object (if applicable):
21300      * <pre>
21301 Value      Description<br />
21302 ---------  ------------------------------------------<br />
21303 handles    Array of DOM nodes that trigger dragging<br />
21304            for the element being registered<br />
21305 isHandle   True if the element passed in triggers<br />
21306            dragging itself, else false
21307 </pre>
21308      */
21309         register : function(el, data){
21310             data = data || {};
21311             if(typeof el == "string"){
21312                 el = document.getElementById(el);
21313             }
21314             data.ddel = el;
21315             elements[getId(el)] = data;
21316             if(data.isHandle !== false){
21317                 handles[data.ddel.id] = data;
21318             }
21319             if(data.handles){
21320                 var hs = data.handles;
21321                 for(var i = 0, len = hs.length; i < len; i++){
21322                         handles[getId(hs[i])] = data;
21323                 }
21324             }
21325         },
21326
21327     /**
21328      * Unregister a drag drop element
21329      * @param {String|HTMLElement}  element The id or DOM node to unregister
21330      */
21331         unregister : function(el){
21332             var id = getId(el, false);
21333             var data = elements[id];
21334             if(data){
21335                 delete elements[id];
21336                 if(data.handles){
21337                     var hs = data.handles;
21338                     for(var i = 0, len = hs.length; i < len; i++){
21339                         delete handles[getId(hs[i], false)];
21340                     }
21341                 }
21342             }
21343         },
21344
21345     /**
21346      * Returns the handle registered for a DOM Node by id
21347      * @param {String|HTMLElement} id The DOM node or id to look up
21348      * @return {Object} handle The custom handle data
21349      */
21350         getHandle : function(id){
21351             if(typeof id != "string"){ // must be element?
21352                 id = id.id;
21353             }
21354             return handles[id];
21355         },
21356
21357     /**
21358      * Returns the handle that is registered for the DOM node that is the target of the event
21359      * @param {Event} e The event
21360      * @return {Object} handle The custom handle data
21361      */
21362         getHandleFromEvent : function(e){
21363             var t = Roo.lib.Event.getTarget(e);
21364             return t ? handles[t.id] : null;
21365         },
21366
21367     /**
21368      * Returns a custom data object that is registered for a DOM node by id
21369      * @param {String|HTMLElement} id The DOM node or id to look up
21370      * @return {Object} data The custom data
21371      */
21372         getTarget : function(id){
21373             if(typeof id != "string"){ // must be element?
21374                 id = id.id;
21375             }
21376             return elements[id];
21377         },
21378
21379     /**
21380      * Returns a custom data object that is registered for the DOM node that is the target of the event
21381      * @param {Event} e The event
21382      * @return {Object} data The custom data
21383      */
21384         getTargetFromEvent : function(e){
21385             var t = Roo.lib.Event.getTarget(e);
21386             return t ? elements[t.id] || handles[t.id] : null;
21387         }
21388     };
21389 }();/*
21390  * Based on:
21391  * Ext JS Library 1.1.1
21392  * Copyright(c) 2006-2007, Ext JS, LLC.
21393  *
21394  * Originally Released Under LGPL - original licence link has changed is not relivant.
21395  *
21396  * Fork - LGPL
21397  * <script type="text/javascript">
21398  */
21399  
21400
21401 /**
21402  * @class Roo.dd.StatusProxy
21403  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21404  * default drag proxy used by all Roo.dd components.
21405  * @constructor
21406  * @param {Object} config
21407  */
21408 Roo.dd.StatusProxy = function(config){
21409     Roo.apply(this, config);
21410     this.id = this.id || Roo.id();
21411     this.el = new Roo.Layer({
21412         dh: {
21413             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21414                 {tag: "div", cls: "x-dd-drop-icon"},
21415                 {tag: "div", cls: "x-dd-drag-ghost"}
21416             ]
21417         }, 
21418         shadow: !config || config.shadow !== false
21419     });
21420     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21421     this.dropStatus = this.dropNotAllowed;
21422 };
21423
21424 Roo.dd.StatusProxy.prototype = {
21425     /**
21426      * @cfg {String} dropAllowed
21427      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21428      */
21429     dropAllowed : "x-dd-drop-ok",
21430     /**
21431      * @cfg {String} dropNotAllowed
21432      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21433      */
21434     dropNotAllowed : "x-dd-drop-nodrop",
21435
21436     /**
21437      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21438      * over the current target element.
21439      * @param {String} cssClass The css class for the new drop status indicator image
21440      */
21441     setStatus : function(cssClass){
21442         cssClass = cssClass || this.dropNotAllowed;
21443         if(this.dropStatus != cssClass){
21444             this.el.replaceClass(this.dropStatus, cssClass);
21445             this.dropStatus = cssClass;
21446         }
21447     },
21448
21449     /**
21450      * Resets the status indicator to the default dropNotAllowed value
21451      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21452      */
21453     reset : function(clearGhost){
21454         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21455         this.dropStatus = this.dropNotAllowed;
21456         if(clearGhost){
21457             this.ghost.update("");
21458         }
21459     },
21460
21461     /**
21462      * Updates the contents of the ghost element
21463      * @param {String} html The html that will replace the current innerHTML of the ghost element
21464      */
21465     update : function(html){
21466         if(typeof html == "string"){
21467             this.ghost.update(html);
21468         }else{
21469             this.ghost.update("");
21470             html.style.margin = "0";
21471             this.ghost.dom.appendChild(html);
21472         }
21473         // ensure float = none set?? cant remember why though.
21474         var el = this.ghost.dom.firstChild;
21475                 if(el){
21476                         Roo.fly(el).setStyle('float', 'none');
21477                 }
21478     },
21479     
21480     /**
21481      * Returns the underlying proxy {@link Roo.Layer}
21482      * @return {Roo.Layer} el
21483     */
21484     getEl : function(){
21485         return this.el;
21486     },
21487
21488     /**
21489      * Returns the ghost element
21490      * @return {Roo.Element} el
21491      */
21492     getGhost : function(){
21493         return this.ghost;
21494     },
21495
21496     /**
21497      * Hides the proxy
21498      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21499      */
21500     hide : function(clear){
21501         this.el.hide();
21502         if(clear){
21503             this.reset(true);
21504         }
21505     },
21506
21507     /**
21508      * Stops the repair animation if it's currently running
21509      */
21510     stop : function(){
21511         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21512             this.anim.stop();
21513         }
21514     },
21515
21516     /**
21517      * Displays this proxy
21518      */
21519     show : function(){
21520         this.el.show();
21521     },
21522
21523     /**
21524      * Force the Layer to sync its shadow and shim positions to the element
21525      */
21526     sync : function(){
21527         this.el.sync();
21528     },
21529
21530     /**
21531      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21532      * invalid drop operation by the item being dragged.
21533      * @param {Array} xy The XY position of the element ([x, y])
21534      * @param {Function} callback The function to call after the repair is complete
21535      * @param {Object} scope The scope in which to execute the callback
21536      */
21537     repair : function(xy, callback, scope){
21538         this.callback = callback;
21539         this.scope = scope;
21540         if(xy && this.animRepair !== false){
21541             this.el.addClass("x-dd-drag-repair");
21542             this.el.hideUnders(true);
21543             this.anim = this.el.shift({
21544                 duration: this.repairDuration || .5,
21545                 easing: 'easeOut',
21546                 xy: xy,
21547                 stopFx: true,
21548                 callback: this.afterRepair,
21549                 scope: this
21550             });
21551         }else{
21552             this.afterRepair();
21553         }
21554     },
21555
21556     // private
21557     afterRepair : function(){
21558         this.hide(true);
21559         if(typeof this.callback == "function"){
21560             this.callback.call(this.scope || this);
21561         }
21562         this.callback = null;
21563         this.scope = null;
21564     }
21565 };/*
21566  * Based on:
21567  * Ext JS Library 1.1.1
21568  * Copyright(c) 2006-2007, Ext JS, LLC.
21569  *
21570  * Originally Released Under LGPL - original licence link has changed is not relivant.
21571  *
21572  * Fork - LGPL
21573  * <script type="text/javascript">
21574  */
21575
21576 /**
21577  * @class Roo.dd.DragSource
21578  * @extends Roo.dd.DDProxy
21579  * A simple class that provides the basic implementation needed to make any element draggable.
21580  * @constructor
21581  * @param {String/HTMLElement/Element} el The container element
21582  * @param {Object} config
21583  */
21584 Roo.dd.DragSource = function(el, config){
21585     this.el = Roo.get(el);
21586     this.dragData = {};
21587     
21588     Roo.apply(this, config);
21589     
21590     if(!this.proxy){
21591         this.proxy = new Roo.dd.StatusProxy();
21592     }
21593
21594     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21595           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21596     
21597     this.dragging = false;
21598 };
21599
21600 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21601     /**
21602      * @cfg {String} dropAllowed
21603      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21604      */
21605     dropAllowed : "x-dd-drop-ok",
21606     /**
21607      * @cfg {String} dropNotAllowed
21608      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21609      */
21610     dropNotAllowed : "x-dd-drop-nodrop",
21611
21612     /**
21613      * Returns the data object associated with this drag source
21614      * @return {Object} data An object containing arbitrary data
21615      */
21616     getDragData : function(e){
21617         return this.dragData;
21618     },
21619
21620     // private
21621     onDragEnter : function(e, id){
21622         var target = Roo.dd.DragDropMgr.getDDById(id);
21623         this.cachedTarget = target;
21624         if(this.beforeDragEnter(target, e, id) !== false){
21625             if(target.isNotifyTarget){
21626                 var status = target.notifyEnter(this, e, this.dragData);
21627                 this.proxy.setStatus(status);
21628             }else{
21629                 this.proxy.setStatus(this.dropAllowed);
21630             }
21631             
21632             if(this.afterDragEnter){
21633                 /**
21634                  * An empty function by default, but provided so that you can perform a custom action
21635                  * when the dragged item enters the drop target by providing an implementation.
21636                  * @param {Roo.dd.DragDrop} target The drop target
21637                  * @param {Event} e The event object
21638                  * @param {String} id The id of the dragged element
21639                  * @method afterDragEnter
21640                  */
21641                 this.afterDragEnter(target, e, id);
21642             }
21643         }
21644     },
21645
21646     /**
21647      * An empty function by default, but provided so that you can perform a custom action
21648      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21649      * @param {Roo.dd.DragDrop} target The drop target
21650      * @param {Event} e The event object
21651      * @param {String} id The id of the dragged element
21652      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21653      */
21654     beforeDragEnter : function(target, e, id){
21655         return true;
21656     },
21657
21658     // private
21659     alignElWithMouse: function() {
21660         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21661         this.proxy.sync();
21662     },
21663
21664     // private
21665     onDragOver : function(e, id){
21666         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21667         if(this.beforeDragOver(target, e, id) !== false){
21668             if(target.isNotifyTarget){
21669                 var status = target.notifyOver(this, e, this.dragData);
21670                 this.proxy.setStatus(status);
21671             }
21672
21673             if(this.afterDragOver){
21674                 /**
21675                  * An empty function by default, but provided so that you can perform a custom action
21676                  * while the dragged item is over the drop target by providing an implementation.
21677                  * @param {Roo.dd.DragDrop} target The drop target
21678                  * @param {Event} e The event object
21679                  * @param {String} id The id of the dragged element
21680                  * @method afterDragOver
21681                  */
21682                 this.afterDragOver(target, e, id);
21683             }
21684         }
21685     },
21686
21687     /**
21688      * An empty function by default, but provided so that you can perform a custom action
21689      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21690      * @param {Roo.dd.DragDrop} target The drop target
21691      * @param {Event} e The event object
21692      * @param {String} id The id of the dragged element
21693      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21694      */
21695     beforeDragOver : function(target, e, id){
21696         return true;
21697     },
21698
21699     // private
21700     onDragOut : function(e, id){
21701         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21702         if(this.beforeDragOut(target, e, id) !== false){
21703             if(target.isNotifyTarget){
21704                 target.notifyOut(this, e, this.dragData);
21705             }
21706             this.proxy.reset();
21707             if(this.afterDragOut){
21708                 /**
21709                  * An empty function by default, but provided so that you can perform a custom action
21710                  * after the dragged item is dragged out of the target without dropping.
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 afterDragOut
21715                  */
21716                 this.afterDragOut(target, e, id);
21717             }
21718         }
21719         this.cachedTarget = null;
21720     },
21721
21722     /**
21723      * An empty function by default, but provided so that you can perform a custom action before the dragged
21724      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21725      * @param {Roo.dd.DragDrop} target The drop target
21726      * @param {Event} e The event object
21727      * @param {String} id The id of the dragged element
21728      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21729      */
21730     beforeDragOut : function(target, e, id){
21731         return true;
21732     },
21733     
21734     // private
21735     onDragDrop : function(e, id){
21736         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21737         if(this.beforeDragDrop(target, e, id) !== false){
21738             if(target.isNotifyTarget){
21739                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21740                     this.onValidDrop(target, e, id);
21741                 }else{
21742                     this.onInvalidDrop(target, e, id);
21743                 }
21744             }else{
21745                 this.onValidDrop(target, e, id);
21746             }
21747             
21748             if(this.afterDragDrop){
21749                 /**
21750                  * An empty function by default, but provided so that you can perform a custom action
21751                  * after a valid drag drop has occurred 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 dropped element
21755                  * @method afterDragDrop
21756                  */
21757                 this.afterDragDrop(target, e, id);
21758             }
21759         }
21760         delete this.cachedTarget;
21761     },
21762
21763     /**
21764      * An empty function by default, but provided so that you can perform a custom action before the dragged
21765      * item is dropped onto the target and optionally cancel the onDragDrop.
21766      * @param {Roo.dd.DragDrop} target The drop target
21767      * @param {Event} e The event object
21768      * @param {String} id The id of the dragged element
21769      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21770      */
21771     beforeDragDrop : function(target, e, id){
21772         return true;
21773     },
21774
21775     // private
21776     onValidDrop : function(target, e, id){
21777         this.hideProxy();
21778         if(this.afterValidDrop){
21779             /**
21780              * An empty function by default, but provided so that you can perform a custom action
21781              * after a valid drop has occurred by providing an implementation.
21782              * @param {Object} target The target DD 
21783              * @param {Event} e The event object
21784              * @param {String} id The id of the dropped element
21785              * @method afterInvalidDrop
21786              */
21787             this.afterValidDrop(target, e, id);
21788         }
21789     },
21790
21791     // private
21792     getRepairXY : function(e, data){
21793         return this.el.getXY();  
21794     },
21795
21796     // private
21797     onInvalidDrop : function(target, e, id){
21798         this.beforeInvalidDrop(target, e, id);
21799         if(this.cachedTarget){
21800             if(this.cachedTarget.isNotifyTarget){
21801                 this.cachedTarget.notifyOut(this, e, this.dragData);
21802             }
21803             this.cacheTarget = null;
21804         }
21805         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21806
21807         if(this.afterInvalidDrop){
21808             /**
21809              * An empty function by default, but provided so that you can perform a custom action
21810              * after an invalid drop has occurred by providing an implementation.
21811              * @param {Event} e The event object
21812              * @param {String} id The id of the dropped element
21813              * @method afterInvalidDrop
21814              */
21815             this.afterInvalidDrop(e, id);
21816         }
21817     },
21818
21819     // private
21820     afterRepair : function(){
21821         if(Roo.enableFx){
21822             this.el.highlight(this.hlColor || "c3daf9");
21823         }
21824         this.dragging = false;
21825     },
21826
21827     /**
21828      * An empty function by default, but provided so that you can perform a custom action after an invalid
21829      * drop has occurred.
21830      * @param {Roo.dd.DragDrop} target The drop target
21831      * @param {Event} e The event object
21832      * @param {String} id The id of the dragged element
21833      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21834      */
21835     beforeInvalidDrop : function(target, e, id){
21836         return true;
21837     },
21838
21839     // private
21840     handleMouseDown : function(e){
21841         if(this.dragging) {
21842             return;
21843         }
21844         var data = this.getDragData(e);
21845         if(data && this.onBeforeDrag(data, e) !== false){
21846             this.dragData = data;
21847             this.proxy.stop();
21848             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21849         } 
21850     },
21851
21852     /**
21853      * An empty function by default, but provided so that you can perform a custom action before the initial
21854      * drag event begins and optionally cancel it.
21855      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21856      * @param {Event} e The event object
21857      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21858      */
21859     onBeforeDrag : function(data, e){
21860         return true;
21861     },
21862
21863     /**
21864      * An empty function by default, but provided so that you can perform a custom action once the initial
21865      * drag event has begun.  The drag cannot be canceled from this function.
21866      * @param {Number} x The x position of the click on the dragged object
21867      * @param {Number} y The y position of the click on the dragged object
21868      */
21869     onStartDrag : Roo.emptyFn,
21870
21871     // private - YUI override
21872     startDrag : function(x, y){
21873         this.proxy.reset();
21874         this.dragging = true;
21875         this.proxy.update("");
21876         this.onInitDrag(x, y);
21877         this.proxy.show();
21878     },
21879
21880     // private
21881     onInitDrag : function(x, y){
21882         var clone = this.el.dom.cloneNode(true);
21883         clone.id = Roo.id(); // prevent duplicate ids
21884         this.proxy.update(clone);
21885         this.onStartDrag(x, y);
21886         return true;
21887     },
21888
21889     /**
21890      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21891      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21892      */
21893     getProxy : function(){
21894         return this.proxy;  
21895     },
21896
21897     /**
21898      * Hides the drag source's {@link Roo.dd.StatusProxy}
21899      */
21900     hideProxy : function(){
21901         this.proxy.hide();  
21902         this.proxy.reset(true);
21903         this.dragging = false;
21904     },
21905
21906     // private
21907     triggerCacheRefresh : function(){
21908         Roo.dd.DDM.refreshCache(this.groups);
21909     },
21910
21911     // private - override to prevent hiding
21912     b4EndDrag: function(e) {
21913     },
21914
21915     // private - override to prevent moving
21916     endDrag : function(e){
21917         this.onEndDrag(this.dragData, e);
21918     },
21919
21920     // private
21921     onEndDrag : function(data, e){
21922     },
21923     
21924     // private - pin to cursor
21925     autoOffset : function(x, y) {
21926         this.setDelta(-12, -20);
21927     }    
21928 });/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938
21939
21940 /**
21941  * @class Roo.dd.DropTarget
21942  * @extends Roo.dd.DDTarget
21943  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21944  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21945  * @constructor
21946  * @param {String/HTMLElement/Element} el The container element
21947  * @param {Object} config
21948  */
21949 Roo.dd.DropTarget = function(el, config){
21950     this.el = Roo.get(el);
21951     
21952     var listeners = false; ;
21953     if (config && config.listeners) {
21954         listeners= config.listeners;
21955         delete config.listeners;
21956     }
21957     Roo.apply(this, config);
21958     
21959     if(this.containerScroll){
21960         Roo.dd.ScrollManager.register(this.el);
21961     }
21962     this.addEvents( {
21963          /**
21964          * @scope Roo.dd.DropTarget
21965          */
21966          
21967          /**
21968          * @event enter
21969          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21970          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21971          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21972          * 
21973          * IMPORTANT : it should set this.overClass and this.dropAllowed
21974          * 
21975          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21976          * @param {Event} e The event
21977          * @param {Object} data An object containing arbitrary data supplied by the drag source
21978          */
21979         "enter" : true,
21980         
21981          /**
21982          * @event over
21983          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21984          * This method will be called on every mouse movement while the drag source is over the drop target.
21985          * This default implementation simply returns the dropAllowed config value.
21986          * 
21987          * IMPORTANT : it should set this.dropAllowed
21988          * 
21989          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21990          * @param {Event} e The event
21991          * @param {Object} data An object containing arbitrary data supplied by the drag source
21992          
21993          */
21994         "over" : true,
21995         /**
21996          * @event out
21997          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21998          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21999          * overClass (if any) from the drop element.
22000          * 
22001          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22002          * @param {Event} e The event
22003          * @param {Object} data An object containing arbitrary data supplied by the drag source
22004          */
22005          "out" : true,
22006          
22007         /**
22008          * @event drop
22009          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22010          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22011          * implementation that does something to process the drop event and returns true so that the drag source's
22012          * repair action does not run.
22013          * 
22014          * IMPORTANT : it should set this.success
22015          * 
22016          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22017          * @param {Event} e The event
22018          * @param {Object} data An object containing arbitrary data supplied by the drag source
22019         */
22020          "drop" : true
22021     });
22022             
22023      
22024     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22025         this.el.dom, 
22026         this.ddGroup || this.group,
22027         {
22028             isTarget: true,
22029             listeners : listeners || {} 
22030            
22031         
22032         }
22033     );
22034
22035 };
22036
22037 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22038     /**
22039      * @cfg {String} overClass
22040      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22041      */
22042      /**
22043      * @cfg {String} ddGroup
22044      * The drag drop group to handle drop events for
22045      */
22046      
22047     /**
22048      * @cfg {String} dropAllowed
22049      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22050      */
22051     dropAllowed : "x-dd-drop-ok",
22052     /**
22053      * @cfg {String} dropNotAllowed
22054      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22055      */
22056     dropNotAllowed : "x-dd-drop-nodrop",
22057     /**
22058      * @cfg {boolean} success
22059      * set this after drop listener.. 
22060      */
22061     success : false,
22062     /**
22063      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22064      * if the drop point is valid for over/enter..
22065      */
22066     valid : false,
22067     // private
22068     isTarget : true,
22069
22070     // private
22071     isNotifyTarget : true,
22072     
22073     /**
22074      * @hide
22075      */
22076     notifyEnter : function(dd, e, data)
22077     {
22078         this.valid = true;
22079         this.fireEvent('enter', dd, e, data);
22080         if(this.overClass){
22081             this.el.addClass(this.overClass);
22082         }
22083         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22084             this.valid ? this.dropAllowed : this.dropNotAllowed
22085         );
22086     },
22087
22088     /**
22089      * @hide
22090      */
22091     notifyOver : function(dd, e, data)
22092     {
22093         this.valid = true;
22094         this.fireEvent('over', dd, e, data);
22095         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22096             this.valid ? this.dropAllowed : this.dropNotAllowed
22097         );
22098     },
22099
22100     /**
22101      * @hide
22102      */
22103     notifyOut : function(dd, e, data)
22104     {
22105         this.fireEvent('out', dd, e, data);
22106         if(this.overClass){
22107             this.el.removeClass(this.overClass);
22108         }
22109     },
22110
22111     /**
22112      * @hide
22113      */
22114     notifyDrop : function(dd, e, data)
22115     {
22116         this.success = false;
22117         this.fireEvent('drop', dd, e, data);
22118         return this.success;
22119     }
22120 });/*
22121  * Based on:
22122  * Ext JS Library 1.1.1
22123  * Copyright(c) 2006-2007, Ext JS, LLC.
22124  *
22125  * Originally Released Under LGPL - original licence link has changed is not relivant.
22126  *
22127  * Fork - LGPL
22128  * <script type="text/javascript">
22129  */
22130
22131
22132 /**
22133  * @class Roo.dd.DragZone
22134  * @extends Roo.dd.DragSource
22135  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22136  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22137  * @constructor
22138  * @param {String/HTMLElement/Element} el The container element
22139  * @param {Object} config
22140  */
22141 Roo.dd.DragZone = function(el, config){
22142     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22143     if(this.containerScroll){
22144         Roo.dd.ScrollManager.register(this.el);
22145     }
22146 };
22147
22148 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22149     /**
22150      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22151      * for auto scrolling during drag operations.
22152      */
22153     /**
22154      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22155      * method after a failed drop (defaults to "c3daf9" - light blue)
22156      */
22157
22158     /**
22159      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22160      * for a valid target to drag based on the mouse down. Override this method
22161      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22162      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22163      * @param {EventObject} e The mouse down event
22164      * @return {Object} The dragData
22165      */
22166     getDragData : function(e){
22167         return Roo.dd.Registry.getHandleFromEvent(e);
22168     },
22169     
22170     /**
22171      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22172      * this.dragData.ddel
22173      * @param {Number} x The x position of the click on the dragged object
22174      * @param {Number} y The y position of the click on the dragged object
22175      * @return {Boolean} true to continue the drag, false to cancel
22176      */
22177     onInitDrag : function(x, y){
22178         this.proxy.update(this.dragData.ddel.cloneNode(true));
22179         this.onStartDrag(x, y);
22180         return true;
22181     },
22182     
22183     /**
22184      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22185      */
22186     afterRepair : function(){
22187         if(Roo.enableFx){
22188             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22189         }
22190         this.dragging = false;
22191     },
22192
22193     /**
22194      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22195      * the XY of this.dragData.ddel
22196      * @param {EventObject} e The mouse up event
22197      * @return {Array} The xy location (e.g. [100, 200])
22198      */
22199     getRepairXY : function(e){
22200         return Roo.Element.fly(this.dragData.ddel).getXY();  
22201     }
22202 });/*
22203  * Based on:
22204  * Ext JS Library 1.1.1
22205  * Copyright(c) 2006-2007, Ext JS, LLC.
22206  *
22207  * Originally Released Under LGPL - original licence link has changed is not relivant.
22208  *
22209  * Fork - LGPL
22210  * <script type="text/javascript">
22211  */
22212 /**
22213  * @class Roo.dd.DropZone
22214  * @extends Roo.dd.DropTarget
22215  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22216  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22217  * @constructor
22218  * @param {String/HTMLElement/Element} el The container element
22219  * @param {Object} config
22220  */
22221 Roo.dd.DropZone = function(el, config){
22222     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22223 };
22224
22225 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22226     /**
22227      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22228      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22229      * provide your own custom lookup.
22230      * @param {Event} e The event
22231      * @return {Object} data The custom data
22232      */
22233     getTargetFromEvent : function(e){
22234         return Roo.dd.Registry.getTargetFromEvent(e);
22235     },
22236
22237     /**
22238      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22239      * that it has registered.  This method has no default implementation and should be overridden to provide
22240      * node-specific processing if necessary.
22241      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22242      * {@link #getTargetFromEvent} for this node)
22243      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22244      * @param {Event} e The event
22245      * @param {Object} data An object containing arbitrary data supplied by the drag source
22246      */
22247     onNodeEnter : function(n, dd, e, data){
22248         
22249     },
22250
22251     /**
22252      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22253      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22254      * overridden to provide the proper feedback.
22255      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22256      * {@link #getTargetFromEvent} for this node)
22257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22258      * @param {Event} e The event
22259      * @param {Object} data An object containing arbitrary data supplied by the drag source
22260      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22261      * underlying {@link Roo.dd.StatusProxy} can be updated
22262      */
22263     onNodeOver : function(n, dd, e, data){
22264         return this.dropAllowed;
22265     },
22266
22267     /**
22268      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22269      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22270      * node-specific processing if necessary.
22271      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22272      * {@link #getTargetFromEvent} for this node)
22273      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22274      * @param {Event} e The event
22275      * @param {Object} data An object containing arbitrary data supplied by the drag source
22276      */
22277     onNodeOut : function(n, dd, e, data){
22278         
22279     },
22280
22281     /**
22282      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22283      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22284      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22285      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22286      * {@link #getTargetFromEvent} for this node)
22287      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22288      * @param {Event} e The event
22289      * @param {Object} data An object containing arbitrary data supplied by the drag source
22290      * @return {Boolean} True if the drop was valid, else false
22291      */
22292     onNodeDrop : function(n, dd, e, data){
22293         return false;
22294     },
22295
22296     /**
22297      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22298      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22299      * it should be overridden to provide the proper feedback if necessary.
22300      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22301      * @param {Event} e The event
22302      * @param {Object} data An object containing arbitrary data supplied by the drag source
22303      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22304      * underlying {@link Roo.dd.StatusProxy} can be updated
22305      */
22306     onContainerOver : function(dd, e, data){
22307         return this.dropNotAllowed;
22308     },
22309
22310     /**
22311      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22312      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22313      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22314      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22315      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22316      * @param {Event} e The event
22317      * @param {Object} data An object containing arbitrary data supplied by the drag source
22318      * @return {Boolean} True if the drop was valid, else false
22319      */
22320     onContainerDrop : function(dd, e, data){
22321         return false;
22322     },
22323
22324     /**
22325      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22326      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22327      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22328      * you should override this method and provide a custom implementation.
22329      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22330      * @param {Event} e The event
22331      * @param {Object} data An object containing arbitrary data supplied by the drag source
22332      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22333      * underlying {@link Roo.dd.StatusProxy} can be updated
22334      */
22335     notifyEnter : function(dd, e, data){
22336         return this.dropNotAllowed;
22337     },
22338
22339     /**
22340      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22341      * This method will be called on every mouse movement while the drag source is over the drop zone.
22342      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22343      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22344      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22345      * registered node, it will call {@link #onContainerOver}.
22346      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22347      * @param {Event} e The event
22348      * @param {Object} data An object containing arbitrary data supplied by the drag source
22349      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22350      * underlying {@link Roo.dd.StatusProxy} can be updated
22351      */
22352     notifyOver : function(dd, e, data){
22353         var n = this.getTargetFromEvent(e);
22354         if(!n){ // not over valid drop target
22355             if(this.lastOverNode){
22356                 this.onNodeOut(this.lastOverNode, dd, e, data);
22357                 this.lastOverNode = null;
22358             }
22359             return this.onContainerOver(dd, e, data);
22360         }
22361         if(this.lastOverNode != n){
22362             if(this.lastOverNode){
22363                 this.onNodeOut(this.lastOverNode, dd, e, data);
22364             }
22365             this.onNodeEnter(n, dd, e, data);
22366             this.lastOverNode = n;
22367         }
22368         return this.onNodeOver(n, dd, e, data);
22369     },
22370
22371     /**
22372      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22373      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22374      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22375      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22376      * @param {Event} e The event
22377      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22378      */
22379     notifyOut : function(dd, e, data){
22380         if(this.lastOverNode){
22381             this.onNodeOut(this.lastOverNode, dd, e, data);
22382             this.lastOverNode = null;
22383         }
22384     },
22385
22386     /**
22387      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22388      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22389      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22390      * otherwise it will call {@link #onContainerDrop}.
22391      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22392      * @param {Event} e The event
22393      * @param {Object} data An object containing arbitrary data supplied by the drag source
22394      * @return {Boolean} True if the drop was valid, else false
22395      */
22396     notifyDrop : function(dd, e, data){
22397         if(this.lastOverNode){
22398             this.onNodeOut(this.lastOverNode, dd, e, data);
22399             this.lastOverNode = null;
22400         }
22401         var n = this.getTargetFromEvent(e);
22402         return n ?
22403             this.onNodeDrop(n, dd, e, data) :
22404             this.onContainerDrop(dd, e, data);
22405     },
22406
22407     // private
22408     triggerCacheRefresh : function(){
22409         Roo.dd.DDM.refreshCache(this.groups);
22410     }  
22411 });/*
22412  * Based on:
22413  * Ext JS Library 1.1.1
22414  * Copyright(c) 2006-2007, Ext JS, LLC.
22415  *
22416  * Originally Released Under LGPL - original licence link has changed is not relivant.
22417  *
22418  * Fork - LGPL
22419  * <script type="text/javascript">
22420  */
22421
22422
22423 /**
22424  * @class Roo.data.SortTypes
22425  * @singleton
22426  * Defines the default sorting (casting?) comparison functions used when sorting data.
22427  */
22428 Roo.data.SortTypes = {
22429     /**
22430      * Default sort that does nothing
22431      * @param {Mixed} s The value being converted
22432      * @return {Mixed} The comparison value
22433      */
22434     none : function(s){
22435         return s;
22436     },
22437     
22438     /**
22439      * The regular expression used to strip tags
22440      * @type {RegExp}
22441      * @property
22442      */
22443     stripTagsRE : /<\/?[^>]+>/gi,
22444     
22445     /**
22446      * Strips all HTML tags to sort on text only
22447      * @param {Mixed} s The value being converted
22448      * @return {String} The comparison value
22449      */
22450     asText : function(s){
22451         return String(s).replace(this.stripTagsRE, "");
22452     },
22453     
22454     /**
22455      * Strips all HTML tags to sort on text only - Case insensitive
22456      * @param {Mixed} s The value being converted
22457      * @return {String} The comparison value
22458      */
22459     asUCText : function(s){
22460         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22461     },
22462     
22463     /**
22464      * Case insensitive string
22465      * @param {Mixed} s The value being converted
22466      * @return {String} The comparison value
22467      */
22468     asUCString : function(s) {
22469         return String(s).toUpperCase();
22470     },
22471     
22472     /**
22473      * Date sorting
22474      * @param {Mixed} s The value being converted
22475      * @return {Number} The comparison value
22476      */
22477     asDate : function(s) {
22478         if(!s){
22479             return 0;
22480         }
22481         if(s instanceof Date){
22482             return s.getTime();
22483         }
22484         return Date.parse(String(s));
22485     },
22486     
22487     /**
22488      * Float sorting
22489      * @param {Mixed} s The value being converted
22490      * @return {Float} The comparison value
22491      */
22492     asFloat : function(s) {
22493         var val = parseFloat(String(s).replace(/,/g, ""));
22494         if(isNaN(val)) {
22495             val = 0;
22496         }
22497         return val;
22498     },
22499     
22500     /**
22501      * Integer sorting
22502      * @param {Mixed} s The value being converted
22503      * @return {Number} The comparison value
22504      */
22505     asInt : function(s) {
22506         var val = parseInt(String(s).replace(/,/g, ""));
22507         if(isNaN(val)) {
22508             val = 0;
22509         }
22510         return val;
22511     }
22512 };/*
22513  * Based on:
22514  * Ext JS Library 1.1.1
22515  * Copyright(c) 2006-2007, Ext JS, LLC.
22516  *
22517  * Originally Released Under LGPL - original licence link has changed is not relivant.
22518  *
22519  * Fork - LGPL
22520  * <script type="text/javascript">
22521  */
22522
22523 /**
22524 * @class Roo.data.Record
22525  * Instances of this class encapsulate both record <em>definition</em> information, and record
22526  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22527  * to access Records cached in an {@link Roo.data.Store} object.<br>
22528  * <p>
22529  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22530  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22531  * objects.<br>
22532  * <p>
22533  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22534  * @constructor
22535  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22536  * {@link #create}. The parameters are the same.
22537  * @param {Array} data An associative Array of data values keyed by the field name.
22538  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22539  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22540  * not specified an integer id is generated.
22541  */
22542 Roo.data.Record = function(data, id){
22543     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22544     this.data = data;
22545 };
22546
22547 /**
22548  * Generate a constructor for a specific record layout.
22549  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22550  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22551  * Each field definition object may contain the following properties: <ul>
22552  * <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,
22553  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22554  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22555  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22556  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22557  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22558  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22559  * this may be omitted.</p></li>
22560  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22561  * <ul><li>auto (Default, implies no conversion)</li>
22562  * <li>string</li>
22563  * <li>int</li>
22564  * <li>float</li>
22565  * <li>boolean</li>
22566  * <li>date</li></ul></p></li>
22567  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22568  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22569  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22570  * by the Reader into an object that will be stored in the Record. It is passed the
22571  * following parameters:<ul>
22572  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22573  * </ul></p></li>
22574  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22575  * </ul>
22576  * <br>usage:<br><pre><code>
22577 var TopicRecord = Roo.data.Record.create(
22578     {name: 'title', mapping: 'topic_title'},
22579     {name: 'author', mapping: 'username'},
22580     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22581     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22582     {name: 'lastPoster', mapping: 'user2'},
22583     {name: 'excerpt', mapping: 'post_text'}
22584 );
22585
22586 var myNewRecord = new TopicRecord({
22587     title: 'Do my job please',
22588     author: 'noobie',
22589     totalPosts: 1,
22590     lastPost: new Date(),
22591     lastPoster: 'Animal',
22592     excerpt: 'No way dude!'
22593 });
22594 myStore.add(myNewRecord);
22595 </code></pre>
22596  * @method create
22597  * @static
22598  */
22599 Roo.data.Record.create = function(o){
22600     var f = function(){
22601         f.superclass.constructor.apply(this, arguments);
22602     };
22603     Roo.extend(f, Roo.data.Record);
22604     var p = f.prototype;
22605     p.fields = new Roo.util.MixedCollection(false, function(field){
22606         return field.name;
22607     });
22608     for(var i = 0, len = o.length; i < len; i++){
22609         p.fields.add(new Roo.data.Field(o[i]));
22610     }
22611     f.getField = function(name){
22612         return p.fields.get(name);  
22613     };
22614     return f;
22615 };
22616
22617 Roo.data.Record.AUTO_ID = 1000;
22618 Roo.data.Record.EDIT = 'edit';
22619 Roo.data.Record.REJECT = 'reject';
22620 Roo.data.Record.COMMIT = 'commit';
22621
22622 Roo.data.Record.prototype = {
22623     /**
22624      * Readonly flag - true if this record has been modified.
22625      * @type Boolean
22626      */
22627     dirty : false,
22628     editing : false,
22629     error: null,
22630     modified: null,
22631
22632     // private
22633     join : function(store){
22634         this.store = store;
22635     },
22636
22637     /**
22638      * Set the named field to the specified value.
22639      * @param {String} name The name of the field to set.
22640      * @param {Object} value The value to set the field to.
22641      */
22642     set : function(name, value){
22643         if(this.data[name] == value){
22644             return;
22645         }
22646         this.dirty = true;
22647         if(!this.modified){
22648             this.modified = {};
22649         }
22650         if(typeof this.modified[name] == 'undefined'){
22651             this.modified[name] = this.data[name];
22652         }
22653         this.data[name] = value;
22654         if(!this.editing && this.store){
22655             this.store.afterEdit(this);
22656         }       
22657     },
22658
22659     /**
22660      * Get the value of the named field.
22661      * @param {String} name The name of the field to get the value of.
22662      * @return {Object} The value of the field.
22663      */
22664     get : function(name){
22665         return this.data[name]; 
22666     },
22667
22668     // private
22669     beginEdit : function(){
22670         this.editing = true;
22671         this.modified = {}; 
22672     },
22673
22674     // private
22675     cancelEdit : function(){
22676         this.editing = false;
22677         delete this.modified;
22678     },
22679
22680     // private
22681     endEdit : function(){
22682         this.editing = false;
22683         if(this.dirty && this.store){
22684             this.store.afterEdit(this);
22685         }
22686     },
22687
22688     /**
22689      * Usually called by the {@link Roo.data.Store} which owns the Record.
22690      * Rejects all changes made to the Record since either creation, or the last commit operation.
22691      * Modified fields are reverted to their original values.
22692      * <p>
22693      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22694      * of reject operations.
22695      */
22696     reject : function(){
22697         var m = this.modified;
22698         for(var n in m){
22699             if(typeof m[n] != "function"){
22700                 this.data[n] = m[n];
22701             }
22702         }
22703         this.dirty = false;
22704         delete this.modified;
22705         this.editing = false;
22706         if(this.store){
22707             this.store.afterReject(this);
22708         }
22709     },
22710
22711     /**
22712      * Usually called by the {@link Roo.data.Store} which owns the Record.
22713      * Commits all changes made to the Record since either creation, or the last commit operation.
22714      * <p>
22715      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22716      * of commit operations.
22717      */
22718     commit : function(){
22719         this.dirty = false;
22720         delete this.modified;
22721         this.editing = false;
22722         if(this.store){
22723             this.store.afterCommit(this);
22724         }
22725     },
22726
22727     // private
22728     hasError : function(){
22729         return this.error != null;
22730     },
22731
22732     // private
22733     clearError : function(){
22734         this.error = null;
22735     },
22736
22737     /**
22738      * Creates a copy of this record.
22739      * @param {String} id (optional) A new record id if you don't want to use this record's id
22740      * @return {Record}
22741      */
22742     copy : function(newId) {
22743         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22744     }
22745 };/*
22746  * Based on:
22747  * Ext JS Library 1.1.1
22748  * Copyright(c) 2006-2007, Ext JS, LLC.
22749  *
22750  * Originally Released Under LGPL - original licence link has changed is not relivant.
22751  *
22752  * Fork - LGPL
22753  * <script type="text/javascript">
22754  */
22755
22756
22757
22758 /**
22759  * @class Roo.data.Store
22760  * @extends Roo.util.Observable
22761  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22762  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22763  * <p>
22764  * 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
22765  * has no knowledge of the format of the data returned by the Proxy.<br>
22766  * <p>
22767  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22768  * instances from the data object. These records are cached and made available through accessor functions.
22769  * @constructor
22770  * Creates a new Store.
22771  * @param {Object} config A config object containing the objects needed for the Store to access data,
22772  * and read the data into Records.
22773  */
22774 Roo.data.Store = function(config){
22775     this.data = new Roo.util.MixedCollection(false);
22776     this.data.getKey = function(o){
22777         return o.id;
22778     };
22779     this.baseParams = {};
22780     // private
22781     this.paramNames = {
22782         "start" : "start",
22783         "limit" : "limit",
22784         "sort" : "sort",
22785         "dir" : "dir",
22786         "multisort" : "_multisort"
22787     };
22788
22789     if(config && config.data){
22790         this.inlineData = config.data;
22791         delete config.data;
22792     }
22793
22794     Roo.apply(this, config);
22795     
22796     if(this.reader){ // reader passed
22797         this.reader = Roo.factory(this.reader, Roo.data);
22798         this.reader.xmodule = this.xmodule || false;
22799         if(!this.recordType){
22800             this.recordType = this.reader.recordType;
22801         }
22802         if(this.reader.onMetaChange){
22803             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22804         }
22805     }
22806
22807     if(this.recordType){
22808         this.fields = this.recordType.prototype.fields;
22809     }
22810     this.modified = [];
22811
22812     this.addEvents({
22813         /**
22814          * @event datachanged
22815          * Fires when the data cache has changed, and a widget which is using this Store
22816          * as a Record cache should refresh its view.
22817          * @param {Store} this
22818          */
22819         datachanged : true,
22820         /**
22821          * @event metachange
22822          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22823          * @param {Store} this
22824          * @param {Object} meta The JSON metadata
22825          */
22826         metachange : true,
22827         /**
22828          * @event add
22829          * Fires when Records have been added to the Store
22830          * @param {Store} this
22831          * @param {Roo.data.Record[]} records The array of Records added
22832          * @param {Number} index The index at which the record(s) were added
22833          */
22834         add : true,
22835         /**
22836          * @event remove
22837          * Fires when a Record has been removed from the Store
22838          * @param {Store} this
22839          * @param {Roo.data.Record} record The Record that was removed
22840          * @param {Number} index The index at which the record was removed
22841          */
22842         remove : true,
22843         /**
22844          * @event update
22845          * Fires when a Record has been updated
22846          * @param {Store} this
22847          * @param {Roo.data.Record} record The Record that was updated
22848          * @param {String} operation The update operation being performed.  Value may be one of:
22849          * <pre><code>
22850  Roo.data.Record.EDIT
22851  Roo.data.Record.REJECT
22852  Roo.data.Record.COMMIT
22853          * </code></pre>
22854          */
22855         update : true,
22856         /**
22857          * @event clear
22858          * Fires when the data cache has been cleared.
22859          * @param {Store} this
22860          */
22861         clear : true,
22862         /**
22863          * @event beforeload
22864          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22865          * the load action will be canceled.
22866          * @param {Store} this
22867          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22868          */
22869         beforeload : true,
22870         /**
22871          * @event beforeloadadd
22872          * Fires after a new set of Records has been loaded.
22873          * @param {Store} this
22874          * @param {Roo.data.Record[]} records The Records that were loaded
22875          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22876          */
22877         beforeloadadd : true,
22878         /**
22879          * @event load
22880          * Fires after a new set of Records has been loaded, before they are added to the store.
22881          * @param {Store} this
22882          * @param {Roo.data.Record[]} records The Records that were loaded
22883          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22884          * @params {Object} return from reader
22885          */
22886         load : true,
22887         /**
22888          * @event loadexception
22889          * Fires if an exception occurs in the Proxy during loading.
22890          * Called with the signature of the Proxy's "loadexception" event.
22891          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22892          * 
22893          * @param {Proxy} 
22894          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22895          * @param {Object} load options 
22896          * @param {Object} jsonData from your request (normally this contains the Exception)
22897          */
22898         loadexception : true
22899     });
22900     
22901     if(this.proxy){
22902         this.proxy = Roo.factory(this.proxy, Roo.data);
22903         this.proxy.xmodule = this.xmodule || false;
22904         this.relayEvents(this.proxy,  ["loadexception"]);
22905     }
22906     this.sortToggle = {};
22907     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22908
22909     Roo.data.Store.superclass.constructor.call(this);
22910
22911     if(this.inlineData){
22912         this.loadData(this.inlineData);
22913         delete this.inlineData;
22914     }
22915 };
22916
22917 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22918      /**
22919     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22920     * without a remote query - used by combo/forms at present.
22921     */
22922     
22923     /**
22924     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22925     */
22926     /**
22927     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22928     */
22929     /**
22930     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22931     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22932     */
22933     /**
22934     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22935     * on any HTTP request
22936     */
22937     /**
22938     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22939     */
22940     /**
22941     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22942     */
22943     multiSort: false,
22944     /**
22945     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22946     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22947     */
22948     remoteSort : false,
22949
22950     /**
22951     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22952      * loaded or when a record is removed. (defaults to false).
22953     */
22954     pruneModifiedRecords : false,
22955
22956     // private
22957     lastOptions : null,
22958
22959     /**
22960      * Add Records to the Store and fires the add event.
22961      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22962      */
22963     add : function(records){
22964         records = [].concat(records);
22965         for(var i = 0, len = records.length; i < len; i++){
22966             records[i].join(this);
22967         }
22968         var index = this.data.length;
22969         this.data.addAll(records);
22970         this.fireEvent("add", this, records, index);
22971     },
22972
22973     /**
22974      * Remove a Record from the Store and fires the remove event.
22975      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22976      */
22977     remove : function(record){
22978         var index = this.data.indexOf(record);
22979         this.data.removeAt(index);
22980  
22981         if(this.pruneModifiedRecords){
22982             this.modified.remove(record);
22983         }
22984         this.fireEvent("remove", this, record, index);
22985     },
22986
22987     /**
22988      * Remove all Records from the Store and fires the clear event.
22989      */
22990     removeAll : function(){
22991         this.data.clear();
22992         if(this.pruneModifiedRecords){
22993             this.modified = [];
22994         }
22995         this.fireEvent("clear", this);
22996     },
22997
22998     /**
22999      * Inserts Records to the Store at the given index and fires the add event.
23000      * @param {Number} index The start index at which to insert the passed Records.
23001      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23002      */
23003     insert : function(index, records){
23004         records = [].concat(records);
23005         for(var i = 0, len = records.length; i < len; i++){
23006             this.data.insert(index, records[i]);
23007             records[i].join(this);
23008         }
23009         this.fireEvent("add", this, records, index);
23010     },
23011
23012     /**
23013      * Get the index within the cache of the passed Record.
23014      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23015      * @return {Number} The index of the passed Record. Returns -1 if not found.
23016      */
23017     indexOf : function(record){
23018         return this.data.indexOf(record);
23019     },
23020
23021     /**
23022      * Get the index within the cache of the Record with the passed id.
23023      * @param {String} id The id of the Record to find.
23024      * @return {Number} The index of the Record. Returns -1 if not found.
23025      */
23026     indexOfId : function(id){
23027         return this.data.indexOfKey(id);
23028     },
23029
23030     /**
23031      * Get the Record with the specified id.
23032      * @param {String} id The id of the Record to find.
23033      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23034      */
23035     getById : function(id){
23036         return this.data.key(id);
23037     },
23038
23039     /**
23040      * Get the Record at the specified index.
23041      * @param {Number} index The index of the Record to find.
23042      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23043      */
23044     getAt : function(index){
23045         return this.data.itemAt(index);
23046     },
23047
23048     /**
23049      * Returns a range of Records between specified indices.
23050      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23051      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23052      * @return {Roo.data.Record[]} An array of Records
23053      */
23054     getRange : function(start, end){
23055         return this.data.getRange(start, end);
23056     },
23057
23058     // private
23059     storeOptions : function(o){
23060         o = Roo.apply({}, o);
23061         delete o.callback;
23062         delete o.scope;
23063         this.lastOptions = o;
23064     },
23065
23066     /**
23067      * Loads the Record cache from the configured Proxy using the configured Reader.
23068      * <p>
23069      * If using remote paging, then the first load call must specify the <em>start</em>
23070      * and <em>limit</em> properties in the options.params property to establish the initial
23071      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23072      * <p>
23073      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23074      * and this call will return before the new data has been loaded. Perform any post-processing
23075      * in a callback function, or in a "load" event handler.</strong>
23076      * <p>
23077      * @param {Object} options An object containing properties which control loading options:<ul>
23078      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23079      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23080      * passed the following arguments:<ul>
23081      * <li>r : Roo.data.Record[]</li>
23082      * <li>options: Options object from the load call</li>
23083      * <li>success: Boolean success indicator</li></ul></li>
23084      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23085      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23086      * </ul>
23087      */
23088     load : function(options){
23089         options = options || {};
23090         if(this.fireEvent("beforeload", this, options) !== false){
23091             this.storeOptions(options);
23092             var p = Roo.apply(options.params || {}, this.baseParams);
23093             // if meta was not loaded from remote source.. try requesting it.
23094             if (!this.reader.metaFromRemote) {
23095                 p._requestMeta = 1;
23096             }
23097             if(this.sortInfo && this.remoteSort){
23098                 var pn = this.paramNames;
23099                 p[pn["sort"]] = this.sortInfo.field;
23100                 p[pn["dir"]] = this.sortInfo.direction;
23101             }
23102             if (this.multiSort) {
23103                 var pn = this.paramNames;
23104                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23105             }
23106             
23107             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23108         }
23109     },
23110
23111     /**
23112      * Reloads the Record cache from the configured Proxy using the configured Reader and
23113      * the options from the last load operation performed.
23114      * @param {Object} options (optional) An object containing properties which may override the options
23115      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23116      * the most recently used options are reused).
23117      */
23118     reload : function(options){
23119         this.load(Roo.applyIf(options||{}, this.lastOptions));
23120     },
23121
23122     // private
23123     // Called as a callback by the Reader during a load operation.
23124     loadRecords : function(o, options, success){
23125         if(!o || success === false){
23126             if(success !== false){
23127                 this.fireEvent("load", this, [], options, o);
23128             }
23129             if(options.callback){
23130                 options.callback.call(options.scope || this, [], options, false);
23131             }
23132             return;
23133         }
23134         // if data returned failure - throw an exception.
23135         if (o.success === false) {
23136             // show a message if no listener is registered.
23137             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23138                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23139             }
23140             // loadmask wil be hooked into this..
23141             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23142             return;
23143         }
23144         var r = o.records, t = o.totalRecords || r.length;
23145         
23146         this.fireEvent("beforeloadadd", this, r, options, o);
23147         
23148         if(!options || options.add !== true){
23149             if(this.pruneModifiedRecords){
23150                 this.modified = [];
23151             }
23152             for(var i = 0, len = r.length; i < len; i++){
23153                 r[i].join(this);
23154             }
23155             if(this.snapshot){
23156                 this.data = this.snapshot;
23157                 delete this.snapshot;
23158             }
23159             this.data.clear();
23160             this.data.addAll(r);
23161             this.totalLength = t;
23162             this.applySort();
23163             this.fireEvent("datachanged", this);
23164         }else{
23165             this.totalLength = Math.max(t, this.data.length+r.length);
23166             this.add(r);
23167         }
23168         
23169         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23170                 
23171             var e = new Roo.data.Record({});
23172
23173             e.set(this.parent.displayField, this.parent.emptyTitle);
23174             e.set(this.parent.valueField, '');
23175
23176             this.insert(0, e);
23177         }
23178             
23179         this.fireEvent("load", this, r, options, o);
23180         if(options.callback){
23181             options.callback.call(options.scope || this, r, options, true);
23182         }
23183     },
23184
23185
23186     /**
23187      * Loads data from a passed data block. A Reader which understands the format of the data
23188      * must have been configured in the constructor.
23189      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23190      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23191      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23192      */
23193     loadData : function(o, append){
23194         var r = this.reader.readRecords(o);
23195         this.loadRecords(r, {add: append}, true);
23196     },
23197
23198     /**
23199      * Gets the number of cached records.
23200      * <p>
23201      * <em>If using paging, this may not be the total size of the dataset. If the data object
23202      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23203      * the data set size</em>
23204      */
23205     getCount : function(){
23206         return this.data.length || 0;
23207     },
23208
23209     /**
23210      * Gets the total number of records in the dataset as returned by the server.
23211      * <p>
23212      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23213      * the dataset size</em>
23214      */
23215     getTotalCount : function(){
23216         return this.totalLength || 0;
23217     },
23218
23219     /**
23220      * Returns the sort state of the Store as an object with two properties:
23221      * <pre><code>
23222  field {String} The name of the field by which the Records are sorted
23223  direction {String} The sort order, "ASC" or "DESC"
23224      * </code></pre>
23225      */
23226     getSortState : function(){
23227         return this.sortInfo;
23228     },
23229
23230     // private
23231     applySort : function(){
23232         if(this.sortInfo && !this.remoteSort){
23233             var s = this.sortInfo, f = s.field;
23234             var st = this.fields.get(f).sortType;
23235             var fn = function(r1, r2){
23236                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23237                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23238             };
23239             this.data.sort(s.direction, fn);
23240             if(this.snapshot && this.snapshot != this.data){
23241                 this.snapshot.sort(s.direction, fn);
23242             }
23243         }
23244     },
23245
23246     /**
23247      * Sets the default sort column and order to be used by the next load operation.
23248      * @param {String} fieldName The name of the field to sort by.
23249      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23250      */
23251     setDefaultSort : function(field, dir){
23252         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23253     },
23254
23255     /**
23256      * Sort the Records.
23257      * If remote sorting is used, the sort is performed on the server, and the cache is
23258      * reloaded. If local sorting is used, the cache is sorted internally.
23259      * @param {String} fieldName The name of the field to sort by.
23260      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23261      */
23262     sort : function(fieldName, dir){
23263         var f = this.fields.get(fieldName);
23264         if(!dir){
23265             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23266             
23267             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23268                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23269             }else{
23270                 dir = f.sortDir;
23271             }
23272         }
23273         this.sortToggle[f.name] = dir;
23274         this.sortInfo = {field: f.name, direction: dir};
23275         if(!this.remoteSort){
23276             this.applySort();
23277             this.fireEvent("datachanged", this);
23278         }else{
23279             this.load(this.lastOptions);
23280         }
23281     },
23282
23283     /**
23284      * Calls the specified function for each of the Records in the cache.
23285      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23286      * Returning <em>false</em> aborts and exits the iteration.
23287      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23288      */
23289     each : function(fn, scope){
23290         this.data.each(fn, scope);
23291     },
23292
23293     /**
23294      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23295      * (e.g., during paging).
23296      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23297      */
23298     getModifiedRecords : function(){
23299         return this.modified;
23300     },
23301
23302     // private
23303     createFilterFn : function(property, value, anyMatch){
23304         if(!value.exec){ // not a regex
23305             value = String(value);
23306             if(value.length == 0){
23307                 return false;
23308             }
23309             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23310         }
23311         return function(r){
23312             return value.test(r.data[property]);
23313         };
23314     },
23315
23316     /**
23317      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23318      * @param {String} property A field on your records
23319      * @param {Number} start The record index to start at (defaults to 0)
23320      * @param {Number} end The last record index to include (defaults to length - 1)
23321      * @return {Number} The sum
23322      */
23323     sum : function(property, start, end){
23324         var rs = this.data.items, v = 0;
23325         start = start || 0;
23326         end = (end || end === 0) ? end : rs.length-1;
23327
23328         for(var i = start; i <= end; i++){
23329             v += (rs[i].data[property] || 0);
23330         }
23331         return v;
23332     },
23333
23334     /**
23335      * Filter the records by a specified property.
23336      * @param {String} field A field on your records
23337      * @param {String/RegExp} value Either a string that the field
23338      * should start with or a RegExp to test against the field
23339      * @param {Boolean} anyMatch True to match any part not just the beginning
23340      */
23341     filter : function(property, value, anyMatch){
23342         var fn = this.createFilterFn(property, value, anyMatch);
23343         return fn ? this.filterBy(fn) : this.clearFilter();
23344     },
23345
23346     /**
23347      * Filter by a function. The specified function will be called with each
23348      * record in this data source. If the function returns true the record is included,
23349      * otherwise it is filtered.
23350      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23351      * @param {Object} scope (optional) The scope of the function (defaults to this)
23352      */
23353     filterBy : function(fn, scope){
23354         this.snapshot = this.snapshot || this.data;
23355         this.data = this.queryBy(fn, scope||this);
23356         this.fireEvent("datachanged", this);
23357     },
23358
23359     /**
23360      * Query the records by a specified property.
23361      * @param {String} field A field on your records
23362      * @param {String/RegExp} value Either a string that the field
23363      * should start with or a RegExp to test against the field
23364      * @param {Boolean} anyMatch True to match any part not just the beginning
23365      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23366      */
23367     query : function(property, value, anyMatch){
23368         var fn = this.createFilterFn(property, value, anyMatch);
23369         return fn ? this.queryBy(fn) : this.data.clone();
23370     },
23371
23372     /**
23373      * Query by a function. The specified function will be called with each
23374      * record in this data source. If the function returns true the record is included
23375      * in the results.
23376      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23377      * @param {Object} scope (optional) The scope of the function (defaults to this)
23378       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23379      **/
23380     queryBy : function(fn, scope){
23381         var data = this.snapshot || this.data;
23382         return data.filterBy(fn, scope||this);
23383     },
23384
23385     /**
23386      * Collects unique values for a particular dataIndex from this store.
23387      * @param {String} dataIndex The property to collect
23388      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23389      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23390      * @return {Array} An array of the unique values
23391      **/
23392     collect : function(dataIndex, allowNull, bypassFilter){
23393         var d = (bypassFilter === true && this.snapshot) ?
23394                 this.snapshot.items : this.data.items;
23395         var v, sv, r = [], l = {};
23396         for(var i = 0, len = d.length; i < len; i++){
23397             v = d[i].data[dataIndex];
23398             sv = String(v);
23399             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23400                 l[sv] = true;
23401                 r[r.length] = v;
23402             }
23403         }
23404         return r;
23405     },
23406
23407     /**
23408      * Revert to a view of the Record cache with no filtering applied.
23409      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23410      */
23411     clearFilter : function(suppressEvent){
23412         if(this.snapshot && this.snapshot != this.data){
23413             this.data = this.snapshot;
23414             delete this.snapshot;
23415             if(suppressEvent !== true){
23416                 this.fireEvent("datachanged", this);
23417             }
23418         }
23419     },
23420
23421     // private
23422     afterEdit : function(record){
23423         if(this.modified.indexOf(record) == -1){
23424             this.modified.push(record);
23425         }
23426         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23427     },
23428     
23429     // private
23430     afterReject : function(record){
23431         this.modified.remove(record);
23432         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23433     },
23434
23435     // private
23436     afterCommit : function(record){
23437         this.modified.remove(record);
23438         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23439     },
23440
23441     /**
23442      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23443      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23444      */
23445     commitChanges : function(){
23446         var m = this.modified.slice(0);
23447         this.modified = [];
23448         for(var i = 0, len = m.length; i < len; i++){
23449             m[i].commit();
23450         }
23451     },
23452
23453     /**
23454      * Cancel outstanding changes on all changed records.
23455      */
23456     rejectChanges : function(){
23457         var m = this.modified.slice(0);
23458         this.modified = [];
23459         for(var i = 0, len = m.length; i < len; i++){
23460             m[i].reject();
23461         }
23462     },
23463
23464     onMetaChange : function(meta, rtype, o){
23465         this.recordType = rtype;
23466         this.fields = rtype.prototype.fields;
23467         delete this.snapshot;
23468         this.sortInfo = meta.sortInfo || this.sortInfo;
23469         this.modified = [];
23470         this.fireEvent('metachange', this, this.reader.meta);
23471     },
23472     
23473     moveIndex : function(data, type)
23474     {
23475         var index = this.indexOf(data);
23476         
23477         var newIndex = index + type;
23478         
23479         this.remove(data);
23480         
23481         this.insert(newIndex, data);
23482         
23483     }
23484 });/*
23485  * Based on:
23486  * Ext JS Library 1.1.1
23487  * Copyright(c) 2006-2007, Ext JS, LLC.
23488  *
23489  * Originally Released Under LGPL - original licence link has changed is not relivant.
23490  *
23491  * Fork - LGPL
23492  * <script type="text/javascript">
23493  */
23494
23495 /**
23496  * @class Roo.data.SimpleStore
23497  * @extends Roo.data.Store
23498  * Small helper class to make creating Stores from Array data easier.
23499  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23500  * @cfg {Array} fields An array of field definition objects, or field name strings.
23501  * @cfg {Array} data The multi-dimensional array of data
23502  * @constructor
23503  * @param {Object} config
23504  */
23505 Roo.data.SimpleStore = function(config){
23506     Roo.data.SimpleStore.superclass.constructor.call(this, {
23507         isLocal : true,
23508         reader: new Roo.data.ArrayReader({
23509                 id: config.id
23510             },
23511             Roo.data.Record.create(config.fields)
23512         ),
23513         proxy : new Roo.data.MemoryProxy(config.data)
23514     });
23515     this.load();
23516 };
23517 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23518  * Based on:
23519  * Ext JS Library 1.1.1
23520  * Copyright(c) 2006-2007, Ext JS, LLC.
23521  *
23522  * Originally Released Under LGPL - original licence link has changed is not relivant.
23523  *
23524  * Fork - LGPL
23525  * <script type="text/javascript">
23526  */
23527
23528 /**
23529 /**
23530  * @extends Roo.data.Store
23531  * @class Roo.data.JsonStore
23532  * Small helper class to make creating Stores for JSON data easier. <br/>
23533 <pre><code>
23534 var store = new Roo.data.JsonStore({
23535     url: 'get-images.php',
23536     root: 'images',
23537     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23538 });
23539 </code></pre>
23540  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23541  * JsonReader and HttpProxy (unless inline data is provided).</b>
23542  * @cfg {Array} fields An array of field definition objects, or field name strings.
23543  * @constructor
23544  * @param {Object} config
23545  */
23546 Roo.data.JsonStore = function(c){
23547     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23548         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23549         reader: new Roo.data.JsonReader(c, c.fields)
23550     }));
23551 };
23552 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23553  * Based on:
23554  * Ext JS Library 1.1.1
23555  * Copyright(c) 2006-2007, Ext JS, LLC.
23556  *
23557  * Originally Released Under LGPL - original licence link has changed is not relivant.
23558  *
23559  * Fork - LGPL
23560  * <script type="text/javascript">
23561  */
23562
23563  
23564 Roo.data.Field = function(config){
23565     if(typeof config == "string"){
23566         config = {name: config};
23567     }
23568     Roo.apply(this, config);
23569     
23570     if(!this.type){
23571         this.type = "auto";
23572     }
23573     
23574     var st = Roo.data.SortTypes;
23575     // named sortTypes are supported, here we look them up
23576     if(typeof this.sortType == "string"){
23577         this.sortType = st[this.sortType];
23578     }
23579     
23580     // set default sortType for strings and dates
23581     if(!this.sortType){
23582         switch(this.type){
23583             case "string":
23584                 this.sortType = st.asUCString;
23585                 break;
23586             case "date":
23587                 this.sortType = st.asDate;
23588                 break;
23589             default:
23590                 this.sortType = st.none;
23591         }
23592     }
23593
23594     // define once
23595     var stripRe = /[\$,%]/g;
23596
23597     // prebuilt conversion function for this field, instead of
23598     // switching every time we're reading a value
23599     if(!this.convert){
23600         var cv, dateFormat = this.dateFormat;
23601         switch(this.type){
23602             case "":
23603             case "auto":
23604             case undefined:
23605                 cv = function(v){ return v; };
23606                 break;
23607             case "string":
23608                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23609                 break;
23610             case "int":
23611                 cv = function(v){
23612                     return v !== undefined && v !== null && v !== '' ?
23613                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23614                     };
23615                 break;
23616             case "float":
23617                 cv = function(v){
23618                     return v !== undefined && v !== null && v !== '' ?
23619                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23620                     };
23621                 break;
23622             case "bool":
23623             case "boolean":
23624                 cv = function(v){ return v === true || v === "true" || v == 1; };
23625                 break;
23626             case "date":
23627                 cv = function(v){
23628                     if(!v){
23629                         return '';
23630                     }
23631                     if(v instanceof Date){
23632                         return v;
23633                     }
23634                     if(dateFormat){
23635                         if(dateFormat == "timestamp"){
23636                             return new Date(v*1000);
23637                         }
23638                         return Date.parseDate(v, dateFormat);
23639                     }
23640                     var parsed = Date.parse(v);
23641                     return parsed ? new Date(parsed) : null;
23642                 };
23643              break;
23644             
23645         }
23646         this.convert = cv;
23647     }
23648 };
23649
23650 Roo.data.Field.prototype = {
23651     dateFormat: null,
23652     defaultValue: "",
23653     mapping: null,
23654     sortType : null,
23655     sortDir : "ASC"
23656 };/*
23657  * Based on:
23658  * Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  *
23661  * Originally Released Under LGPL - original licence link has changed is not relivant.
23662  *
23663  * Fork - LGPL
23664  * <script type="text/javascript">
23665  */
23666  
23667 // Base class for reading structured data from a data source.  This class is intended to be
23668 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23669
23670 /**
23671  * @class Roo.data.DataReader
23672  * Base class for reading structured data from a data source.  This class is intended to be
23673  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23674  */
23675
23676 Roo.data.DataReader = function(meta, recordType){
23677     
23678     this.meta = meta;
23679     
23680     this.recordType = recordType instanceof Array ? 
23681         Roo.data.Record.create(recordType) : recordType;
23682 };
23683
23684 Roo.data.DataReader.prototype = {
23685      /**
23686      * Create an empty record
23687      * @param {Object} data (optional) - overlay some values
23688      * @return {Roo.data.Record} record created.
23689      */
23690     newRow :  function(d) {
23691         var da =  {};
23692         this.recordType.prototype.fields.each(function(c) {
23693             switch( c.type) {
23694                 case 'int' : da[c.name] = 0; break;
23695                 case 'date' : da[c.name] = new Date(); break;
23696                 case 'float' : da[c.name] = 0.0; break;
23697                 case 'boolean' : da[c.name] = false; break;
23698                 default : da[c.name] = ""; break;
23699             }
23700             
23701         });
23702         return new this.recordType(Roo.apply(da, d));
23703     }
23704     
23705 };/*
23706  * Based on:
23707  * Ext JS Library 1.1.1
23708  * Copyright(c) 2006-2007, Ext JS, LLC.
23709  *
23710  * Originally Released Under LGPL - original licence link has changed is not relivant.
23711  *
23712  * Fork - LGPL
23713  * <script type="text/javascript">
23714  */
23715
23716 /**
23717  * @class Roo.data.DataProxy
23718  * @extends Roo.data.Observable
23719  * This class is an abstract base class for implementations which provide retrieval of
23720  * unformatted data objects.<br>
23721  * <p>
23722  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23723  * (of the appropriate type which knows how to parse the data object) to provide a block of
23724  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23725  * <p>
23726  * Custom implementations must implement the load method as described in
23727  * {@link Roo.data.HttpProxy#load}.
23728  */
23729 Roo.data.DataProxy = function(){
23730     this.addEvents({
23731         /**
23732          * @event beforeload
23733          * Fires before a network request is made to retrieve a data object.
23734          * @param {Object} This DataProxy object.
23735          * @param {Object} params The params parameter to the load function.
23736          */
23737         beforeload : true,
23738         /**
23739          * @event load
23740          * Fires before the load method's callback is called.
23741          * @param {Object} This DataProxy object.
23742          * @param {Object} o The data object.
23743          * @param {Object} arg The callback argument object passed to the load function.
23744          */
23745         load : true,
23746         /**
23747          * @event loadexception
23748          * Fires if an Exception occurs during data retrieval.
23749          * @param {Object} This DataProxy object.
23750          * @param {Object} o The data object.
23751          * @param {Object} arg The callback argument object passed to the load function.
23752          * @param {Object} e The Exception.
23753          */
23754         loadexception : true
23755     });
23756     Roo.data.DataProxy.superclass.constructor.call(this);
23757 };
23758
23759 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23760
23761     /**
23762      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23763      */
23764 /*
23765  * Based on:
23766  * Ext JS Library 1.1.1
23767  * Copyright(c) 2006-2007, Ext JS, LLC.
23768  *
23769  * Originally Released Under LGPL - original licence link has changed is not relivant.
23770  *
23771  * Fork - LGPL
23772  * <script type="text/javascript">
23773  */
23774 /**
23775  * @class Roo.data.MemoryProxy
23776  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23777  * to the Reader when its load method is called.
23778  * @constructor
23779  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23780  */
23781 Roo.data.MemoryProxy = function(data){
23782     if (data.data) {
23783         data = data.data;
23784     }
23785     Roo.data.MemoryProxy.superclass.constructor.call(this);
23786     this.data = data;
23787 };
23788
23789 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23790     
23791     /**
23792      * Load data from the requested source (in this case an in-memory
23793      * data object passed to the constructor), read the data object into
23794      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23795      * process that block using the passed callback.
23796      * @param {Object} params This parameter is not used by the MemoryProxy class.
23797      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23798      * object into a block of Roo.data.Records.
23799      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23800      * The function must be passed <ul>
23801      * <li>The Record block object</li>
23802      * <li>The "arg" argument from the load function</li>
23803      * <li>A boolean success indicator</li>
23804      * </ul>
23805      * @param {Object} scope The scope in which to call the callback
23806      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23807      */
23808     load : function(params, reader, callback, scope, arg){
23809         params = params || {};
23810         var result;
23811         try {
23812             result = reader.readRecords(this.data);
23813         }catch(e){
23814             this.fireEvent("loadexception", this, arg, null, e);
23815             callback.call(scope, null, arg, false);
23816             return;
23817         }
23818         callback.call(scope, result, arg, true);
23819     },
23820     
23821     // private
23822     update : function(params, records){
23823         
23824     }
23825 });/*
23826  * Based on:
23827  * Ext JS Library 1.1.1
23828  * Copyright(c) 2006-2007, Ext JS, LLC.
23829  *
23830  * Originally Released Under LGPL - original licence link has changed is not relivant.
23831  *
23832  * Fork - LGPL
23833  * <script type="text/javascript">
23834  */
23835 /**
23836  * @class Roo.data.HttpProxy
23837  * @extends Roo.data.DataProxy
23838  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23839  * configured to reference a certain URL.<br><br>
23840  * <p>
23841  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23842  * from which the running page was served.<br><br>
23843  * <p>
23844  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23845  * <p>
23846  * Be aware that to enable the browser to parse an XML document, the server must set
23847  * the Content-Type header in the HTTP response to "text/xml".
23848  * @constructor
23849  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23850  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23851  * will be used to make the request.
23852  */
23853 Roo.data.HttpProxy = function(conn){
23854     Roo.data.HttpProxy.superclass.constructor.call(this);
23855     // is conn a conn config or a real conn?
23856     this.conn = conn;
23857     this.useAjax = !conn || !conn.events;
23858   
23859 };
23860
23861 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23862     // thse are take from connection...
23863     
23864     /**
23865      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23866      */
23867     /**
23868      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23869      * extra parameters to each request made by this object. (defaults to undefined)
23870      */
23871     /**
23872      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23873      *  to each request made by this object. (defaults to undefined)
23874      */
23875     /**
23876      * @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)
23877      */
23878     /**
23879      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23880      */
23881      /**
23882      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23883      * @type Boolean
23884      */
23885   
23886
23887     /**
23888      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23889      * @type Boolean
23890      */
23891     /**
23892      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23893      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23894      * a finer-grained basis than the DataProxy events.
23895      */
23896     getConnection : function(){
23897         return this.useAjax ? Roo.Ajax : this.conn;
23898     },
23899
23900     /**
23901      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23902      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23903      * process that block using the passed callback.
23904      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23905      * for the request to the remote server.
23906      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23907      * object into a block of Roo.data.Records.
23908      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23909      * The function must be passed <ul>
23910      * <li>The Record block object</li>
23911      * <li>The "arg" argument from the load function</li>
23912      * <li>A boolean success indicator</li>
23913      * </ul>
23914      * @param {Object} scope The scope in which to call the callback
23915      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23916      */
23917     load : function(params, reader, callback, scope, arg){
23918         if(this.fireEvent("beforeload", this, params) !== false){
23919             var  o = {
23920                 params : params || {},
23921                 request: {
23922                     callback : callback,
23923                     scope : scope,
23924                     arg : arg
23925                 },
23926                 reader: reader,
23927                 callback : this.loadResponse,
23928                 scope: this
23929             };
23930             if(this.useAjax){
23931                 Roo.applyIf(o, this.conn);
23932                 if(this.activeRequest){
23933                     Roo.Ajax.abort(this.activeRequest);
23934                 }
23935                 this.activeRequest = Roo.Ajax.request(o);
23936             }else{
23937                 this.conn.request(o);
23938             }
23939         }else{
23940             callback.call(scope||this, null, arg, false);
23941         }
23942     },
23943
23944     // private
23945     loadResponse : function(o, success, response){
23946         delete this.activeRequest;
23947         if(!success){
23948             this.fireEvent("loadexception", this, o, response);
23949             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23950             return;
23951         }
23952         var result;
23953         try {
23954             result = o.reader.read(response);
23955         }catch(e){
23956             this.fireEvent("loadexception", this, o, response, e);
23957             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23958             return;
23959         }
23960         
23961         this.fireEvent("load", this, o, o.request.arg);
23962         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23963     },
23964
23965     // private
23966     update : function(dataSet){
23967
23968     },
23969
23970     // private
23971     updateResponse : function(dataSet){
23972
23973     }
23974 });/*
23975  * Based on:
23976  * Ext JS Library 1.1.1
23977  * Copyright(c) 2006-2007, Ext JS, LLC.
23978  *
23979  * Originally Released Under LGPL - original licence link has changed is not relivant.
23980  *
23981  * Fork - LGPL
23982  * <script type="text/javascript">
23983  */
23984
23985 /**
23986  * @class Roo.data.ScriptTagProxy
23987  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23988  * other than the originating domain of the running page.<br><br>
23989  * <p>
23990  * <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
23991  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23992  * <p>
23993  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23994  * source code that is used as the source inside a &lt;script> tag.<br><br>
23995  * <p>
23996  * In order for the browser to process the returned data, the server must wrap the data object
23997  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23998  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23999  * depending on whether the callback name was passed:
24000  * <p>
24001  * <pre><code>
24002 boolean scriptTag = false;
24003 String cb = request.getParameter("callback");
24004 if (cb != null) {
24005     scriptTag = true;
24006     response.setContentType("text/javascript");
24007 } else {
24008     response.setContentType("application/x-json");
24009 }
24010 Writer out = response.getWriter();
24011 if (scriptTag) {
24012     out.write(cb + "(");
24013 }
24014 out.print(dataBlock.toJsonString());
24015 if (scriptTag) {
24016     out.write(");");
24017 }
24018 </pre></code>
24019  *
24020  * @constructor
24021  * @param {Object} config A configuration object.
24022  */
24023 Roo.data.ScriptTagProxy = function(config){
24024     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24025     Roo.apply(this, config);
24026     this.head = document.getElementsByTagName("head")[0];
24027 };
24028
24029 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24030
24031 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24032     /**
24033      * @cfg {String} url The URL from which to request the data object.
24034      */
24035     /**
24036      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24037      */
24038     timeout : 30000,
24039     /**
24040      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24041      * the server the name of the callback function set up by the load call to process the returned data object.
24042      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24043      * javascript output which calls this named function passing the data object as its only parameter.
24044      */
24045     callbackParam : "callback",
24046     /**
24047      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24048      * name to the request.
24049      */
24050     nocache : true,
24051
24052     /**
24053      * Load data from the configured URL, read the data object into
24054      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24055      * process that block using the passed callback.
24056      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24057      * for the request to the remote server.
24058      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24059      * object into a block of Roo.data.Records.
24060      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24061      * The function must be passed <ul>
24062      * <li>The Record block object</li>
24063      * <li>The "arg" argument from the load function</li>
24064      * <li>A boolean success indicator</li>
24065      * </ul>
24066      * @param {Object} scope The scope in which to call the callback
24067      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24068      */
24069     load : function(params, reader, callback, scope, arg){
24070         if(this.fireEvent("beforeload", this, params) !== false){
24071
24072             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24073
24074             var url = this.url;
24075             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24076             if(this.nocache){
24077                 url += "&_dc=" + (new Date().getTime());
24078             }
24079             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24080             var trans = {
24081                 id : transId,
24082                 cb : "stcCallback"+transId,
24083                 scriptId : "stcScript"+transId,
24084                 params : params,
24085                 arg : arg,
24086                 url : url,
24087                 callback : callback,
24088                 scope : scope,
24089                 reader : reader
24090             };
24091             var conn = this;
24092
24093             window[trans.cb] = function(o){
24094                 conn.handleResponse(o, trans);
24095             };
24096
24097             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24098
24099             if(this.autoAbort !== false){
24100                 this.abort();
24101             }
24102
24103             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24104
24105             var script = document.createElement("script");
24106             script.setAttribute("src", url);
24107             script.setAttribute("type", "text/javascript");
24108             script.setAttribute("id", trans.scriptId);
24109             this.head.appendChild(script);
24110
24111             this.trans = trans;
24112         }else{
24113             callback.call(scope||this, null, arg, false);
24114         }
24115     },
24116
24117     // private
24118     isLoading : function(){
24119         return this.trans ? true : false;
24120     },
24121
24122     /**
24123      * Abort the current server request.
24124      */
24125     abort : function(){
24126         if(this.isLoading()){
24127             this.destroyTrans(this.trans);
24128         }
24129     },
24130
24131     // private
24132     destroyTrans : function(trans, isLoaded){
24133         this.head.removeChild(document.getElementById(trans.scriptId));
24134         clearTimeout(trans.timeoutId);
24135         if(isLoaded){
24136             window[trans.cb] = undefined;
24137             try{
24138                 delete window[trans.cb];
24139             }catch(e){}
24140         }else{
24141             // if hasn't been loaded, wait for load to remove it to prevent script error
24142             window[trans.cb] = function(){
24143                 window[trans.cb] = undefined;
24144                 try{
24145                     delete window[trans.cb];
24146                 }catch(e){}
24147             };
24148         }
24149     },
24150
24151     // private
24152     handleResponse : function(o, trans){
24153         this.trans = false;
24154         this.destroyTrans(trans, true);
24155         var result;
24156         try {
24157             result = trans.reader.readRecords(o);
24158         }catch(e){
24159             this.fireEvent("loadexception", this, o, trans.arg, e);
24160             trans.callback.call(trans.scope||window, null, trans.arg, false);
24161             return;
24162         }
24163         this.fireEvent("load", this, o, trans.arg);
24164         trans.callback.call(trans.scope||window, result, trans.arg, true);
24165     },
24166
24167     // private
24168     handleFailure : function(trans){
24169         this.trans = false;
24170         this.destroyTrans(trans, false);
24171         this.fireEvent("loadexception", this, null, trans.arg);
24172         trans.callback.call(trans.scope||window, null, trans.arg, false);
24173     }
24174 });/*
24175  * Based on:
24176  * Ext JS Library 1.1.1
24177  * Copyright(c) 2006-2007, Ext JS, LLC.
24178  *
24179  * Originally Released Under LGPL - original licence link has changed is not relivant.
24180  *
24181  * Fork - LGPL
24182  * <script type="text/javascript">
24183  */
24184
24185 /**
24186  * @class Roo.data.JsonReader
24187  * @extends Roo.data.DataReader
24188  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24189  * based on mappings in a provided Roo.data.Record constructor.
24190  * 
24191  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24192  * in the reply previously. 
24193  * 
24194  * <p>
24195  * Example code:
24196  * <pre><code>
24197 var RecordDef = Roo.data.Record.create([
24198     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24199     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24200 ]);
24201 var myReader = new Roo.data.JsonReader({
24202     totalProperty: "results",    // The property which contains the total dataset size (optional)
24203     root: "rows",                // The property which contains an Array of row objects
24204     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24205 }, RecordDef);
24206 </code></pre>
24207  * <p>
24208  * This would consume a JSON file like this:
24209  * <pre><code>
24210 { 'results': 2, 'rows': [
24211     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24212     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24213 }
24214 </code></pre>
24215  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24216  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24217  * paged from the remote server.
24218  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24219  * @cfg {String} root name of the property which contains the Array of row objects.
24220  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24221  * @cfg {Array} fields Array of field definition objects
24222  * @constructor
24223  * Create a new JsonReader
24224  * @param {Object} meta Metadata configuration options
24225  * @param {Object} recordType Either an Array of field definition objects,
24226  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24227  */
24228 Roo.data.JsonReader = function(meta, recordType){
24229     
24230     meta = meta || {};
24231     // set some defaults:
24232     Roo.applyIf(meta, {
24233         totalProperty: 'total',
24234         successProperty : 'success',
24235         root : 'data',
24236         id : 'id'
24237     });
24238     
24239     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24240 };
24241 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24242     
24243     /**
24244      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24245      * Used by Store query builder to append _requestMeta to params.
24246      * 
24247      */
24248     metaFromRemote : false,
24249     /**
24250      * This method is only used by a DataProxy which has retrieved data from a remote server.
24251      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24252      * @return {Object} data A data block which is used by an Roo.data.Store object as
24253      * a cache of Roo.data.Records.
24254      */
24255     read : function(response){
24256         var json = response.responseText;
24257        
24258         var o = /* eval:var:o */ eval("("+json+")");
24259         if(!o) {
24260             throw {message: "JsonReader.read: Json object not found"};
24261         }
24262         
24263         if(o.metaData){
24264             
24265             delete this.ef;
24266             this.metaFromRemote = true;
24267             this.meta = o.metaData;
24268             this.recordType = Roo.data.Record.create(o.metaData.fields);
24269             this.onMetaChange(this.meta, this.recordType, o);
24270         }
24271         return this.readRecords(o);
24272     },
24273
24274     // private function a store will implement
24275     onMetaChange : function(meta, recordType, o){
24276
24277     },
24278
24279     /**
24280          * @ignore
24281          */
24282     simpleAccess: function(obj, subsc) {
24283         return obj[subsc];
24284     },
24285
24286         /**
24287          * @ignore
24288          */
24289     getJsonAccessor: function(){
24290         var re = /[\[\.]/;
24291         return function(expr) {
24292             try {
24293                 return(re.test(expr))
24294                     ? new Function("obj", "return obj." + expr)
24295                     : function(obj){
24296                         return obj[expr];
24297                     };
24298             } catch(e){}
24299             return Roo.emptyFn;
24300         };
24301     }(),
24302
24303     /**
24304      * Create a data block containing Roo.data.Records from an XML document.
24305      * @param {Object} o An object which contains an Array of row objects in the property specified
24306      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24307      * which contains the total size of the dataset.
24308      * @return {Object} data A data block which is used by an Roo.data.Store object as
24309      * a cache of Roo.data.Records.
24310      */
24311     readRecords : function(o){
24312         /**
24313          * After any data loads, the raw JSON data is available for further custom processing.
24314          * @type Object
24315          */
24316         this.o = o;
24317         var s = this.meta, Record = this.recordType,
24318             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24319
24320 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24321         if (!this.ef) {
24322             if(s.totalProperty) {
24323                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24324                 }
24325                 if(s.successProperty) {
24326                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24327                 }
24328                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24329                 if (s.id) {
24330                         var g = this.getJsonAccessor(s.id);
24331                         this.getId = function(rec) {
24332                                 var r = g(rec);  
24333                                 return (r === undefined || r === "") ? null : r;
24334                         };
24335                 } else {
24336                         this.getId = function(){return null;};
24337                 }
24338             this.ef = [];
24339             for(var jj = 0; jj < fl; jj++){
24340                 f = fi[jj];
24341                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24342                 this.ef[jj] = this.getJsonAccessor(map);
24343             }
24344         }
24345
24346         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24347         if(s.totalProperty){
24348             var vt = parseInt(this.getTotal(o), 10);
24349             if(!isNaN(vt)){
24350                 totalRecords = vt;
24351             }
24352         }
24353         if(s.successProperty){
24354             var vs = this.getSuccess(o);
24355             if(vs === false || vs === 'false'){
24356                 success = false;
24357             }
24358         }
24359         var records = [];
24360         for(var i = 0; i < c; i++){
24361                 var n = root[i];
24362             var values = {};
24363             var id = this.getId(n);
24364             for(var j = 0; j < fl; j++){
24365                 f = fi[j];
24366             var v = this.ef[j](n);
24367             if (!f.convert) {
24368                 Roo.log('missing convert for ' + f.name);
24369                 Roo.log(f);
24370                 continue;
24371             }
24372             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24373             }
24374             var record = new Record(values, id);
24375             record.json = n;
24376             records[i] = record;
24377         }
24378         return {
24379             raw : o,
24380             success : success,
24381             records : records,
24382             totalRecords : totalRecords
24383         };
24384     }
24385 });/*
24386  * Based on:
24387  * Ext JS Library 1.1.1
24388  * Copyright(c) 2006-2007, Ext JS, LLC.
24389  *
24390  * Originally Released Under LGPL - original licence link has changed is not relivant.
24391  *
24392  * Fork - LGPL
24393  * <script type="text/javascript">
24394  */
24395
24396 /**
24397  * @class Roo.data.XmlReader
24398  * @extends Roo.data.DataReader
24399  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24400  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24401  * <p>
24402  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24403  * header in the HTTP response must be set to "text/xml".</em>
24404  * <p>
24405  * Example code:
24406  * <pre><code>
24407 var RecordDef = Roo.data.Record.create([
24408    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24409    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24410 ]);
24411 var myReader = new Roo.data.XmlReader({
24412    totalRecords: "results", // The element which contains the total dataset size (optional)
24413    record: "row",           // The repeated element which contains row information
24414    id: "id"                 // The element within the row that provides an ID for the record (optional)
24415 }, RecordDef);
24416 </code></pre>
24417  * <p>
24418  * This would consume an XML file like this:
24419  * <pre><code>
24420 &lt;?xml?>
24421 &lt;dataset>
24422  &lt;results>2&lt;/results>
24423  &lt;row>
24424    &lt;id>1&lt;/id>
24425    &lt;name>Bill&lt;/name>
24426    &lt;occupation>Gardener&lt;/occupation>
24427  &lt;/row>
24428  &lt;row>
24429    &lt;id>2&lt;/id>
24430    &lt;name>Ben&lt;/name>
24431    &lt;occupation>Horticulturalist&lt;/occupation>
24432  &lt;/row>
24433 &lt;/dataset>
24434 </code></pre>
24435  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24436  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24437  * paged from the remote server.
24438  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24439  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24440  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24441  * a record identifier value.
24442  * @constructor
24443  * Create a new XmlReader
24444  * @param {Object} meta Metadata configuration options
24445  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24446  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24447  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24448  */
24449 Roo.data.XmlReader = function(meta, recordType){
24450     meta = meta || {};
24451     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24452 };
24453 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24454     /**
24455      * This method is only used by a DataProxy which has retrieved data from a remote server.
24456          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24457          * to contain a method called 'responseXML' that returns an XML document object.
24458      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24459      * a cache of Roo.data.Records.
24460      */
24461     read : function(response){
24462         var doc = response.responseXML;
24463         if(!doc) {
24464             throw {message: "XmlReader.read: XML Document not available"};
24465         }
24466         return this.readRecords(doc);
24467     },
24468
24469     /**
24470      * Create a data block containing Roo.data.Records from an XML document.
24471          * @param {Object} doc A parsed XML document.
24472      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24473      * a cache of Roo.data.Records.
24474      */
24475     readRecords : function(doc){
24476         /**
24477          * After any data loads/reads, the raw XML Document is available for further custom processing.
24478          * @type XMLDocument
24479          */
24480         this.xmlData = doc;
24481         var root = doc.documentElement || doc;
24482         var q = Roo.DomQuery;
24483         var recordType = this.recordType, fields = recordType.prototype.fields;
24484         var sid = this.meta.id;
24485         var totalRecords = 0, success = true;
24486         if(this.meta.totalRecords){
24487             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24488         }
24489         
24490         if(this.meta.success){
24491             var sv = q.selectValue(this.meta.success, root, true);
24492             success = sv !== false && sv !== 'false';
24493         }
24494         var records = [];
24495         var ns = q.select(this.meta.record, root);
24496         for(var i = 0, len = ns.length; i < len; i++) {
24497                 var n = ns[i];
24498                 var values = {};
24499                 var id = sid ? q.selectValue(sid, n) : undefined;
24500                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24501                     var f = fields.items[j];
24502                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24503                     v = f.convert(v);
24504                     values[f.name] = v;
24505                 }
24506                 var record = new recordType(values, id);
24507                 record.node = n;
24508                 records[records.length] = record;
24509             }
24510
24511             return {
24512                 success : success,
24513                 records : records,
24514                 totalRecords : totalRecords || records.length
24515             };
24516     }
24517 });/*
24518  * Based on:
24519  * Ext JS Library 1.1.1
24520  * Copyright(c) 2006-2007, Ext JS, LLC.
24521  *
24522  * Originally Released Under LGPL - original licence link has changed is not relivant.
24523  *
24524  * Fork - LGPL
24525  * <script type="text/javascript">
24526  */
24527
24528 /**
24529  * @class Roo.data.ArrayReader
24530  * @extends Roo.data.DataReader
24531  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24532  * Each element of that Array represents a row of data fields. The
24533  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24534  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24535  * <p>
24536  * Example code:.
24537  * <pre><code>
24538 var RecordDef = Roo.data.Record.create([
24539     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24540     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24541 ]);
24542 var myReader = new Roo.data.ArrayReader({
24543     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24544 }, RecordDef);
24545 </code></pre>
24546  * <p>
24547  * This would consume an Array like this:
24548  * <pre><code>
24549 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24550   </code></pre>
24551  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24552  * @constructor
24553  * Create a new JsonReader
24554  * @param {Object} meta Metadata configuration options.
24555  * @param {Object} recordType Either an Array of field definition objects
24556  * as specified to {@link Roo.data.Record#create},
24557  * or an {@link Roo.data.Record} object
24558  * created using {@link Roo.data.Record#create}.
24559  */
24560 Roo.data.ArrayReader = function(meta, recordType){
24561     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24562 };
24563
24564 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24565     /**
24566      * Create a data block containing Roo.data.Records from an XML document.
24567      * @param {Object} o An Array of row objects which represents the dataset.
24568      * @return {Object} data A data block which is used by an Roo.data.Store object as
24569      * a cache of Roo.data.Records.
24570      */
24571     readRecords : function(o){
24572         var sid = this.meta ? this.meta.id : null;
24573         var recordType = this.recordType, fields = recordType.prototype.fields;
24574         var records = [];
24575         var root = o;
24576             for(var i = 0; i < root.length; i++){
24577                     var n = root[i];
24578                 var values = {};
24579                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24580                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24581                 var f = fields.items[j];
24582                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24583                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24584                 v = f.convert(v);
24585                 values[f.name] = v;
24586             }
24587                 var record = new recordType(values, id);
24588                 record.json = n;
24589                 records[records.length] = record;
24590             }
24591             return {
24592                 records : records,
24593                 totalRecords : records.length
24594             };
24595     }
24596 });/*
24597  * Based on:
24598  * Ext JS Library 1.1.1
24599  * Copyright(c) 2006-2007, Ext JS, LLC.
24600  *
24601  * Originally Released Under LGPL - original licence link has changed is not relivant.
24602  *
24603  * Fork - LGPL
24604  * <script type="text/javascript">
24605  */
24606
24607
24608 /**
24609  * @class Roo.data.Tree
24610  * @extends Roo.util.Observable
24611  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24612  * in the tree have most standard DOM functionality.
24613  * @constructor
24614  * @param {Node} root (optional) The root node
24615  */
24616 Roo.data.Tree = function(root){
24617    this.nodeHash = {};
24618    /**
24619     * The root node for this tree
24620     * @type Node
24621     */
24622    this.root = null;
24623    if(root){
24624        this.setRootNode(root);
24625    }
24626    this.addEvents({
24627        /**
24628         * @event append
24629         * Fires when a new child node is appended to a node in this tree.
24630         * @param {Tree} tree The owner tree
24631         * @param {Node} parent The parent node
24632         * @param {Node} node The newly appended node
24633         * @param {Number} index The index of the newly appended node
24634         */
24635        "append" : true,
24636        /**
24637         * @event remove
24638         * Fires when a child node is removed from a node in this tree.
24639         * @param {Tree} tree The owner tree
24640         * @param {Node} parent The parent node
24641         * @param {Node} node The child node removed
24642         */
24643        "remove" : true,
24644        /**
24645         * @event move
24646         * Fires when a node is moved to a new location in the tree
24647         * @param {Tree} tree The owner tree
24648         * @param {Node} node The node moved
24649         * @param {Node} oldParent The old parent of this node
24650         * @param {Node} newParent The new parent of this node
24651         * @param {Number} index The index it was moved to
24652         */
24653        "move" : true,
24654        /**
24655         * @event insert
24656         * Fires when a new child node is inserted in a node in this tree.
24657         * @param {Tree} tree The owner tree
24658         * @param {Node} parent The parent node
24659         * @param {Node} node The child node inserted
24660         * @param {Node} refNode The child node the node was inserted before
24661         */
24662        "insert" : true,
24663        /**
24664         * @event beforeappend
24665         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24666         * @param {Tree} tree The owner tree
24667         * @param {Node} parent The parent node
24668         * @param {Node} node The child node to be appended
24669         */
24670        "beforeappend" : true,
24671        /**
24672         * @event beforeremove
24673         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24674         * @param {Tree} tree The owner tree
24675         * @param {Node} parent The parent node
24676         * @param {Node} node The child node to be removed
24677         */
24678        "beforeremove" : true,
24679        /**
24680         * @event beforemove
24681         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24682         * @param {Tree} tree The owner tree
24683         * @param {Node} node The node being moved
24684         * @param {Node} oldParent The parent of the node
24685         * @param {Node} newParent The new parent the node is moving to
24686         * @param {Number} index The index it is being moved to
24687         */
24688        "beforemove" : true,
24689        /**
24690         * @event beforeinsert
24691         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24692         * @param {Tree} tree The owner tree
24693         * @param {Node} parent The parent node
24694         * @param {Node} node The child node to be inserted
24695         * @param {Node} refNode The child node the node is being inserted before
24696         */
24697        "beforeinsert" : true
24698    });
24699
24700     Roo.data.Tree.superclass.constructor.call(this);
24701 };
24702
24703 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24704     pathSeparator: "/",
24705
24706     proxyNodeEvent : function(){
24707         return this.fireEvent.apply(this, arguments);
24708     },
24709
24710     /**
24711      * Returns the root node for this tree.
24712      * @return {Node}
24713      */
24714     getRootNode : function(){
24715         return this.root;
24716     },
24717
24718     /**
24719      * Sets the root node for this tree.
24720      * @param {Node} node
24721      * @return {Node}
24722      */
24723     setRootNode : function(node){
24724         this.root = node;
24725         node.ownerTree = this;
24726         node.isRoot = true;
24727         this.registerNode(node);
24728         return node;
24729     },
24730
24731     /**
24732      * Gets a node in this tree by its id.
24733      * @param {String} id
24734      * @return {Node}
24735      */
24736     getNodeById : function(id){
24737         return this.nodeHash[id];
24738     },
24739
24740     registerNode : function(node){
24741         this.nodeHash[node.id] = node;
24742     },
24743
24744     unregisterNode : function(node){
24745         delete this.nodeHash[node.id];
24746     },
24747
24748     toString : function(){
24749         return "[Tree"+(this.id?" "+this.id:"")+"]";
24750     }
24751 });
24752
24753 /**
24754  * @class Roo.data.Node
24755  * @extends Roo.util.Observable
24756  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24757  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24758  * @constructor
24759  * @param {Object} attributes The attributes/config for the node
24760  */
24761 Roo.data.Node = function(attributes){
24762     /**
24763      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24764      * @type {Object}
24765      */
24766     this.attributes = attributes || {};
24767     this.leaf = this.attributes.leaf;
24768     /**
24769      * The node id. @type String
24770      */
24771     this.id = this.attributes.id;
24772     if(!this.id){
24773         this.id = Roo.id(null, "ynode-");
24774         this.attributes.id = this.id;
24775     }
24776      
24777     
24778     /**
24779      * All child nodes of this node. @type Array
24780      */
24781     this.childNodes = [];
24782     if(!this.childNodes.indexOf){ // indexOf is a must
24783         this.childNodes.indexOf = function(o){
24784             for(var i = 0, len = this.length; i < len; i++){
24785                 if(this[i] == o) {
24786                     return i;
24787                 }
24788             }
24789             return -1;
24790         };
24791     }
24792     /**
24793      * The parent node for this node. @type Node
24794      */
24795     this.parentNode = null;
24796     /**
24797      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24798      */
24799     this.firstChild = null;
24800     /**
24801      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24802      */
24803     this.lastChild = null;
24804     /**
24805      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24806      */
24807     this.previousSibling = null;
24808     /**
24809      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24810      */
24811     this.nextSibling = null;
24812
24813     this.addEvents({
24814        /**
24815         * @event append
24816         * Fires when a new child node is appended
24817         * @param {Tree} tree The owner tree
24818         * @param {Node} this This node
24819         * @param {Node} node The newly appended node
24820         * @param {Number} index The index of the newly appended node
24821         */
24822        "append" : true,
24823        /**
24824         * @event remove
24825         * Fires when a child node is removed
24826         * @param {Tree} tree The owner tree
24827         * @param {Node} this This node
24828         * @param {Node} node The removed node
24829         */
24830        "remove" : true,
24831        /**
24832         * @event move
24833         * Fires when this node is moved to a new location in the tree
24834         * @param {Tree} tree The owner tree
24835         * @param {Node} this This node
24836         * @param {Node} oldParent The old parent of this node
24837         * @param {Node} newParent The new parent of this node
24838         * @param {Number} index The index it was moved to
24839         */
24840        "move" : true,
24841        /**
24842         * @event insert
24843         * Fires when a new child node is inserted.
24844         * @param {Tree} tree The owner tree
24845         * @param {Node} this This node
24846         * @param {Node} node The child node inserted
24847         * @param {Node} refNode The child node the node was inserted before
24848         */
24849        "insert" : true,
24850        /**
24851         * @event beforeappend
24852         * Fires before a new child is appended, return false to cancel the append.
24853         * @param {Tree} tree The owner tree
24854         * @param {Node} this This node
24855         * @param {Node} node The child node to be appended
24856         */
24857        "beforeappend" : true,
24858        /**
24859         * @event beforeremove
24860         * Fires before a child is removed, return false to cancel the remove.
24861         * @param {Tree} tree The owner tree
24862         * @param {Node} this This node
24863         * @param {Node} node The child node to be removed
24864         */
24865        "beforeremove" : true,
24866        /**
24867         * @event beforemove
24868         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24869         * @param {Tree} tree The owner tree
24870         * @param {Node} this This node
24871         * @param {Node} oldParent The parent of this node
24872         * @param {Node} newParent The new parent this node is moving to
24873         * @param {Number} index The index it is being moved to
24874         */
24875        "beforemove" : true,
24876        /**
24877         * @event beforeinsert
24878         * Fires before a new child is inserted, return false to cancel the insert.
24879         * @param {Tree} tree The owner tree
24880         * @param {Node} this This node
24881         * @param {Node} node The child node to be inserted
24882         * @param {Node} refNode The child node the node is being inserted before
24883         */
24884        "beforeinsert" : true
24885    });
24886     this.listeners = this.attributes.listeners;
24887     Roo.data.Node.superclass.constructor.call(this);
24888 };
24889
24890 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24891     fireEvent : function(evtName){
24892         // first do standard event for this node
24893         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24894             return false;
24895         }
24896         // then bubble it up to the tree if the event wasn't cancelled
24897         var ot = this.getOwnerTree();
24898         if(ot){
24899             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24900                 return false;
24901             }
24902         }
24903         return true;
24904     },
24905
24906     /**
24907      * Returns true if this node is a leaf
24908      * @return {Boolean}
24909      */
24910     isLeaf : function(){
24911         return this.leaf === true;
24912     },
24913
24914     // private
24915     setFirstChild : function(node){
24916         this.firstChild = node;
24917     },
24918
24919     //private
24920     setLastChild : function(node){
24921         this.lastChild = node;
24922     },
24923
24924
24925     /**
24926      * Returns true if this node is the last child of its parent
24927      * @return {Boolean}
24928      */
24929     isLast : function(){
24930        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24931     },
24932
24933     /**
24934      * Returns true if this node is the first child of its parent
24935      * @return {Boolean}
24936      */
24937     isFirst : function(){
24938        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24939     },
24940
24941     hasChildNodes : function(){
24942         return !this.isLeaf() && this.childNodes.length > 0;
24943     },
24944
24945     /**
24946      * Insert node(s) as the last child node of this node.
24947      * @param {Node/Array} node The node or Array of nodes to append
24948      * @return {Node} The appended node if single append, or null if an array was passed
24949      */
24950     appendChild : function(node){
24951         var multi = false;
24952         if(node instanceof Array){
24953             multi = node;
24954         }else if(arguments.length > 1){
24955             multi = arguments;
24956         }
24957         // if passed an array or multiple args do them one by one
24958         if(multi){
24959             for(var i = 0, len = multi.length; i < len; i++) {
24960                 this.appendChild(multi[i]);
24961             }
24962         }else{
24963             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24964                 return false;
24965             }
24966             var index = this.childNodes.length;
24967             var oldParent = node.parentNode;
24968             // it's a move, make sure we move it cleanly
24969             if(oldParent){
24970                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24971                     return false;
24972                 }
24973                 oldParent.removeChild(node);
24974             }
24975             index = this.childNodes.length;
24976             if(index == 0){
24977                 this.setFirstChild(node);
24978             }
24979             this.childNodes.push(node);
24980             node.parentNode = this;
24981             var ps = this.childNodes[index-1];
24982             if(ps){
24983                 node.previousSibling = ps;
24984                 ps.nextSibling = node;
24985             }else{
24986                 node.previousSibling = null;
24987             }
24988             node.nextSibling = null;
24989             this.setLastChild(node);
24990             node.setOwnerTree(this.getOwnerTree());
24991             this.fireEvent("append", this.ownerTree, this, node, index);
24992             if(oldParent){
24993                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24994             }
24995             return node;
24996         }
24997     },
24998
24999     /**
25000      * Removes a child node from this node.
25001      * @param {Node} node The node to remove
25002      * @return {Node} The removed node
25003      */
25004     removeChild : function(node){
25005         var index = this.childNodes.indexOf(node);
25006         if(index == -1){
25007             return false;
25008         }
25009         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25010             return false;
25011         }
25012
25013         // remove it from childNodes collection
25014         this.childNodes.splice(index, 1);
25015
25016         // update siblings
25017         if(node.previousSibling){
25018             node.previousSibling.nextSibling = node.nextSibling;
25019         }
25020         if(node.nextSibling){
25021             node.nextSibling.previousSibling = node.previousSibling;
25022         }
25023
25024         // update child refs
25025         if(this.firstChild == node){
25026             this.setFirstChild(node.nextSibling);
25027         }
25028         if(this.lastChild == node){
25029             this.setLastChild(node.previousSibling);
25030         }
25031
25032         node.setOwnerTree(null);
25033         // clear any references from the node
25034         node.parentNode = null;
25035         node.previousSibling = null;
25036         node.nextSibling = null;
25037         this.fireEvent("remove", this.ownerTree, this, node);
25038         return node;
25039     },
25040
25041     /**
25042      * Inserts the first node before the second node in this nodes childNodes collection.
25043      * @param {Node} node The node to insert
25044      * @param {Node} refNode The node to insert before (if null the node is appended)
25045      * @return {Node} The inserted node
25046      */
25047     insertBefore : function(node, refNode){
25048         if(!refNode){ // like standard Dom, refNode can be null for append
25049             return this.appendChild(node);
25050         }
25051         // nothing to do
25052         if(node == refNode){
25053             return false;
25054         }
25055
25056         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25057             return false;
25058         }
25059         var index = this.childNodes.indexOf(refNode);
25060         var oldParent = node.parentNode;
25061         var refIndex = index;
25062
25063         // when moving internally, indexes will change after remove
25064         if(oldParent == this && this.childNodes.indexOf(node) < index){
25065             refIndex--;
25066         }
25067
25068         // it's a move, make sure we move it cleanly
25069         if(oldParent){
25070             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25071                 return false;
25072             }
25073             oldParent.removeChild(node);
25074         }
25075         if(refIndex == 0){
25076             this.setFirstChild(node);
25077         }
25078         this.childNodes.splice(refIndex, 0, node);
25079         node.parentNode = this;
25080         var ps = this.childNodes[refIndex-1];
25081         if(ps){
25082             node.previousSibling = ps;
25083             ps.nextSibling = node;
25084         }else{
25085             node.previousSibling = null;
25086         }
25087         node.nextSibling = refNode;
25088         refNode.previousSibling = node;
25089         node.setOwnerTree(this.getOwnerTree());
25090         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25091         if(oldParent){
25092             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25093         }
25094         return node;
25095     },
25096
25097     /**
25098      * Returns the child node at the specified index.
25099      * @param {Number} index
25100      * @return {Node}
25101      */
25102     item : function(index){
25103         return this.childNodes[index];
25104     },
25105
25106     /**
25107      * Replaces one child node in this node with another.
25108      * @param {Node} newChild The replacement node
25109      * @param {Node} oldChild The node to replace
25110      * @return {Node} The replaced node
25111      */
25112     replaceChild : function(newChild, oldChild){
25113         this.insertBefore(newChild, oldChild);
25114         this.removeChild(oldChild);
25115         return oldChild;
25116     },
25117
25118     /**
25119      * Returns the index of a child node
25120      * @param {Node} node
25121      * @return {Number} The index of the node or -1 if it was not found
25122      */
25123     indexOf : function(child){
25124         return this.childNodes.indexOf(child);
25125     },
25126
25127     /**
25128      * Returns the tree this node is in.
25129      * @return {Tree}
25130      */
25131     getOwnerTree : function(){
25132         // if it doesn't have one, look for one
25133         if(!this.ownerTree){
25134             var p = this;
25135             while(p){
25136                 if(p.ownerTree){
25137                     this.ownerTree = p.ownerTree;
25138                     break;
25139                 }
25140                 p = p.parentNode;
25141             }
25142         }
25143         return this.ownerTree;
25144     },
25145
25146     /**
25147      * Returns depth of this node (the root node has a depth of 0)
25148      * @return {Number}
25149      */
25150     getDepth : function(){
25151         var depth = 0;
25152         var p = this;
25153         while(p.parentNode){
25154             ++depth;
25155             p = p.parentNode;
25156         }
25157         return depth;
25158     },
25159
25160     // private
25161     setOwnerTree : function(tree){
25162         // if it's move, we need to update everyone
25163         if(tree != this.ownerTree){
25164             if(this.ownerTree){
25165                 this.ownerTree.unregisterNode(this);
25166             }
25167             this.ownerTree = tree;
25168             var cs = this.childNodes;
25169             for(var i = 0, len = cs.length; i < len; i++) {
25170                 cs[i].setOwnerTree(tree);
25171             }
25172             if(tree){
25173                 tree.registerNode(this);
25174             }
25175         }
25176     },
25177
25178     /**
25179      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25180      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25181      * @return {String} The path
25182      */
25183     getPath : function(attr){
25184         attr = attr || "id";
25185         var p = this.parentNode;
25186         var b = [this.attributes[attr]];
25187         while(p){
25188             b.unshift(p.attributes[attr]);
25189             p = p.parentNode;
25190         }
25191         var sep = this.getOwnerTree().pathSeparator;
25192         return sep + b.join(sep);
25193     },
25194
25195     /**
25196      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25197      * function call will be the scope provided or the current node. The arguments to the function
25198      * will be the args provided or the current node. If the function returns false at any point,
25199      * the bubble is stopped.
25200      * @param {Function} fn The function to call
25201      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25202      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25203      */
25204     bubble : function(fn, scope, args){
25205         var p = this;
25206         while(p){
25207             if(fn.call(scope || p, args || p) === false){
25208                 break;
25209             }
25210             p = p.parentNode;
25211         }
25212     },
25213
25214     /**
25215      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25216      * function call will be the scope provided or the current node. The arguments to the function
25217      * will be the args provided or the current node. If the function returns false at any point,
25218      * the cascade is stopped on that branch.
25219      * @param {Function} fn The function to call
25220      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25221      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25222      */
25223     cascade : function(fn, scope, args){
25224         if(fn.call(scope || this, args || this) !== false){
25225             var cs = this.childNodes;
25226             for(var i = 0, len = cs.length; i < len; i++) {
25227                 cs[i].cascade(fn, scope, args);
25228             }
25229         }
25230     },
25231
25232     /**
25233      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25234      * function call will be the scope provided or the current node. The arguments to the function
25235      * will be the args provided or the current node. If the function returns false at any point,
25236      * the iteration stops.
25237      * @param {Function} fn The function to call
25238      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25239      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25240      */
25241     eachChild : function(fn, scope, args){
25242         var cs = this.childNodes;
25243         for(var i = 0, len = cs.length; i < len; i++) {
25244                 if(fn.call(scope || this, args || cs[i]) === false){
25245                     break;
25246                 }
25247         }
25248     },
25249
25250     /**
25251      * Finds the first child that has the attribute with the specified value.
25252      * @param {String} attribute The attribute name
25253      * @param {Mixed} value The value to search for
25254      * @return {Node} The found child or null if none was found
25255      */
25256     findChild : function(attribute, value){
25257         var cs = this.childNodes;
25258         for(var i = 0, len = cs.length; i < len; i++) {
25259                 if(cs[i].attributes[attribute] == value){
25260                     return cs[i];
25261                 }
25262         }
25263         return null;
25264     },
25265
25266     /**
25267      * Finds the first child by a custom function. The child matches if the function passed
25268      * returns true.
25269      * @param {Function} fn
25270      * @param {Object} scope (optional)
25271      * @return {Node} The found child or null if none was found
25272      */
25273     findChildBy : function(fn, scope){
25274         var cs = this.childNodes;
25275         for(var i = 0, len = cs.length; i < len; i++) {
25276                 if(fn.call(scope||cs[i], cs[i]) === true){
25277                     return cs[i];
25278                 }
25279         }
25280         return null;
25281     },
25282
25283     /**
25284      * Sorts this nodes children using the supplied sort function
25285      * @param {Function} fn
25286      * @param {Object} scope (optional)
25287      */
25288     sort : function(fn, scope){
25289         var cs = this.childNodes;
25290         var len = cs.length;
25291         if(len > 0){
25292             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25293             cs.sort(sortFn);
25294             for(var i = 0; i < len; i++){
25295                 var n = cs[i];
25296                 n.previousSibling = cs[i-1];
25297                 n.nextSibling = cs[i+1];
25298                 if(i == 0){
25299                     this.setFirstChild(n);
25300                 }
25301                 if(i == len-1){
25302                     this.setLastChild(n);
25303                 }
25304             }
25305         }
25306     },
25307
25308     /**
25309      * Returns true if this node is an ancestor (at any point) of the passed node.
25310      * @param {Node} node
25311      * @return {Boolean}
25312      */
25313     contains : function(node){
25314         return node.isAncestor(this);
25315     },
25316
25317     /**
25318      * Returns true if the passed node is an ancestor (at any point) of this node.
25319      * @param {Node} node
25320      * @return {Boolean}
25321      */
25322     isAncestor : function(node){
25323         var p = this.parentNode;
25324         while(p){
25325             if(p == node){
25326                 return true;
25327             }
25328             p = p.parentNode;
25329         }
25330         return false;
25331     },
25332
25333     toString : function(){
25334         return "[Node"+(this.id?" "+this.id:"")+"]";
25335     }
25336 });/*
25337  * Based on:
25338  * Ext JS Library 1.1.1
25339  * Copyright(c) 2006-2007, Ext JS, LLC.
25340  *
25341  * Originally Released Under LGPL - original licence link has changed is not relivant.
25342  *
25343  * Fork - LGPL
25344  * <script type="text/javascript">
25345  */
25346  (function(){ 
25347 /**
25348  * @class Roo.Layer
25349  * @extends Roo.Element
25350  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25351  * automatic maintaining of shadow/shim positions.
25352  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25353  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25354  * you can pass a string with a CSS class name. False turns off the shadow.
25355  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25356  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25357  * @cfg {String} cls CSS class to add to the element
25358  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25359  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25360  * @constructor
25361  * @param {Object} config An object with config options.
25362  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25363  */
25364
25365 Roo.Layer = function(config, existingEl){
25366     config = config || {};
25367     var dh = Roo.DomHelper;
25368     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25369     if(existingEl){
25370         this.dom = Roo.getDom(existingEl);
25371     }
25372     if(!this.dom){
25373         var o = config.dh || {tag: "div", cls: "x-layer"};
25374         this.dom = dh.append(pel, o);
25375     }
25376     if(config.cls){
25377         this.addClass(config.cls);
25378     }
25379     this.constrain = config.constrain !== false;
25380     this.visibilityMode = Roo.Element.VISIBILITY;
25381     if(config.id){
25382         this.id = this.dom.id = config.id;
25383     }else{
25384         this.id = Roo.id(this.dom);
25385     }
25386     this.zindex = config.zindex || this.getZIndex();
25387     this.position("absolute", this.zindex);
25388     if(config.shadow){
25389         this.shadowOffset = config.shadowOffset || 4;
25390         this.shadow = new Roo.Shadow({
25391             offset : this.shadowOffset,
25392             mode : config.shadow
25393         });
25394     }else{
25395         this.shadowOffset = 0;
25396     }
25397     this.useShim = config.shim !== false && Roo.useShims;
25398     this.useDisplay = config.useDisplay;
25399     this.hide();
25400 };
25401
25402 var supr = Roo.Element.prototype;
25403
25404 // shims are shared among layer to keep from having 100 iframes
25405 var shims = [];
25406
25407 Roo.extend(Roo.Layer, Roo.Element, {
25408
25409     getZIndex : function(){
25410         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25411     },
25412
25413     getShim : function(){
25414         if(!this.useShim){
25415             return null;
25416         }
25417         if(this.shim){
25418             return this.shim;
25419         }
25420         var shim = shims.shift();
25421         if(!shim){
25422             shim = this.createShim();
25423             shim.enableDisplayMode('block');
25424             shim.dom.style.display = 'none';
25425             shim.dom.style.visibility = 'visible';
25426         }
25427         var pn = this.dom.parentNode;
25428         if(shim.dom.parentNode != pn){
25429             pn.insertBefore(shim.dom, this.dom);
25430         }
25431         shim.setStyle('z-index', this.getZIndex()-2);
25432         this.shim = shim;
25433         return shim;
25434     },
25435
25436     hideShim : function(){
25437         if(this.shim){
25438             this.shim.setDisplayed(false);
25439             shims.push(this.shim);
25440             delete this.shim;
25441         }
25442     },
25443
25444     disableShadow : function(){
25445         if(this.shadow){
25446             this.shadowDisabled = true;
25447             this.shadow.hide();
25448             this.lastShadowOffset = this.shadowOffset;
25449             this.shadowOffset = 0;
25450         }
25451     },
25452
25453     enableShadow : function(show){
25454         if(this.shadow){
25455             this.shadowDisabled = false;
25456             this.shadowOffset = this.lastShadowOffset;
25457             delete this.lastShadowOffset;
25458             if(show){
25459                 this.sync(true);
25460             }
25461         }
25462     },
25463
25464     // private
25465     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25466     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25467     sync : function(doShow){
25468         var sw = this.shadow;
25469         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25470             var sh = this.getShim();
25471
25472             var w = this.getWidth(),
25473                 h = this.getHeight();
25474
25475             var l = this.getLeft(true),
25476                 t = this.getTop(true);
25477
25478             if(sw && !this.shadowDisabled){
25479                 if(doShow && !sw.isVisible()){
25480                     sw.show(this);
25481                 }else{
25482                     sw.realign(l, t, w, h);
25483                 }
25484                 if(sh){
25485                     if(doShow){
25486                        sh.show();
25487                     }
25488                     // fit the shim behind the shadow, so it is shimmed too
25489                     var a = sw.adjusts, s = sh.dom.style;
25490                     s.left = (Math.min(l, l+a.l))+"px";
25491                     s.top = (Math.min(t, t+a.t))+"px";
25492                     s.width = (w+a.w)+"px";
25493                     s.height = (h+a.h)+"px";
25494                 }
25495             }else if(sh){
25496                 if(doShow){
25497                    sh.show();
25498                 }
25499                 sh.setSize(w, h);
25500                 sh.setLeftTop(l, t);
25501             }
25502             
25503         }
25504     },
25505
25506     // private
25507     destroy : function(){
25508         this.hideShim();
25509         if(this.shadow){
25510             this.shadow.hide();
25511         }
25512         this.removeAllListeners();
25513         var pn = this.dom.parentNode;
25514         if(pn){
25515             pn.removeChild(this.dom);
25516         }
25517         Roo.Element.uncache(this.id);
25518     },
25519
25520     remove : function(){
25521         this.destroy();
25522     },
25523
25524     // private
25525     beginUpdate : function(){
25526         this.updating = true;
25527     },
25528
25529     // private
25530     endUpdate : function(){
25531         this.updating = false;
25532         this.sync(true);
25533     },
25534
25535     // private
25536     hideUnders : function(negOffset){
25537         if(this.shadow){
25538             this.shadow.hide();
25539         }
25540         this.hideShim();
25541     },
25542
25543     // private
25544     constrainXY : function(){
25545         if(this.constrain){
25546             var vw = Roo.lib.Dom.getViewWidth(),
25547                 vh = Roo.lib.Dom.getViewHeight();
25548             var s = Roo.get(document).getScroll();
25549
25550             var xy = this.getXY();
25551             var x = xy[0], y = xy[1];   
25552             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25553             // only move it if it needs it
25554             var moved = false;
25555             // first validate right/bottom
25556             if((x + w) > vw+s.left){
25557                 x = vw - w - this.shadowOffset;
25558                 moved = true;
25559             }
25560             if((y + h) > vh+s.top){
25561                 y = vh - h - this.shadowOffset;
25562                 moved = true;
25563             }
25564             // then make sure top/left isn't negative
25565             if(x < s.left){
25566                 x = s.left;
25567                 moved = true;
25568             }
25569             if(y < s.top){
25570                 y = s.top;
25571                 moved = true;
25572             }
25573             if(moved){
25574                 if(this.avoidY){
25575                     var ay = this.avoidY;
25576                     if(y <= ay && (y+h) >= ay){
25577                         y = ay-h-5;   
25578                     }
25579                 }
25580                 xy = [x, y];
25581                 this.storeXY(xy);
25582                 supr.setXY.call(this, xy);
25583                 this.sync();
25584             }
25585         }
25586     },
25587
25588     isVisible : function(){
25589         return this.visible;    
25590     },
25591
25592     // private
25593     showAction : function(){
25594         this.visible = true; // track visibility to prevent getStyle calls
25595         if(this.useDisplay === true){
25596             this.setDisplayed("");
25597         }else if(this.lastXY){
25598             supr.setXY.call(this, this.lastXY);
25599         }else if(this.lastLT){
25600             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25601         }
25602     },
25603
25604     // private
25605     hideAction : function(){
25606         this.visible = false;
25607         if(this.useDisplay === true){
25608             this.setDisplayed(false);
25609         }else{
25610             this.setLeftTop(-10000,-10000);
25611         }
25612     },
25613
25614     // overridden Element method
25615     setVisible : function(v, a, d, c, e){
25616         if(v){
25617             this.showAction();
25618         }
25619         if(a && v){
25620             var cb = function(){
25621                 this.sync(true);
25622                 if(c){
25623                     c();
25624                 }
25625             }.createDelegate(this);
25626             supr.setVisible.call(this, true, true, d, cb, e);
25627         }else{
25628             if(!v){
25629                 this.hideUnders(true);
25630             }
25631             var cb = c;
25632             if(a){
25633                 cb = function(){
25634                     this.hideAction();
25635                     if(c){
25636                         c();
25637                     }
25638                 }.createDelegate(this);
25639             }
25640             supr.setVisible.call(this, v, a, d, cb, e);
25641             if(v){
25642                 this.sync(true);
25643             }else if(!a){
25644                 this.hideAction();
25645             }
25646         }
25647     },
25648
25649     storeXY : function(xy){
25650         delete this.lastLT;
25651         this.lastXY = xy;
25652     },
25653
25654     storeLeftTop : function(left, top){
25655         delete this.lastXY;
25656         this.lastLT = [left, top];
25657     },
25658
25659     // private
25660     beforeFx : function(){
25661         this.beforeAction();
25662         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25663     },
25664
25665     // private
25666     afterFx : function(){
25667         Roo.Layer.superclass.afterFx.apply(this, arguments);
25668         this.sync(this.isVisible());
25669     },
25670
25671     // private
25672     beforeAction : function(){
25673         if(!this.updating && this.shadow){
25674             this.shadow.hide();
25675         }
25676     },
25677
25678     // overridden Element method
25679     setLeft : function(left){
25680         this.storeLeftTop(left, this.getTop(true));
25681         supr.setLeft.apply(this, arguments);
25682         this.sync();
25683     },
25684
25685     setTop : function(top){
25686         this.storeLeftTop(this.getLeft(true), top);
25687         supr.setTop.apply(this, arguments);
25688         this.sync();
25689     },
25690
25691     setLeftTop : function(left, top){
25692         this.storeLeftTop(left, top);
25693         supr.setLeftTop.apply(this, arguments);
25694         this.sync();
25695     },
25696
25697     setXY : function(xy, a, d, c, e){
25698         this.fixDisplay();
25699         this.beforeAction();
25700         this.storeXY(xy);
25701         var cb = this.createCB(c);
25702         supr.setXY.call(this, xy, a, d, cb, e);
25703         if(!a){
25704             cb();
25705         }
25706     },
25707
25708     // private
25709     createCB : function(c){
25710         var el = this;
25711         return function(){
25712             el.constrainXY();
25713             el.sync(true);
25714             if(c){
25715                 c();
25716             }
25717         };
25718     },
25719
25720     // overridden Element method
25721     setX : function(x, a, d, c, e){
25722         this.setXY([x, this.getY()], a, d, c, e);
25723     },
25724
25725     // overridden Element method
25726     setY : function(y, a, d, c, e){
25727         this.setXY([this.getX(), y], a, d, c, e);
25728     },
25729
25730     // overridden Element method
25731     setSize : function(w, h, a, d, c, e){
25732         this.beforeAction();
25733         var cb = this.createCB(c);
25734         supr.setSize.call(this, w, h, a, d, cb, e);
25735         if(!a){
25736             cb();
25737         }
25738     },
25739
25740     // overridden Element method
25741     setWidth : function(w, a, d, c, e){
25742         this.beforeAction();
25743         var cb = this.createCB(c);
25744         supr.setWidth.call(this, w, a, d, cb, e);
25745         if(!a){
25746             cb();
25747         }
25748     },
25749
25750     // overridden Element method
25751     setHeight : function(h, a, d, c, e){
25752         this.beforeAction();
25753         var cb = this.createCB(c);
25754         supr.setHeight.call(this, h, a, d, cb, e);
25755         if(!a){
25756             cb();
25757         }
25758     },
25759
25760     // overridden Element method
25761     setBounds : function(x, y, w, h, a, d, c, e){
25762         this.beforeAction();
25763         var cb = this.createCB(c);
25764         if(!a){
25765             this.storeXY([x, y]);
25766             supr.setXY.call(this, [x, y]);
25767             supr.setSize.call(this, w, h, a, d, cb, e);
25768             cb();
25769         }else{
25770             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25771         }
25772         return this;
25773     },
25774     
25775     /**
25776      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25777      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25778      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25779      * @param {Number} zindex The new z-index to set
25780      * @return {this} The Layer
25781      */
25782     setZIndex : function(zindex){
25783         this.zindex = zindex;
25784         this.setStyle("z-index", zindex + 2);
25785         if(this.shadow){
25786             this.shadow.setZIndex(zindex + 1);
25787         }
25788         if(this.shim){
25789             this.shim.setStyle("z-index", zindex);
25790         }
25791     }
25792 });
25793 })();/*
25794  * Based on:
25795  * Ext JS Library 1.1.1
25796  * Copyright(c) 2006-2007, Ext JS, LLC.
25797  *
25798  * Originally Released Under LGPL - original licence link has changed is not relivant.
25799  *
25800  * Fork - LGPL
25801  * <script type="text/javascript">
25802  */
25803
25804
25805 /**
25806  * @class Roo.Shadow
25807  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25808  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25809  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25810  * @constructor
25811  * Create a new Shadow
25812  * @param {Object} config The config object
25813  */
25814 Roo.Shadow = function(config){
25815     Roo.apply(this, config);
25816     if(typeof this.mode != "string"){
25817         this.mode = this.defaultMode;
25818     }
25819     var o = this.offset, a = {h: 0};
25820     var rad = Math.floor(this.offset/2);
25821     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25822         case "drop":
25823             a.w = 0;
25824             a.l = a.t = o;
25825             a.t -= 1;
25826             if(Roo.isIE){
25827                 a.l -= this.offset + rad;
25828                 a.t -= this.offset + rad;
25829                 a.w -= rad;
25830                 a.h -= rad;
25831                 a.t += 1;
25832             }
25833         break;
25834         case "sides":
25835             a.w = (o*2);
25836             a.l = -o;
25837             a.t = o-1;
25838             if(Roo.isIE){
25839                 a.l -= (this.offset - rad);
25840                 a.t -= this.offset + rad;
25841                 a.l += 1;
25842                 a.w -= (this.offset - rad)*2;
25843                 a.w -= rad + 1;
25844                 a.h -= 1;
25845             }
25846         break;
25847         case "frame":
25848             a.w = a.h = (o*2);
25849             a.l = a.t = -o;
25850             a.t += 1;
25851             a.h -= 2;
25852             if(Roo.isIE){
25853                 a.l -= (this.offset - rad);
25854                 a.t -= (this.offset - rad);
25855                 a.l += 1;
25856                 a.w -= (this.offset + rad + 1);
25857                 a.h -= (this.offset + rad);
25858                 a.h += 1;
25859             }
25860         break;
25861     };
25862
25863     this.adjusts = a;
25864 };
25865
25866 Roo.Shadow.prototype = {
25867     /**
25868      * @cfg {String} mode
25869      * The shadow display mode.  Supports the following options:<br />
25870      * sides: Shadow displays on both sides and bottom only<br />
25871      * frame: Shadow displays equally on all four sides<br />
25872      * drop: Traditional bottom-right drop shadow (default)
25873      */
25874     /**
25875      * @cfg {String} offset
25876      * The number of pixels to offset the shadow from the element (defaults to 4)
25877      */
25878     offset: 4,
25879
25880     // private
25881     defaultMode: "drop",
25882
25883     /**
25884      * Displays the shadow under the target element
25885      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25886      */
25887     show : function(target){
25888         target = Roo.get(target);
25889         if(!this.el){
25890             this.el = Roo.Shadow.Pool.pull();
25891             if(this.el.dom.nextSibling != target.dom){
25892                 this.el.insertBefore(target);
25893             }
25894         }
25895         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25896         if(Roo.isIE){
25897             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25898         }
25899         this.realign(
25900             target.getLeft(true),
25901             target.getTop(true),
25902             target.getWidth(),
25903             target.getHeight()
25904         );
25905         this.el.dom.style.display = "block";
25906     },
25907
25908     /**
25909      * Returns true if the shadow is visible, else false
25910      */
25911     isVisible : function(){
25912         return this.el ? true : false;  
25913     },
25914
25915     /**
25916      * Direct alignment when values are already available. Show must be called at least once before
25917      * calling this method to ensure it is initialized.
25918      * @param {Number} left The target element left position
25919      * @param {Number} top The target element top position
25920      * @param {Number} width The target element width
25921      * @param {Number} height The target element height
25922      */
25923     realign : function(l, t, w, h){
25924         if(!this.el){
25925             return;
25926         }
25927         var a = this.adjusts, d = this.el.dom, s = d.style;
25928         var iea = 0;
25929         s.left = (l+a.l)+"px";
25930         s.top = (t+a.t)+"px";
25931         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25932  
25933         if(s.width != sws || s.height != shs){
25934             s.width = sws;
25935             s.height = shs;
25936             if(!Roo.isIE){
25937                 var cn = d.childNodes;
25938                 var sww = Math.max(0, (sw-12))+"px";
25939                 cn[0].childNodes[1].style.width = sww;
25940                 cn[1].childNodes[1].style.width = sww;
25941                 cn[2].childNodes[1].style.width = sww;
25942                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25943             }
25944         }
25945     },
25946
25947     /**
25948      * Hides this shadow
25949      */
25950     hide : function(){
25951         if(this.el){
25952             this.el.dom.style.display = "none";
25953             Roo.Shadow.Pool.push(this.el);
25954             delete this.el;
25955         }
25956     },
25957
25958     /**
25959      * Adjust the z-index of this shadow
25960      * @param {Number} zindex The new z-index
25961      */
25962     setZIndex : function(z){
25963         this.zIndex = z;
25964         if(this.el){
25965             this.el.setStyle("z-index", z);
25966         }
25967     }
25968 };
25969
25970 // Private utility class that manages the internal Shadow cache
25971 Roo.Shadow.Pool = function(){
25972     var p = [];
25973     var markup = Roo.isIE ?
25974                  '<div class="x-ie-shadow"></div>' :
25975                  '<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>';
25976     return {
25977         pull : function(){
25978             var sh = p.shift();
25979             if(!sh){
25980                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25981                 sh.autoBoxAdjust = false;
25982             }
25983             return sh;
25984         },
25985
25986         push : function(sh){
25987             p.push(sh);
25988         }
25989     };
25990 }();/*
25991  * Based on:
25992  * Ext JS Library 1.1.1
25993  * Copyright(c) 2006-2007, Ext JS, LLC.
25994  *
25995  * Originally Released Under LGPL - original licence link has changed is not relivant.
25996  *
25997  * Fork - LGPL
25998  * <script type="text/javascript">
25999  */
26000
26001
26002 /**
26003  * @class Roo.SplitBar
26004  * @extends Roo.util.Observable
26005  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26006  * <br><br>
26007  * Usage:
26008  * <pre><code>
26009 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26010                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26011 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26012 split.minSize = 100;
26013 split.maxSize = 600;
26014 split.animate = true;
26015 split.on('moved', splitterMoved);
26016 </code></pre>
26017  * @constructor
26018  * Create a new SplitBar
26019  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26020  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26021  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26022  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26023                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26024                         position of the SplitBar).
26025  */
26026 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26027     
26028     /** @private */
26029     this.el = Roo.get(dragElement, true);
26030     this.el.dom.unselectable = "on";
26031     /** @private */
26032     this.resizingEl = Roo.get(resizingElement, true);
26033
26034     /**
26035      * @private
26036      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26037      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26038      * @type Number
26039      */
26040     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26041     
26042     /**
26043      * The minimum size of the resizing element. (Defaults to 0)
26044      * @type Number
26045      */
26046     this.minSize = 0;
26047     
26048     /**
26049      * The maximum size of the resizing element. (Defaults to 2000)
26050      * @type Number
26051      */
26052     this.maxSize = 2000;
26053     
26054     /**
26055      * Whether to animate the transition to the new size
26056      * @type Boolean
26057      */
26058     this.animate = false;
26059     
26060     /**
26061      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26062      * @type Boolean
26063      */
26064     this.useShim = false;
26065     
26066     /** @private */
26067     this.shim = null;
26068     
26069     if(!existingProxy){
26070         /** @private */
26071         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26072     }else{
26073         this.proxy = Roo.get(existingProxy).dom;
26074     }
26075     /** @private */
26076     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26077     
26078     /** @private */
26079     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26080     
26081     /** @private */
26082     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26083     
26084     /** @private */
26085     this.dragSpecs = {};
26086     
26087     /**
26088      * @private The adapter to use to positon and resize elements
26089      */
26090     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26091     this.adapter.init(this);
26092     
26093     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26094         /** @private */
26095         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26096         this.el.addClass("x-splitbar-h");
26097     }else{
26098         /** @private */
26099         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26100         this.el.addClass("x-splitbar-v");
26101     }
26102     
26103     this.addEvents({
26104         /**
26105          * @event resize
26106          * Fires when the splitter is moved (alias for {@link #event-moved})
26107          * @param {Roo.SplitBar} this
26108          * @param {Number} newSize the new width or height
26109          */
26110         "resize" : true,
26111         /**
26112          * @event moved
26113          * Fires when the splitter is moved
26114          * @param {Roo.SplitBar} this
26115          * @param {Number} newSize the new width or height
26116          */
26117         "moved" : true,
26118         /**
26119          * @event beforeresize
26120          * Fires before the splitter is dragged
26121          * @param {Roo.SplitBar} this
26122          */
26123         "beforeresize" : true,
26124
26125         "beforeapply" : true
26126     });
26127
26128     Roo.util.Observable.call(this);
26129 };
26130
26131 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26132     onStartProxyDrag : function(x, y){
26133         this.fireEvent("beforeresize", this);
26134         if(!this.overlay){
26135             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26136             o.unselectable();
26137             o.enableDisplayMode("block");
26138             // all splitbars share the same overlay
26139             Roo.SplitBar.prototype.overlay = o;
26140         }
26141         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26142         this.overlay.show();
26143         Roo.get(this.proxy).setDisplayed("block");
26144         var size = this.adapter.getElementSize(this);
26145         this.activeMinSize = this.getMinimumSize();;
26146         this.activeMaxSize = this.getMaximumSize();;
26147         var c1 = size - this.activeMinSize;
26148         var c2 = Math.max(this.activeMaxSize - size, 0);
26149         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26150             this.dd.resetConstraints();
26151             this.dd.setXConstraint(
26152                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26153                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26154             );
26155             this.dd.setYConstraint(0, 0);
26156         }else{
26157             this.dd.resetConstraints();
26158             this.dd.setXConstraint(0, 0);
26159             this.dd.setYConstraint(
26160                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26161                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26162             );
26163          }
26164         this.dragSpecs.startSize = size;
26165         this.dragSpecs.startPoint = [x, y];
26166         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26167     },
26168     
26169     /** 
26170      * @private Called after the drag operation by the DDProxy
26171      */
26172     onEndProxyDrag : function(e){
26173         Roo.get(this.proxy).setDisplayed(false);
26174         var endPoint = Roo.lib.Event.getXY(e);
26175         if(this.overlay){
26176             this.overlay.hide();
26177         }
26178         var newSize;
26179         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26180             newSize = this.dragSpecs.startSize + 
26181                 (this.placement == Roo.SplitBar.LEFT ?
26182                     endPoint[0] - this.dragSpecs.startPoint[0] :
26183                     this.dragSpecs.startPoint[0] - endPoint[0]
26184                 );
26185         }else{
26186             newSize = this.dragSpecs.startSize + 
26187                 (this.placement == Roo.SplitBar.TOP ?
26188                     endPoint[1] - this.dragSpecs.startPoint[1] :
26189                     this.dragSpecs.startPoint[1] - endPoint[1]
26190                 );
26191         }
26192         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26193         if(newSize != this.dragSpecs.startSize){
26194             if(this.fireEvent('beforeapply', this, newSize) !== false){
26195                 this.adapter.setElementSize(this, newSize);
26196                 this.fireEvent("moved", this, newSize);
26197                 this.fireEvent("resize", this, newSize);
26198             }
26199         }
26200     },
26201     
26202     /**
26203      * Get the adapter this SplitBar uses
26204      * @return The adapter object
26205      */
26206     getAdapter : function(){
26207         return this.adapter;
26208     },
26209     
26210     /**
26211      * Set the adapter this SplitBar uses
26212      * @param {Object} adapter A SplitBar adapter object
26213      */
26214     setAdapter : function(adapter){
26215         this.adapter = adapter;
26216         this.adapter.init(this);
26217     },
26218     
26219     /**
26220      * Gets the minimum size for the resizing element
26221      * @return {Number} The minimum size
26222      */
26223     getMinimumSize : function(){
26224         return this.minSize;
26225     },
26226     
26227     /**
26228      * Sets the minimum size for the resizing element
26229      * @param {Number} minSize The minimum size
26230      */
26231     setMinimumSize : function(minSize){
26232         this.minSize = minSize;
26233     },
26234     
26235     /**
26236      * Gets the maximum size for the resizing element
26237      * @return {Number} The maximum size
26238      */
26239     getMaximumSize : function(){
26240         return this.maxSize;
26241     },
26242     
26243     /**
26244      * Sets the maximum size for the resizing element
26245      * @param {Number} maxSize The maximum size
26246      */
26247     setMaximumSize : function(maxSize){
26248         this.maxSize = maxSize;
26249     },
26250     
26251     /**
26252      * Sets the initialize size for the resizing element
26253      * @param {Number} size The initial size
26254      */
26255     setCurrentSize : function(size){
26256         var oldAnimate = this.animate;
26257         this.animate = false;
26258         this.adapter.setElementSize(this, size);
26259         this.animate = oldAnimate;
26260     },
26261     
26262     /**
26263      * Destroy this splitbar. 
26264      * @param {Boolean} removeEl True to remove the element
26265      */
26266     destroy : function(removeEl){
26267         if(this.shim){
26268             this.shim.remove();
26269         }
26270         this.dd.unreg();
26271         this.proxy.parentNode.removeChild(this.proxy);
26272         if(removeEl){
26273             this.el.remove();
26274         }
26275     }
26276 });
26277
26278 /**
26279  * @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.
26280  */
26281 Roo.SplitBar.createProxy = function(dir){
26282     var proxy = new Roo.Element(document.createElement("div"));
26283     proxy.unselectable();
26284     var cls = 'x-splitbar-proxy';
26285     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26286     document.body.appendChild(proxy.dom);
26287     return proxy.dom;
26288 };
26289
26290 /** 
26291  * @class Roo.SplitBar.BasicLayoutAdapter
26292  * Default Adapter. It assumes the splitter and resizing element are not positioned
26293  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26294  */
26295 Roo.SplitBar.BasicLayoutAdapter = function(){
26296 };
26297
26298 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26299     // do nothing for now
26300     init : function(s){
26301     
26302     },
26303     /**
26304      * Called before drag operations to get the current size of the resizing element. 
26305      * @param {Roo.SplitBar} s The SplitBar using this adapter
26306      */
26307      getElementSize : function(s){
26308         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26309             return s.resizingEl.getWidth();
26310         }else{
26311             return s.resizingEl.getHeight();
26312         }
26313     },
26314     
26315     /**
26316      * Called after drag operations to set the size of the resizing element.
26317      * @param {Roo.SplitBar} s The SplitBar using this adapter
26318      * @param {Number} newSize The new size to set
26319      * @param {Function} onComplete A function to be invoked when resizing is complete
26320      */
26321     setElementSize : function(s, newSize, onComplete){
26322         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26323             if(!s.animate){
26324                 s.resizingEl.setWidth(newSize);
26325                 if(onComplete){
26326                     onComplete(s, newSize);
26327                 }
26328             }else{
26329                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26330             }
26331         }else{
26332             
26333             if(!s.animate){
26334                 s.resizingEl.setHeight(newSize);
26335                 if(onComplete){
26336                     onComplete(s, newSize);
26337                 }
26338             }else{
26339                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26340             }
26341         }
26342     }
26343 };
26344
26345 /** 
26346  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26347  * @extends Roo.SplitBar.BasicLayoutAdapter
26348  * Adapter that  moves the splitter element to align with the resized sizing element. 
26349  * Used with an absolute positioned SplitBar.
26350  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26351  * document.body, make sure you assign an id to the body element.
26352  */
26353 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26354     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26355     this.container = Roo.get(container);
26356 };
26357
26358 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26359     init : function(s){
26360         this.basic.init(s);
26361     },
26362     
26363     getElementSize : function(s){
26364         return this.basic.getElementSize(s);
26365     },
26366     
26367     setElementSize : function(s, newSize, onComplete){
26368         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26369     },
26370     
26371     moveSplitter : function(s){
26372         var yes = Roo.SplitBar;
26373         switch(s.placement){
26374             case yes.LEFT:
26375                 s.el.setX(s.resizingEl.getRight());
26376                 break;
26377             case yes.RIGHT:
26378                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26379                 break;
26380             case yes.TOP:
26381                 s.el.setY(s.resizingEl.getBottom());
26382                 break;
26383             case yes.BOTTOM:
26384                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26385                 break;
26386         }
26387     }
26388 };
26389
26390 /**
26391  * Orientation constant - Create a vertical SplitBar
26392  * @static
26393  * @type Number
26394  */
26395 Roo.SplitBar.VERTICAL = 1;
26396
26397 /**
26398  * Orientation constant - Create a horizontal SplitBar
26399  * @static
26400  * @type Number
26401  */
26402 Roo.SplitBar.HORIZONTAL = 2;
26403
26404 /**
26405  * Placement constant - The resizing element is to the left of the splitter element
26406  * @static
26407  * @type Number
26408  */
26409 Roo.SplitBar.LEFT = 1;
26410
26411 /**
26412  * Placement constant - The resizing element is to the right of the splitter element
26413  * @static
26414  * @type Number
26415  */
26416 Roo.SplitBar.RIGHT = 2;
26417
26418 /**
26419  * Placement constant - The resizing element is positioned above the splitter element
26420  * @static
26421  * @type Number
26422  */
26423 Roo.SplitBar.TOP = 3;
26424
26425 /**
26426  * Placement constant - The resizing element is positioned under splitter element
26427  * @static
26428  * @type Number
26429  */
26430 Roo.SplitBar.BOTTOM = 4;
26431 /*
26432  * Based on:
26433  * Ext JS Library 1.1.1
26434  * Copyright(c) 2006-2007, Ext JS, LLC.
26435  *
26436  * Originally Released Under LGPL - original licence link has changed is not relivant.
26437  *
26438  * Fork - LGPL
26439  * <script type="text/javascript">
26440  */
26441
26442 /**
26443  * @class Roo.View
26444  * @extends Roo.util.Observable
26445  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26446  * This class also supports single and multi selection modes. <br>
26447  * Create a data model bound view:
26448  <pre><code>
26449  var store = new Roo.data.Store(...);
26450
26451  var view = new Roo.View({
26452     el : "my-element",
26453     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26454  
26455     singleSelect: true,
26456     selectedClass: "ydataview-selected",
26457     store: store
26458  });
26459
26460  // listen for node click?
26461  view.on("click", function(vw, index, node, e){
26462  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26463  });
26464
26465  // load XML data
26466  dataModel.load("foobar.xml");
26467  </code></pre>
26468  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26469  * <br><br>
26470  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26471  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26472  * 
26473  * Note: old style constructor is still suported (container, template, config)
26474  * 
26475  * @constructor
26476  * Create a new View
26477  * @param {Object} config The config object
26478  * 
26479  */
26480 Roo.View = function(config, depreciated_tpl, depreciated_config){
26481     
26482     this.parent = false;
26483     
26484     if (typeof(depreciated_tpl) == 'undefined') {
26485         // new way.. - universal constructor.
26486         Roo.apply(this, config);
26487         this.el  = Roo.get(this.el);
26488     } else {
26489         // old format..
26490         this.el  = Roo.get(config);
26491         this.tpl = depreciated_tpl;
26492         Roo.apply(this, depreciated_config);
26493     }
26494     this.wrapEl  = this.el.wrap().wrap();
26495     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26496     
26497     
26498     if(typeof(this.tpl) == "string"){
26499         this.tpl = new Roo.Template(this.tpl);
26500     } else {
26501         // support xtype ctors..
26502         this.tpl = new Roo.factory(this.tpl, Roo);
26503     }
26504     
26505     
26506     this.tpl.compile();
26507     
26508     /** @private */
26509     this.addEvents({
26510         /**
26511          * @event beforeclick
26512          * Fires before a click is processed. Returns false to cancel the default action.
26513          * @param {Roo.View} this
26514          * @param {Number} index The index of the target node
26515          * @param {HTMLElement} node The target node
26516          * @param {Roo.EventObject} e The raw event object
26517          */
26518             "beforeclick" : true,
26519         /**
26520          * @event click
26521          * Fires when a template node is clicked.
26522          * @param {Roo.View} this
26523          * @param {Number} index The index of the target node
26524          * @param {HTMLElement} node The target node
26525          * @param {Roo.EventObject} e The raw event object
26526          */
26527             "click" : true,
26528         /**
26529          * @event dblclick
26530          * Fires when a template node is double clicked.
26531          * @param {Roo.View} this
26532          * @param {Number} index The index of the target node
26533          * @param {HTMLElement} node The target node
26534          * @param {Roo.EventObject} e The raw event object
26535          */
26536             "dblclick" : true,
26537         /**
26538          * @event contextmenu
26539          * Fires when a template node is right clicked.
26540          * @param {Roo.View} this
26541          * @param {Number} index The index of the target node
26542          * @param {HTMLElement} node The target node
26543          * @param {Roo.EventObject} e The raw event object
26544          */
26545             "contextmenu" : true,
26546         /**
26547          * @event selectionchange
26548          * Fires when the selected nodes change.
26549          * @param {Roo.View} this
26550          * @param {Array} selections Array of the selected nodes
26551          */
26552             "selectionchange" : true,
26553     
26554         /**
26555          * @event beforeselect
26556          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26557          * @param {Roo.View} this
26558          * @param {HTMLElement} node The node to be selected
26559          * @param {Array} selections Array of currently selected nodes
26560          */
26561             "beforeselect" : true,
26562         /**
26563          * @event preparedata
26564          * Fires on every row to render, to allow you to change the data.
26565          * @param {Roo.View} this
26566          * @param {Object} data to be rendered (change this)
26567          */
26568           "preparedata" : true
26569           
26570           
26571         });
26572
26573
26574
26575     this.el.on({
26576         "click": this.onClick,
26577         "dblclick": this.onDblClick,
26578         "contextmenu": this.onContextMenu,
26579         scope:this
26580     });
26581
26582     this.selections = [];
26583     this.nodes = [];
26584     this.cmp = new Roo.CompositeElementLite([]);
26585     if(this.store){
26586         this.store = Roo.factory(this.store, Roo.data);
26587         this.setStore(this.store, true);
26588     }
26589     
26590     if ( this.footer && this.footer.xtype) {
26591            
26592          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26593         
26594         this.footer.dataSource = this.store;
26595         this.footer.container = fctr;
26596         this.footer = Roo.factory(this.footer, Roo);
26597         fctr.insertFirst(this.el);
26598         
26599         // this is a bit insane - as the paging toolbar seems to detach the el..
26600 //        dom.parentNode.parentNode.parentNode
26601          // they get detached?
26602     }
26603     
26604     
26605     Roo.View.superclass.constructor.call(this);
26606     
26607     
26608 };
26609
26610 Roo.extend(Roo.View, Roo.util.Observable, {
26611     
26612      /**
26613      * @cfg {Roo.data.Store} store Data store to load data from.
26614      */
26615     store : false,
26616     
26617     /**
26618      * @cfg {String|Roo.Element} el The container element.
26619      */
26620     el : '',
26621     
26622     /**
26623      * @cfg {String|Roo.Template} tpl The template used by this View 
26624      */
26625     tpl : false,
26626     /**
26627      * @cfg {String} dataName the named area of the template to use as the data area
26628      *                          Works with domtemplates roo-name="name"
26629      */
26630     dataName: false,
26631     /**
26632      * @cfg {String} selectedClass The css class to add to selected nodes
26633      */
26634     selectedClass : "x-view-selected",
26635      /**
26636      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26637      */
26638     emptyText : "",
26639     
26640     /**
26641      * @cfg {String} text to display on mask (default Loading)
26642      */
26643     mask : false,
26644     /**
26645      * @cfg {Boolean} multiSelect Allow multiple selection
26646      */
26647     multiSelect : false,
26648     /**
26649      * @cfg {Boolean} singleSelect Allow single selection
26650      */
26651     singleSelect:  false,
26652     
26653     /**
26654      * @cfg {Boolean} toggleSelect - selecting 
26655      */
26656     toggleSelect : false,
26657     
26658     /**
26659      * @cfg {Boolean} tickable - selecting 
26660      */
26661     tickable : false,
26662     
26663     /**
26664      * Returns the element this view is bound to.
26665      * @return {Roo.Element}
26666      */
26667     getEl : function(){
26668         return this.wrapEl;
26669     },
26670     
26671     
26672
26673     /**
26674      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26675      */
26676     refresh : function(){
26677         //Roo.log('refresh');
26678         var t = this.tpl;
26679         
26680         // if we are using something like 'domtemplate', then
26681         // the what gets used is:
26682         // t.applySubtemplate(NAME, data, wrapping data..)
26683         // the outer template then get' applied with
26684         //     the store 'extra data'
26685         // and the body get's added to the
26686         //      roo-name="data" node?
26687         //      <span class='roo-tpl-{name}'></span> ?????
26688         
26689         
26690         
26691         this.clearSelections();
26692         this.el.update("");
26693         var html = [];
26694         var records = this.store.getRange();
26695         if(records.length < 1) {
26696             
26697             // is this valid??  = should it render a template??
26698             
26699             this.el.update(this.emptyText);
26700             return;
26701         }
26702         var el = this.el;
26703         if (this.dataName) {
26704             this.el.update(t.apply(this.store.meta)); //????
26705             el = this.el.child('.roo-tpl-' + this.dataName);
26706         }
26707         
26708         for(var i = 0, len = records.length; i < len; i++){
26709             var data = this.prepareData(records[i].data, i, records[i]);
26710             this.fireEvent("preparedata", this, data, i, records[i]);
26711             
26712             var d = Roo.apply({}, data);
26713             
26714             if(this.tickable){
26715                 Roo.apply(d, {'roo-id' : Roo.id()});
26716                 
26717                 var _this = this;
26718             
26719                 Roo.each(this.parent.item, function(item){
26720                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26721                         return;
26722                     }
26723                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26724                 });
26725             }
26726             
26727             html[html.length] = Roo.util.Format.trim(
26728                 this.dataName ?
26729                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26730                     t.apply(d)
26731             );
26732         }
26733         
26734         
26735         
26736         el.update(html.join(""));
26737         this.nodes = el.dom.childNodes;
26738         this.updateIndexes(0);
26739     },
26740     
26741
26742     /**
26743      * Function to override to reformat the data that is sent to
26744      * the template for each node.
26745      * DEPRICATED - use the preparedata event handler.
26746      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26747      * a JSON object for an UpdateManager bound view).
26748      */
26749     prepareData : function(data, index, record)
26750     {
26751         this.fireEvent("preparedata", this, data, index, record);
26752         return data;
26753     },
26754
26755     onUpdate : function(ds, record){
26756         // Roo.log('on update');   
26757         this.clearSelections();
26758         var index = this.store.indexOf(record);
26759         var n = this.nodes[index];
26760         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26761         n.parentNode.removeChild(n);
26762         this.updateIndexes(index, index);
26763     },
26764
26765     
26766     
26767 // --------- FIXME     
26768     onAdd : function(ds, records, index)
26769     {
26770         //Roo.log(['on Add', ds, records, index] );        
26771         this.clearSelections();
26772         if(this.nodes.length == 0){
26773             this.refresh();
26774             return;
26775         }
26776         var n = this.nodes[index];
26777         for(var i = 0, len = records.length; i < len; i++){
26778             var d = this.prepareData(records[i].data, i, records[i]);
26779             if(n){
26780                 this.tpl.insertBefore(n, d);
26781             }else{
26782                 
26783                 this.tpl.append(this.el, d);
26784             }
26785         }
26786         this.updateIndexes(index);
26787     },
26788
26789     onRemove : function(ds, record, index){
26790        // Roo.log('onRemove');
26791         this.clearSelections();
26792         var el = this.dataName  ?
26793             this.el.child('.roo-tpl-' + this.dataName) :
26794             this.el; 
26795         
26796         el.dom.removeChild(this.nodes[index]);
26797         this.updateIndexes(index);
26798     },
26799
26800     /**
26801      * Refresh an individual node.
26802      * @param {Number} index
26803      */
26804     refreshNode : function(index){
26805         this.onUpdate(this.store, this.store.getAt(index));
26806     },
26807
26808     updateIndexes : function(startIndex, endIndex){
26809         var ns = this.nodes;
26810         startIndex = startIndex || 0;
26811         endIndex = endIndex || ns.length - 1;
26812         for(var i = startIndex; i <= endIndex; i++){
26813             ns[i].nodeIndex = i;
26814         }
26815     },
26816
26817     /**
26818      * Changes the data store this view uses and refresh the view.
26819      * @param {Store} store
26820      */
26821     setStore : function(store, initial){
26822         if(!initial && this.store){
26823             this.store.un("datachanged", this.refresh);
26824             this.store.un("add", this.onAdd);
26825             this.store.un("remove", this.onRemove);
26826             this.store.un("update", this.onUpdate);
26827             this.store.un("clear", this.refresh);
26828             this.store.un("beforeload", this.onBeforeLoad);
26829             this.store.un("load", this.onLoad);
26830             this.store.un("loadexception", this.onLoad);
26831         }
26832         if(store){
26833           
26834             store.on("datachanged", this.refresh, this);
26835             store.on("add", this.onAdd, this);
26836             store.on("remove", this.onRemove, this);
26837             store.on("update", this.onUpdate, this);
26838             store.on("clear", this.refresh, this);
26839             store.on("beforeload", this.onBeforeLoad, this);
26840             store.on("load", this.onLoad, this);
26841             store.on("loadexception", this.onLoad, this);
26842         }
26843         
26844         if(store){
26845             this.refresh();
26846         }
26847     },
26848     /**
26849      * onbeforeLoad - masks the loading area.
26850      *
26851      */
26852     onBeforeLoad : function(store,opts)
26853     {
26854          //Roo.log('onBeforeLoad');   
26855         if (!opts.add) {
26856             this.el.update("");
26857         }
26858         this.el.mask(this.mask ? this.mask : "Loading" ); 
26859     },
26860     onLoad : function ()
26861     {
26862         this.el.unmask();
26863     },
26864     
26865
26866     /**
26867      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26868      * @param {HTMLElement} node
26869      * @return {HTMLElement} The template node
26870      */
26871     findItemFromChild : function(node){
26872         var el = this.dataName  ?
26873             this.el.child('.roo-tpl-' + this.dataName,true) :
26874             this.el.dom; 
26875         
26876         if(!node || node.parentNode == el){
26877                     return node;
26878             }
26879             var p = node.parentNode;
26880             while(p && p != el){
26881             if(p.parentNode == el){
26882                 return p;
26883             }
26884             p = p.parentNode;
26885         }
26886             return null;
26887     },
26888
26889     /** @ignore */
26890     onClick : function(e){
26891         var item = this.findItemFromChild(e.getTarget());
26892         if(item){
26893             var index = this.indexOf(item);
26894             if(this.onItemClick(item, index, e) !== false){
26895                 this.fireEvent("click", this, index, item, e);
26896             }
26897         }else{
26898             this.clearSelections();
26899         }
26900     },
26901
26902     /** @ignore */
26903     onContextMenu : function(e){
26904         var item = this.findItemFromChild(e.getTarget());
26905         if(item){
26906             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26907         }
26908     },
26909
26910     /** @ignore */
26911     onDblClick : function(e){
26912         var item = this.findItemFromChild(e.getTarget());
26913         if(item){
26914             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26915         }
26916     },
26917
26918     onItemClick : function(item, index, e)
26919     {
26920         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26921             return false;
26922         }
26923         if (this.toggleSelect) {
26924             var m = this.isSelected(item) ? 'unselect' : 'select';
26925             //Roo.log(m);
26926             var _t = this;
26927             _t[m](item, true, false);
26928             return true;
26929         }
26930         if(this.multiSelect || this.singleSelect){
26931             if(this.multiSelect && e.shiftKey && this.lastSelection){
26932                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26933             }else{
26934                 this.select(item, this.multiSelect && e.ctrlKey);
26935                 this.lastSelection = item;
26936             }
26937             
26938             if(!this.tickable){
26939                 e.preventDefault();
26940             }
26941             
26942         }
26943         return true;
26944     },
26945
26946     /**
26947      * Get the number of selected nodes.
26948      * @return {Number}
26949      */
26950     getSelectionCount : function(){
26951         return this.selections.length;
26952     },
26953
26954     /**
26955      * Get the currently selected nodes.
26956      * @return {Array} An array of HTMLElements
26957      */
26958     getSelectedNodes : function(){
26959         return this.selections;
26960     },
26961
26962     /**
26963      * Get the indexes of the selected nodes.
26964      * @return {Array}
26965      */
26966     getSelectedIndexes : function(){
26967         var indexes = [], s = this.selections;
26968         for(var i = 0, len = s.length; i < len; i++){
26969             indexes.push(s[i].nodeIndex);
26970         }
26971         return indexes;
26972     },
26973
26974     /**
26975      * Clear all selections
26976      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26977      */
26978     clearSelections : function(suppressEvent){
26979         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26980             this.cmp.elements = this.selections;
26981             this.cmp.removeClass(this.selectedClass);
26982             this.selections = [];
26983             if(!suppressEvent){
26984                 this.fireEvent("selectionchange", this, this.selections);
26985             }
26986         }
26987     },
26988
26989     /**
26990      * Returns true if the passed node is selected
26991      * @param {HTMLElement/Number} node The node or node index
26992      * @return {Boolean}
26993      */
26994     isSelected : function(node){
26995         var s = this.selections;
26996         if(s.length < 1){
26997             return false;
26998         }
26999         node = this.getNode(node);
27000         return s.indexOf(node) !== -1;
27001     },
27002
27003     /**
27004      * Selects nodes.
27005      * @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
27006      * @param {Boolean} keepExisting (optional) true to keep existing selections
27007      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27008      */
27009     select : function(nodeInfo, keepExisting, suppressEvent){
27010         if(nodeInfo instanceof Array){
27011             if(!keepExisting){
27012                 this.clearSelections(true);
27013             }
27014             for(var i = 0, len = nodeInfo.length; i < len; i++){
27015                 this.select(nodeInfo[i], true, true);
27016             }
27017             return;
27018         } 
27019         var node = this.getNode(nodeInfo);
27020         if(!node || this.isSelected(node)){
27021             return; // already selected.
27022         }
27023         if(!keepExisting){
27024             this.clearSelections(true);
27025         }
27026         
27027         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27028             Roo.fly(node).addClass(this.selectedClass);
27029             this.selections.push(node);
27030             if(!suppressEvent){
27031                 this.fireEvent("selectionchange", this, this.selections);
27032             }
27033         }
27034         
27035         
27036     },
27037       /**
27038      * Unselects nodes.
27039      * @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
27040      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27041      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27042      */
27043     unselect : function(nodeInfo, keepExisting, suppressEvent)
27044     {
27045         if(nodeInfo instanceof Array){
27046             Roo.each(this.selections, function(s) {
27047                 this.unselect(s, nodeInfo);
27048             }, this);
27049             return;
27050         }
27051         var node = this.getNode(nodeInfo);
27052         if(!node || !this.isSelected(node)){
27053             //Roo.log("not selected");
27054             return; // not selected.
27055         }
27056         // fireevent???
27057         var ns = [];
27058         Roo.each(this.selections, function(s) {
27059             if (s == node ) {
27060                 Roo.fly(node).removeClass(this.selectedClass);
27061
27062                 return;
27063             }
27064             ns.push(s);
27065         },this);
27066         
27067         this.selections= ns;
27068         this.fireEvent("selectionchange", this, this.selections);
27069     },
27070
27071     /**
27072      * Gets a template node.
27073      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27074      * @return {HTMLElement} The node or null if it wasn't found
27075      */
27076     getNode : function(nodeInfo){
27077         if(typeof nodeInfo == "string"){
27078             return document.getElementById(nodeInfo);
27079         }else if(typeof nodeInfo == "number"){
27080             return this.nodes[nodeInfo];
27081         }
27082         return nodeInfo;
27083     },
27084
27085     /**
27086      * Gets a range template nodes.
27087      * @param {Number} startIndex
27088      * @param {Number} endIndex
27089      * @return {Array} An array of nodes
27090      */
27091     getNodes : function(start, end){
27092         var ns = this.nodes;
27093         start = start || 0;
27094         end = typeof end == "undefined" ? ns.length - 1 : end;
27095         var nodes = [];
27096         if(start <= end){
27097             for(var i = start; i <= end; i++){
27098                 nodes.push(ns[i]);
27099             }
27100         } else{
27101             for(var i = start; i >= end; i--){
27102                 nodes.push(ns[i]);
27103             }
27104         }
27105         return nodes;
27106     },
27107
27108     /**
27109      * Finds the index of the passed node
27110      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27111      * @return {Number} The index of the node or -1
27112      */
27113     indexOf : function(node){
27114         node = this.getNode(node);
27115         if(typeof node.nodeIndex == "number"){
27116             return node.nodeIndex;
27117         }
27118         var ns = this.nodes;
27119         for(var i = 0, len = ns.length; i < len; i++){
27120             if(ns[i] == node){
27121                 return i;
27122             }
27123         }
27124         return -1;
27125     }
27126 });
27127 /*
27128  * Based on:
27129  * Ext JS Library 1.1.1
27130  * Copyright(c) 2006-2007, Ext JS, LLC.
27131  *
27132  * Originally Released Under LGPL - original licence link has changed is not relivant.
27133  *
27134  * Fork - LGPL
27135  * <script type="text/javascript">
27136  */
27137
27138 /**
27139  * @class Roo.JsonView
27140  * @extends Roo.View
27141  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27142 <pre><code>
27143 var view = new Roo.JsonView({
27144     container: "my-element",
27145     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27146     multiSelect: true, 
27147     jsonRoot: "data" 
27148 });
27149
27150 // listen for node click?
27151 view.on("click", function(vw, index, node, e){
27152     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27153 });
27154
27155 // direct load of JSON data
27156 view.load("foobar.php");
27157
27158 // Example from my blog list
27159 var tpl = new Roo.Template(
27160     '&lt;div class="entry"&gt;' +
27161     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27162     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27163     "&lt;/div&gt;&lt;hr /&gt;"
27164 );
27165
27166 var moreView = new Roo.JsonView({
27167     container :  "entry-list", 
27168     template : tpl,
27169     jsonRoot: "posts"
27170 });
27171 moreView.on("beforerender", this.sortEntries, this);
27172 moreView.load({
27173     url: "/blog/get-posts.php",
27174     params: "allposts=true",
27175     text: "Loading Blog Entries..."
27176 });
27177 </code></pre>
27178
27179 * Note: old code is supported with arguments : (container, template, config)
27180
27181
27182  * @constructor
27183  * Create a new JsonView
27184  * 
27185  * @param {Object} config The config object
27186  * 
27187  */
27188 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27189     
27190     
27191     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27192
27193     var um = this.el.getUpdateManager();
27194     um.setRenderer(this);
27195     um.on("update", this.onLoad, this);
27196     um.on("failure", this.onLoadException, this);
27197
27198     /**
27199      * @event beforerender
27200      * Fires before rendering of the downloaded JSON data.
27201      * @param {Roo.JsonView} this
27202      * @param {Object} data The JSON data loaded
27203      */
27204     /**
27205      * @event load
27206      * Fires when data is loaded.
27207      * @param {Roo.JsonView} this
27208      * @param {Object} data The JSON data loaded
27209      * @param {Object} response The raw Connect response object
27210      */
27211     /**
27212      * @event loadexception
27213      * Fires when loading fails.
27214      * @param {Roo.JsonView} this
27215      * @param {Object} response The raw Connect response object
27216      */
27217     this.addEvents({
27218         'beforerender' : true,
27219         'load' : true,
27220         'loadexception' : true
27221     });
27222 };
27223 Roo.extend(Roo.JsonView, Roo.View, {
27224     /**
27225      * @type {String} The root property in the loaded JSON object that contains the data
27226      */
27227     jsonRoot : "",
27228
27229     /**
27230      * Refreshes the view.
27231      */
27232     refresh : function(){
27233         this.clearSelections();
27234         this.el.update("");
27235         var html = [];
27236         var o = this.jsonData;
27237         if(o && o.length > 0){
27238             for(var i = 0, len = o.length; i < len; i++){
27239                 var data = this.prepareData(o[i], i, o);
27240                 html[html.length] = this.tpl.apply(data);
27241             }
27242         }else{
27243             html.push(this.emptyText);
27244         }
27245         this.el.update(html.join(""));
27246         this.nodes = this.el.dom.childNodes;
27247         this.updateIndexes(0);
27248     },
27249
27250     /**
27251      * 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.
27252      * @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:
27253      <pre><code>
27254      view.load({
27255          url: "your-url.php",
27256          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27257          callback: yourFunction,
27258          scope: yourObject, //(optional scope)
27259          discardUrl: false,
27260          nocache: false,
27261          text: "Loading...",
27262          timeout: 30,
27263          scripts: false
27264      });
27265      </code></pre>
27266      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27267      * 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.
27268      * @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}
27269      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27270      * @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.
27271      */
27272     load : function(){
27273         var um = this.el.getUpdateManager();
27274         um.update.apply(um, arguments);
27275     },
27276
27277     // note - render is a standard framework call...
27278     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27279     render : function(el, response){
27280         
27281         this.clearSelections();
27282         this.el.update("");
27283         var o;
27284         try{
27285             if (response != '') {
27286                 o = Roo.util.JSON.decode(response.responseText);
27287                 if(this.jsonRoot){
27288                     
27289                     o = o[this.jsonRoot];
27290                 }
27291             }
27292         } catch(e){
27293         }
27294         /**
27295          * The current JSON data or null
27296          */
27297         this.jsonData = o;
27298         this.beforeRender();
27299         this.refresh();
27300     },
27301
27302 /**
27303  * Get the number of records in the current JSON dataset
27304  * @return {Number}
27305  */
27306     getCount : function(){
27307         return this.jsonData ? this.jsonData.length : 0;
27308     },
27309
27310 /**
27311  * Returns the JSON object for the specified node(s)
27312  * @param {HTMLElement/Array} node The node or an array of nodes
27313  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27314  * you get the JSON object for the node
27315  */
27316     getNodeData : function(node){
27317         if(node instanceof Array){
27318             var data = [];
27319             for(var i = 0, len = node.length; i < len; i++){
27320                 data.push(this.getNodeData(node[i]));
27321             }
27322             return data;
27323         }
27324         return this.jsonData[this.indexOf(node)] || null;
27325     },
27326
27327     beforeRender : function(){
27328         this.snapshot = this.jsonData;
27329         if(this.sortInfo){
27330             this.sort.apply(this, this.sortInfo);
27331         }
27332         this.fireEvent("beforerender", this, this.jsonData);
27333     },
27334
27335     onLoad : function(el, o){
27336         this.fireEvent("load", this, this.jsonData, o);
27337     },
27338
27339     onLoadException : function(el, o){
27340         this.fireEvent("loadexception", this, o);
27341     },
27342
27343 /**
27344  * Filter the data by a specific property.
27345  * @param {String} property A property on your JSON objects
27346  * @param {String/RegExp} value Either string that the property values
27347  * should start with, or a RegExp to test against the property
27348  */
27349     filter : function(property, value){
27350         if(this.jsonData){
27351             var data = [];
27352             var ss = this.snapshot;
27353             if(typeof value == "string"){
27354                 var vlen = value.length;
27355                 if(vlen == 0){
27356                     this.clearFilter();
27357                     return;
27358                 }
27359                 value = value.toLowerCase();
27360                 for(var i = 0, len = ss.length; i < len; i++){
27361                     var o = ss[i];
27362                     if(o[property].substr(0, vlen).toLowerCase() == value){
27363                         data.push(o);
27364                     }
27365                 }
27366             } else if(value.exec){ // regex?
27367                 for(var i = 0, len = ss.length; i < len; i++){
27368                     var o = ss[i];
27369                     if(value.test(o[property])){
27370                         data.push(o);
27371                     }
27372                 }
27373             } else{
27374                 return;
27375             }
27376             this.jsonData = data;
27377             this.refresh();
27378         }
27379     },
27380
27381 /**
27382  * Filter by a function. The passed function will be called with each
27383  * object in the current dataset. If the function returns true the value is kept,
27384  * otherwise it is filtered.
27385  * @param {Function} fn
27386  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27387  */
27388     filterBy : function(fn, scope){
27389         if(this.jsonData){
27390             var data = [];
27391             var ss = this.snapshot;
27392             for(var i = 0, len = ss.length; i < len; i++){
27393                 var o = ss[i];
27394                 if(fn.call(scope || this, o)){
27395                     data.push(o);
27396                 }
27397             }
27398             this.jsonData = data;
27399             this.refresh();
27400         }
27401     },
27402
27403 /**
27404  * Clears the current filter.
27405  */
27406     clearFilter : function(){
27407         if(this.snapshot && this.jsonData != this.snapshot){
27408             this.jsonData = this.snapshot;
27409             this.refresh();
27410         }
27411     },
27412
27413
27414 /**
27415  * Sorts the data for this view and refreshes it.
27416  * @param {String} property A property on your JSON objects to sort on
27417  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27418  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27419  */
27420     sort : function(property, dir, sortType){
27421         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27422         if(this.jsonData){
27423             var p = property;
27424             var dsc = dir && dir.toLowerCase() == "desc";
27425             var f = function(o1, o2){
27426                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27427                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27428                 ;
27429                 if(v1 < v2){
27430                     return dsc ? +1 : -1;
27431                 } else if(v1 > v2){
27432                     return dsc ? -1 : +1;
27433                 } else{
27434                     return 0;
27435                 }
27436             };
27437             this.jsonData.sort(f);
27438             this.refresh();
27439             if(this.jsonData != this.snapshot){
27440                 this.snapshot.sort(f);
27441             }
27442         }
27443     }
27444 });/*
27445  * Based on:
27446  * Ext JS Library 1.1.1
27447  * Copyright(c) 2006-2007, Ext JS, LLC.
27448  *
27449  * Originally Released Under LGPL - original licence link has changed is not relivant.
27450  *
27451  * Fork - LGPL
27452  * <script type="text/javascript">
27453  */
27454  
27455
27456 /**
27457  * @class Roo.ColorPalette
27458  * @extends Roo.Component
27459  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27460  * Here's an example of typical usage:
27461  * <pre><code>
27462 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27463 cp.render('my-div');
27464
27465 cp.on('select', function(palette, selColor){
27466     // do something with selColor
27467 });
27468 </code></pre>
27469  * @constructor
27470  * Create a new ColorPalette
27471  * @param {Object} config The config object
27472  */
27473 Roo.ColorPalette = function(config){
27474     Roo.ColorPalette.superclass.constructor.call(this, config);
27475     this.addEvents({
27476         /**
27477              * @event select
27478              * Fires when a color is selected
27479              * @param {ColorPalette} this
27480              * @param {String} color The 6-digit color hex code (without the # symbol)
27481              */
27482         select: true
27483     });
27484
27485     if(this.handler){
27486         this.on("select", this.handler, this.scope, true);
27487     }
27488 };
27489 Roo.extend(Roo.ColorPalette, Roo.Component, {
27490     /**
27491      * @cfg {String} itemCls
27492      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27493      */
27494     itemCls : "x-color-palette",
27495     /**
27496      * @cfg {String} value
27497      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27498      * the hex codes are case-sensitive.
27499      */
27500     value : null,
27501     clickEvent:'click',
27502     // private
27503     ctype: "Roo.ColorPalette",
27504
27505     /**
27506      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27507      */
27508     allowReselect : false,
27509
27510     /**
27511      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27512      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27513      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27514      * of colors with the width setting until the box is symmetrical.</p>
27515      * <p>You can override individual colors if needed:</p>
27516      * <pre><code>
27517 var cp = new Roo.ColorPalette();
27518 cp.colors[0] = "FF0000";  // change the first box to red
27519 </code></pre>
27520
27521 Or you can provide a custom array of your own for complete control:
27522 <pre><code>
27523 var cp = new Roo.ColorPalette();
27524 cp.colors = ["000000", "993300", "333300"];
27525 </code></pre>
27526      * @type Array
27527      */
27528     colors : [
27529         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27530         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27531         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27532         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27533         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27534     ],
27535
27536     // private
27537     onRender : function(container, position){
27538         var t = new Roo.MasterTemplate(
27539             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27540         );
27541         var c = this.colors;
27542         for(var i = 0, len = c.length; i < len; i++){
27543             t.add([c[i]]);
27544         }
27545         var el = document.createElement("div");
27546         el.className = this.itemCls;
27547         t.overwrite(el);
27548         container.dom.insertBefore(el, position);
27549         this.el = Roo.get(el);
27550         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27551         if(this.clickEvent != 'click'){
27552             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27553         }
27554     },
27555
27556     // private
27557     afterRender : function(){
27558         Roo.ColorPalette.superclass.afterRender.call(this);
27559         if(this.value){
27560             var s = this.value;
27561             this.value = null;
27562             this.select(s);
27563         }
27564     },
27565
27566     // private
27567     handleClick : function(e, t){
27568         e.preventDefault();
27569         if(!this.disabled){
27570             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27571             this.select(c.toUpperCase());
27572         }
27573     },
27574
27575     /**
27576      * Selects the specified color in the palette (fires the select event)
27577      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27578      */
27579     select : function(color){
27580         color = color.replace("#", "");
27581         if(color != this.value || this.allowReselect){
27582             var el = this.el;
27583             if(this.value){
27584                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27585             }
27586             el.child("a.color-"+color).addClass("x-color-palette-sel");
27587             this.value = color;
27588             this.fireEvent("select", this, color);
27589         }
27590     }
27591 });/*
27592  * Based on:
27593  * Ext JS Library 1.1.1
27594  * Copyright(c) 2006-2007, Ext JS, LLC.
27595  *
27596  * Originally Released Under LGPL - original licence link has changed is not relivant.
27597  *
27598  * Fork - LGPL
27599  * <script type="text/javascript">
27600  */
27601  
27602 /**
27603  * @class Roo.DatePicker
27604  * @extends Roo.Component
27605  * Simple date picker class.
27606  * @constructor
27607  * Create a new DatePicker
27608  * @param {Object} config The config object
27609  */
27610 Roo.DatePicker = function(config){
27611     Roo.DatePicker.superclass.constructor.call(this, config);
27612
27613     this.value = config && config.value ?
27614                  config.value.clearTime() : new Date().clearTime();
27615
27616     this.addEvents({
27617         /**
27618              * @event select
27619              * Fires when a date is selected
27620              * @param {DatePicker} this
27621              * @param {Date} date The selected date
27622              */
27623         'select': true,
27624         /**
27625              * @event monthchange
27626              * Fires when the displayed month changes 
27627              * @param {DatePicker} this
27628              * @param {Date} date The selected month
27629              */
27630         'monthchange': true
27631     });
27632
27633     if(this.handler){
27634         this.on("select", this.handler,  this.scope || this);
27635     }
27636     // build the disabledDatesRE
27637     if(!this.disabledDatesRE && this.disabledDates){
27638         var dd = this.disabledDates;
27639         var re = "(?:";
27640         for(var i = 0; i < dd.length; i++){
27641             re += dd[i];
27642             if(i != dd.length-1) {
27643                 re += "|";
27644             }
27645         }
27646         this.disabledDatesRE = new RegExp(re + ")");
27647     }
27648 };
27649
27650 Roo.extend(Roo.DatePicker, Roo.Component, {
27651     /**
27652      * @cfg {String} todayText
27653      * The text to display on the button that selects the current date (defaults to "Today")
27654      */
27655     todayText : "Today",
27656     /**
27657      * @cfg {String} okText
27658      * The text to display on the ok button
27659      */
27660     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27661     /**
27662      * @cfg {String} cancelText
27663      * The text to display on the cancel button
27664      */
27665     cancelText : "Cancel",
27666     /**
27667      * @cfg {String} todayTip
27668      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27669      */
27670     todayTip : "{0} (Spacebar)",
27671     /**
27672      * @cfg {Date} minDate
27673      * Minimum allowable date (JavaScript date object, defaults to null)
27674      */
27675     minDate : null,
27676     /**
27677      * @cfg {Date} maxDate
27678      * Maximum allowable date (JavaScript date object, defaults to null)
27679      */
27680     maxDate : null,
27681     /**
27682      * @cfg {String} minText
27683      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27684      */
27685     minText : "This date is before the minimum date",
27686     /**
27687      * @cfg {String} maxText
27688      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27689      */
27690     maxText : "This date is after the maximum date",
27691     /**
27692      * @cfg {String} format
27693      * The default date format string which can be overriden for localization support.  The format must be
27694      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27695      */
27696     format : "m/d/y",
27697     /**
27698      * @cfg {Array} disabledDays
27699      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27700      */
27701     disabledDays : null,
27702     /**
27703      * @cfg {String} disabledDaysText
27704      * The tooltip to display when the date falls on a disabled day (defaults to "")
27705      */
27706     disabledDaysText : "",
27707     /**
27708      * @cfg {RegExp} disabledDatesRE
27709      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27710      */
27711     disabledDatesRE : null,
27712     /**
27713      * @cfg {String} disabledDatesText
27714      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27715      */
27716     disabledDatesText : "",
27717     /**
27718      * @cfg {Boolean} constrainToViewport
27719      * True to constrain the date picker to the viewport (defaults to true)
27720      */
27721     constrainToViewport : true,
27722     /**
27723      * @cfg {Array} monthNames
27724      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27725      */
27726     monthNames : Date.monthNames,
27727     /**
27728      * @cfg {Array} dayNames
27729      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27730      */
27731     dayNames : Date.dayNames,
27732     /**
27733      * @cfg {String} nextText
27734      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27735      */
27736     nextText: 'Next Month (Control+Right)',
27737     /**
27738      * @cfg {String} prevText
27739      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27740      */
27741     prevText: 'Previous Month (Control+Left)',
27742     /**
27743      * @cfg {String} monthYearText
27744      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27745      */
27746     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27747     /**
27748      * @cfg {Number} startDay
27749      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27750      */
27751     startDay : 0,
27752     /**
27753      * @cfg {Bool} showClear
27754      * Show a clear button (usefull for date form elements that can be blank.)
27755      */
27756     
27757     showClear: false,
27758     
27759     /**
27760      * Sets the value of the date field
27761      * @param {Date} value The date to set
27762      */
27763     setValue : function(value){
27764         var old = this.value;
27765         
27766         if (typeof(value) == 'string') {
27767          
27768             value = Date.parseDate(value, this.format);
27769         }
27770         if (!value) {
27771             value = new Date();
27772         }
27773         
27774         this.value = value.clearTime(true);
27775         if(this.el){
27776             this.update(this.value);
27777         }
27778     },
27779
27780     /**
27781      * Gets the current selected value of the date field
27782      * @return {Date} The selected date
27783      */
27784     getValue : function(){
27785         return this.value;
27786     },
27787
27788     // private
27789     focus : function(){
27790         if(this.el){
27791             this.update(this.activeDate);
27792         }
27793     },
27794
27795     // privateval
27796     onRender : function(container, position){
27797         
27798         var m = [
27799              '<table cellspacing="0">',
27800                 '<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>',
27801                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27802         var dn = this.dayNames;
27803         for(var i = 0; i < 7; i++){
27804             var d = this.startDay+i;
27805             if(d > 6){
27806                 d = d-7;
27807             }
27808             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27809         }
27810         m[m.length] = "</tr></thead><tbody><tr>";
27811         for(var i = 0; i < 42; i++) {
27812             if(i % 7 == 0 && i != 0){
27813                 m[m.length] = "</tr><tr>";
27814             }
27815             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27816         }
27817         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27818             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27819
27820         var el = document.createElement("div");
27821         el.className = "x-date-picker";
27822         el.innerHTML = m.join("");
27823
27824         container.dom.insertBefore(el, position);
27825
27826         this.el = Roo.get(el);
27827         this.eventEl = Roo.get(el.firstChild);
27828
27829         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27830             handler: this.showPrevMonth,
27831             scope: this,
27832             preventDefault:true,
27833             stopDefault:true
27834         });
27835
27836         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27837             handler: this.showNextMonth,
27838             scope: this,
27839             preventDefault:true,
27840             stopDefault:true
27841         });
27842
27843         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27844
27845         this.monthPicker = this.el.down('div.x-date-mp');
27846         this.monthPicker.enableDisplayMode('block');
27847         
27848         var kn = new Roo.KeyNav(this.eventEl, {
27849             "left" : function(e){
27850                 e.ctrlKey ?
27851                     this.showPrevMonth() :
27852                     this.update(this.activeDate.add("d", -1));
27853             },
27854
27855             "right" : function(e){
27856                 e.ctrlKey ?
27857                     this.showNextMonth() :
27858                     this.update(this.activeDate.add("d", 1));
27859             },
27860
27861             "up" : function(e){
27862                 e.ctrlKey ?
27863                     this.showNextYear() :
27864                     this.update(this.activeDate.add("d", -7));
27865             },
27866
27867             "down" : function(e){
27868                 e.ctrlKey ?
27869                     this.showPrevYear() :
27870                     this.update(this.activeDate.add("d", 7));
27871             },
27872
27873             "pageUp" : function(e){
27874                 this.showNextMonth();
27875             },
27876
27877             "pageDown" : function(e){
27878                 this.showPrevMonth();
27879             },
27880
27881             "enter" : function(e){
27882                 e.stopPropagation();
27883                 return true;
27884             },
27885
27886             scope : this
27887         });
27888
27889         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27890
27891         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27892
27893         this.el.unselectable();
27894         
27895         this.cells = this.el.select("table.x-date-inner tbody td");
27896         this.textNodes = this.el.query("table.x-date-inner tbody span");
27897
27898         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27899             text: "&#160;",
27900             tooltip: this.monthYearText
27901         });
27902
27903         this.mbtn.on('click', this.showMonthPicker, this);
27904         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27905
27906
27907         var today = (new Date()).dateFormat(this.format);
27908         
27909         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27910         if (this.showClear) {
27911             baseTb.add( new Roo.Toolbar.Fill());
27912         }
27913         baseTb.add({
27914             text: String.format(this.todayText, today),
27915             tooltip: String.format(this.todayTip, today),
27916             handler: this.selectToday,
27917             scope: this
27918         });
27919         
27920         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27921             
27922         //});
27923         if (this.showClear) {
27924             
27925             baseTb.add( new Roo.Toolbar.Fill());
27926             baseTb.add({
27927                 text: '&#160;',
27928                 cls: 'x-btn-icon x-btn-clear',
27929                 handler: function() {
27930                     //this.value = '';
27931                     this.fireEvent("select", this, '');
27932                 },
27933                 scope: this
27934             });
27935         }
27936         
27937         
27938         if(Roo.isIE){
27939             this.el.repaint();
27940         }
27941         this.update(this.value);
27942     },
27943
27944     createMonthPicker : function(){
27945         if(!this.monthPicker.dom.firstChild){
27946             var buf = ['<table border="0" cellspacing="0">'];
27947             for(var i = 0; i < 6; i++){
27948                 buf.push(
27949                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27950                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27951                     i == 0 ?
27952                     '<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>' :
27953                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27954                 );
27955             }
27956             buf.push(
27957                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27958                     this.okText,
27959                     '</button><button type="button" class="x-date-mp-cancel">',
27960                     this.cancelText,
27961                     '</button></td></tr>',
27962                 '</table>'
27963             );
27964             this.monthPicker.update(buf.join(''));
27965             this.monthPicker.on('click', this.onMonthClick, this);
27966             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27967
27968             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27969             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27970
27971             this.mpMonths.each(function(m, a, i){
27972                 i += 1;
27973                 if((i%2) == 0){
27974                     m.dom.xmonth = 5 + Math.round(i * .5);
27975                 }else{
27976                     m.dom.xmonth = Math.round((i-1) * .5);
27977                 }
27978             });
27979         }
27980     },
27981
27982     showMonthPicker : function(){
27983         this.createMonthPicker();
27984         var size = this.el.getSize();
27985         this.monthPicker.setSize(size);
27986         this.monthPicker.child('table').setSize(size);
27987
27988         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27989         this.updateMPMonth(this.mpSelMonth);
27990         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27991         this.updateMPYear(this.mpSelYear);
27992
27993         this.monthPicker.slideIn('t', {duration:.2});
27994     },
27995
27996     updateMPYear : function(y){
27997         this.mpyear = y;
27998         var ys = this.mpYears.elements;
27999         for(var i = 1; i <= 10; i++){
28000             var td = ys[i-1], y2;
28001             if((i%2) == 0){
28002                 y2 = y + Math.round(i * .5);
28003                 td.firstChild.innerHTML = y2;
28004                 td.xyear = y2;
28005             }else{
28006                 y2 = y - (5-Math.round(i * .5));
28007                 td.firstChild.innerHTML = y2;
28008                 td.xyear = y2;
28009             }
28010             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28011         }
28012     },
28013
28014     updateMPMonth : function(sm){
28015         this.mpMonths.each(function(m, a, i){
28016             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28017         });
28018     },
28019
28020     selectMPMonth: function(m){
28021         
28022     },
28023
28024     onMonthClick : function(e, t){
28025         e.stopEvent();
28026         var el = new Roo.Element(t), pn;
28027         if(el.is('button.x-date-mp-cancel')){
28028             this.hideMonthPicker();
28029         }
28030         else if(el.is('button.x-date-mp-ok')){
28031             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28032             this.hideMonthPicker();
28033         }
28034         else if(pn = el.up('td.x-date-mp-month', 2)){
28035             this.mpMonths.removeClass('x-date-mp-sel');
28036             pn.addClass('x-date-mp-sel');
28037             this.mpSelMonth = pn.dom.xmonth;
28038         }
28039         else if(pn = el.up('td.x-date-mp-year', 2)){
28040             this.mpYears.removeClass('x-date-mp-sel');
28041             pn.addClass('x-date-mp-sel');
28042             this.mpSelYear = pn.dom.xyear;
28043         }
28044         else if(el.is('a.x-date-mp-prev')){
28045             this.updateMPYear(this.mpyear-10);
28046         }
28047         else if(el.is('a.x-date-mp-next')){
28048             this.updateMPYear(this.mpyear+10);
28049         }
28050     },
28051
28052     onMonthDblClick : function(e, t){
28053         e.stopEvent();
28054         var el = new Roo.Element(t), pn;
28055         if(pn = el.up('td.x-date-mp-month', 2)){
28056             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28057             this.hideMonthPicker();
28058         }
28059         else if(pn = el.up('td.x-date-mp-year', 2)){
28060             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28061             this.hideMonthPicker();
28062         }
28063     },
28064
28065     hideMonthPicker : function(disableAnim){
28066         if(this.monthPicker){
28067             if(disableAnim === true){
28068                 this.monthPicker.hide();
28069             }else{
28070                 this.monthPicker.slideOut('t', {duration:.2});
28071             }
28072         }
28073     },
28074
28075     // private
28076     showPrevMonth : function(e){
28077         this.update(this.activeDate.add("mo", -1));
28078     },
28079
28080     // private
28081     showNextMonth : function(e){
28082         this.update(this.activeDate.add("mo", 1));
28083     },
28084
28085     // private
28086     showPrevYear : function(){
28087         this.update(this.activeDate.add("y", -1));
28088     },
28089
28090     // private
28091     showNextYear : function(){
28092         this.update(this.activeDate.add("y", 1));
28093     },
28094
28095     // private
28096     handleMouseWheel : function(e){
28097         var delta = e.getWheelDelta();
28098         if(delta > 0){
28099             this.showPrevMonth();
28100             e.stopEvent();
28101         } else if(delta < 0){
28102             this.showNextMonth();
28103             e.stopEvent();
28104         }
28105     },
28106
28107     // private
28108     handleDateClick : function(e, t){
28109         e.stopEvent();
28110         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28111             this.setValue(new Date(t.dateValue));
28112             this.fireEvent("select", this, this.value);
28113         }
28114     },
28115
28116     // private
28117     selectToday : function(){
28118         this.setValue(new Date().clearTime());
28119         this.fireEvent("select", this, this.value);
28120     },
28121
28122     // private
28123     update : function(date)
28124     {
28125         var vd = this.activeDate;
28126         this.activeDate = date;
28127         if(vd && this.el){
28128             var t = date.getTime();
28129             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28130                 this.cells.removeClass("x-date-selected");
28131                 this.cells.each(function(c){
28132                    if(c.dom.firstChild.dateValue == t){
28133                        c.addClass("x-date-selected");
28134                        setTimeout(function(){
28135                             try{c.dom.firstChild.focus();}catch(e){}
28136                        }, 50);
28137                        return false;
28138                    }
28139                 });
28140                 return;
28141             }
28142         }
28143         
28144         var days = date.getDaysInMonth();
28145         var firstOfMonth = date.getFirstDateOfMonth();
28146         var startingPos = firstOfMonth.getDay()-this.startDay;
28147
28148         if(startingPos <= this.startDay){
28149             startingPos += 7;
28150         }
28151
28152         var pm = date.add("mo", -1);
28153         var prevStart = pm.getDaysInMonth()-startingPos;
28154
28155         var cells = this.cells.elements;
28156         var textEls = this.textNodes;
28157         days += startingPos;
28158
28159         // convert everything to numbers so it's fast
28160         var day = 86400000;
28161         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28162         var today = new Date().clearTime().getTime();
28163         var sel = date.clearTime().getTime();
28164         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28165         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28166         var ddMatch = this.disabledDatesRE;
28167         var ddText = this.disabledDatesText;
28168         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28169         var ddaysText = this.disabledDaysText;
28170         var format = this.format;
28171
28172         var setCellClass = function(cal, cell){
28173             cell.title = "";
28174             var t = d.getTime();
28175             cell.firstChild.dateValue = t;
28176             if(t == today){
28177                 cell.className += " x-date-today";
28178                 cell.title = cal.todayText;
28179             }
28180             if(t == sel){
28181                 cell.className += " x-date-selected";
28182                 setTimeout(function(){
28183                     try{cell.firstChild.focus();}catch(e){}
28184                 }, 50);
28185             }
28186             // disabling
28187             if(t < min) {
28188                 cell.className = " x-date-disabled";
28189                 cell.title = cal.minText;
28190                 return;
28191             }
28192             if(t > max) {
28193                 cell.className = " x-date-disabled";
28194                 cell.title = cal.maxText;
28195                 return;
28196             }
28197             if(ddays){
28198                 if(ddays.indexOf(d.getDay()) != -1){
28199                     cell.title = ddaysText;
28200                     cell.className = " x-date-disabled";
28201                 }
28202             }
28203             if(ddMatch && format){
28204                 var fvalue = d.dateFormat(format);
28205                 if(ddMatch.test(fvalue)){
28206                     cell.title = ddText.replace("%0", fvalue);
28207                     cell.className = " x-date-disabled";
28208                 }
28209             }
28210         };
28211
28212         var i = 0;
28213         for(; i < startingPos; i++) {
28214             textEls[i].innerHTML = (++prevStart);
28215             d.setDate(d.getDate()+1);
28216             cells[i].className = "x-date-prevday";
28217             setCellClass(this, cells[i]);
28218         }
28219         for(; i < days; i++){
28220             intDay = i - startingPos + 1;
28221             textEls[i].innerHTML = (intDay);
28222             d.setDate(d.getDate()+1);
28223             cells[i].className = "x-date-active";
28224             setCellClass(this, cells[i]);
28225         }
28226         var extraDays = 0;
28227         for(; i < 42; i++) {
28228              textEls[i].innerHTML = (++extraDays);
28229              d.setDate(d.getDate()+1);
28230              cells[i].className = "x-date-nextday";
28231              setCellClass(this, cells[i]);
28232         }
28233
28234         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28235         this.fireEvent('monthchange', this, date);
28236         
28237         if(!this.internalRender){
28238             var main = this.el.dom.firstChild;
28239             var w = main.offsetWidth;
28240             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28241             Roo.fly(main).setWidth(w);
28242             this.internalRender = true;
28243             // opera does not respect the auto grow header center column
28244             // then, after it gets a width opera refuses to recalculate
28245             // without a second pass
28246             if(Roo.isOpera && !this.secondPass){
28247                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28248                 this.secondPass = true;
28249                 this.update.defer(10, this, [date]);
28250             }
28251         }
28252         
28253         
28254     }
28255 });        /*
28256  * Based on:
28257  * Ext JS Library 1.1.1
28258  * Copyright(c) 2006-2007, Ext JS, LLC.
28259  *
28260  * Originally Released Under LGPL - original licence link has changed is not relivant.
28261  *
28262  * Fork - LGPL
28263  * <script type="text/javascript">
28264  */
28265 /**
28266  * @class Roo.TabPanel
28267  * @extends Roo.util.Observable
28268  * A lightweight tab container.
28269  * <br><br>
28270  * Usage:
28271  * <pre><code>
28272 // basic tabs 1, built from existing content
28273 var tabs = new Roo.TabPanel("tabs1");
28274 tabs.addTab("script", "View Script");
28275 tabs.addTab("markup", "View Markup");
28276 tabs.activate("script");
28277
28278 // more advanced tabs, built from javascript
28279 var jtabs = new Roo.TabPanel("jtabs");
28280 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28281
28282 // set up the UpdateManager
28283 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28284 var updater = tab2.getUpdateManager();
28285 updater.setDefaultUrl("ajax1.htm");
28286 tab2.on('activate', updater.refresh, updater, true);
28287
28288 // Use setUrl for Ajax loading
28289 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28290 tab3.setUrl("ajax2.htm", null, true);
28291
28292 // Disabled tab
28293 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28294 tab4.disable();
28295
28296 jtabs.activate("jtabs-1");
28297  * </code></pre>
28298  * @constructor
28299  * Create a new TabPanel.
28300  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28301  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28302  */
28303 Roo.TabPanel = function(container, config){
28304     /**
28305     * The container element for this TabPanel.
28306     * @type Roo.Element
28307     */
28308     this.el = Roo.get(container, true);
28309     if(config){
28310         if(typeof config == "boolean"){
28311             this.tabPosition = config ? "bottom" : "top";
28312         }else{
28313             Roo.apply(this, config);
28314         }
28315     }
28316     if(this.tabPosition == "bottom"){
28317         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28318         this.el.addClass("x-tabs-bottom");
28319     }
28320     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28321     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28322     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28323     if(Roo.isIE){
28324         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28325     }
28326     if(this.tabPosition != "bottom"){
28327         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28328          * @type Roo.Element
28329          */
28330         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28331         this.el.addClass("x-tabs-top");
28332     }
28333     this.items = [];
28334
28335     this.bodyEl.setStyle("position", "relative");
28336
28337     this.active = null;
28338     this.activateDelegate = this.activate.createDelegate(this);
28339
28340     this.addEvents({
28341         /**
28342          * @event tabchange
28343          * Fires when the active tab changes
28344          * @param {Roo.TabPanel} this
28345          * @param {Roo.TabPanelItem} activePanel The new active tab
28346          */
28347         "tabchange": true,
28348         /**
28349          * @event beforetabchange
28350          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28351          * @param {Roo.TabPanel} this
28352          * @param {Object} e Set cancel to true on this object to cancel the tab change
28353          * @param {Roo.TabPanelItem} tab The tab being changed to
28354          */
28355         "beforetabchange" : true
28356     });
28357
28358     Roo.EventManager.onWindowResize(this.onResize, this);
28359     this.cpad = this.el.getPadding("lr");
28360     this.hiddenCount = 0;
28361
28362
28363     // toolbar on the tabbar support...
28364     if (this.toolbar) {
28365         var tcfg = this.toolbar;
28366         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28367         this.toolbar = new Roo.Toolbar(tcfg);
28368         if (Roo.isSafari) {
28369             var tbl = tcfg.container.child('table', true);
28370             tbl.setAttribute('width', '100%');
28371         }
28372         
28373     }
28374    
28375
28376
28377     Roo.TabPanel.superclass.constructor.call(this);
28378 };
28379
28380 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28381     /*
28382      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28383      */
28384     tabPosition : "top",
28385     /*
28386      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28387      */
28388     currentTabWidth : 0,
28389     /*
28390      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28391      */
28392     minTabWidth : 40,
28393     /*
28394      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28395      */
28396     maxTabWidth : 250,
28397     /*
28398      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28399      */
28400     preferredTabWidth : 175,
28401     /*
28402      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28403      */
28404     resizeTabs : false,
28405     /*
28406      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28407      */
28408     monitorResize : true,
28409     /*
28410      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28411      */
28412     toolbar : false,
28413
28414     /**
28415      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28416      * @param {String} id The id of the div to use <b>or create</b>
28417      * @param {String} text The text for the tab
28418      * @param {String} content (optional) Content to put in the TabPanelItem body
28419      * @param {Boolean} closable (optional) True to create a close icon on the tab
28420      * @return {Roo.TabPanelItem} The created TabPanelItem
28421      */
28422     addTab : function(id, text, content, closable){
28423         var item = new Roo.TabPanelItem(this, id, text, closable);
28424         this.addTabItem(item);
28425         if(content){
28426             item.setContent(content);
28427         }
28428         return item;
28429     },
28430
28431     /**
28432      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28433      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28434      * @return {Roo.TabPanelItem}
28435      */
28436     getTab : function(id){
28437         return this.items[id];
28438     },
28439
28440     /**
28441      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28442      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28443      */
28444     hideTab : function(id){
28445         var t = this.items[id];
28446         if(!t.isHidden()){
28447            t.setHidden(true);
28448            this.hiddenCount++;
28449            this.autoSizeTabs();
28450         }
28451     },
28452
28453     /**
28454      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28455      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28456      */
28457     unhideTab : function(id){
28458         var t = this.items[id];
28459         if(t.isHidden()){
28460            t.setHidden(false);
28461            this.hiddenCount--;
28462            this.autoSizeTabs();
28463         }
28464     },
28465
28466     /**
28467      * Adds an existing {@link Roo.TabPanelItem}.
28468      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28469      */
28470     addTabItem : function(item){
28471         this.items[item.id] = item;
28472         this.items.push(item);
28473         if(this.resizeTabs){
28474            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28475            this.autoSizeTabs();
28476         }else{
28477             item.autoSize();
28478         }
28479     },
28480
28481     /**
28482      * Removes a {@link Roo.TabPanelItem}.
28483      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28484      */
28485     removeTab : function(id){
28486         var items = this.items;
28487         var tab = items[id];
28488         if(!tab) { return; }
28489         var index = items.indexOf(tab);
28490         if(this.active == tab && items.length > 1){
28491             var newTab = this.getNextAvailable(index);
28492             if(newTab) {
28493                 newTab.activate();
28494             }
28495         }
28496         this.stripEl.dom.removeChild(tab.pnode.dom);
28497         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28498             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28499         }
28500         items.splice(index, 1);
28501         delete this.items[tab.id];
28502         tab.fireEvent("close", tab);
28503         tab.purgeListeners();
28504         this.autoSizeTabs();
28505     },
28506
28507     getNextAvailable : function(start){
28508         var items = this.items;
28509         var index = start;
28510         // look for a next tab that will slide over to
28511         // replace the one being removed
28512         while(index < items.length){
28513             var item = items[++index];
28514             if(item && !item.isHidden()){
28515                 return item;
28516             }
28517         }
28518         // if one isn't found select the previous tab (on the left)
28519         index = start;
28520         while(index >= 0){
28521             var item = items[--index];
28522             if(item && !item.isHidden()){
28523                 return item;
28524             }
28525         }
28526         return null;
28527     },
28528
28529     /**
28530      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28531      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28532      */
28533     disableTab : function(id){
28534         var tab = this.items[id];
28535         if(tab && this.active != tab){
28536             tab.disable();
28537         }
28538     },
28539
28540     /**
28541      * Enables a {@link Roo.TabPanelItem} that is disabled.
28542      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28543      */
28544     enableTab : function(id){
28545         var tab = this.items[id];
28546         tab.enable();
28547     },
28548
28549     /**
28550      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28551      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28552      * @return {Roo.TabPanelItem} The TabPanelItem.
28553      */
28554     activate : function(id){
28555         var tab = this.items[id];
28556         if(!tab){
28557             return null;
28558         }
28559         if(tab == this.active || tab.disabled){
28560             return tab;
28561         }
28562         var e = {};
28563         this.fireEvent("beforetabchange", this, e, tab);
28564         if(e.cancel !== true && !tab.disabled){
28565             if(this.active){
28566                 this.active.hide();
28567             }
28568             this.active = this.items[id];
28569             this.active.show();
28570             this.fireEvent("tabchange", this, this.active);
28571         }
28572         return tab;
28573     },
28574
28575     /**
28576      * Gets the active {@link Roo.TabPanelItem}.
28577      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28578      */
28579     getActiveTab : function(){
28580         return this.active;
28581     },
28582
28583     /**
28584      * Updates the tab body element to fit the height of the container element
28585      * for overflow scrolling
28586      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28587      */
28588     syncHeight : function(targetHeight){
28589         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28590         var bm = this.bodyEl.getMargins();
28591         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28592         this.bodyEl.setHeight(newHeight);
28593         return newHeight;
28594     },
28595
28596     onResize : function(){
28597         if(this.monitorResize){
28598             this.autoSizeTabs();
28599         }
28600     },
28601
28602     /**
28603      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28604      */
28605     beginUpdate : function(){
28606         this.updating = true;
28607     },
28608
28609     /**
28610      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28611      */
28612     endUpdate : function(){
28613         this.updating = false;
28614         this.autoSizeTabs();
28615     },
28616
28617     /**
28618      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28619      */
28620     autoSizeTabs : function(){
28621         var count = this.items.length;
28622         var vcount = count - this.hiddenCount;
28623         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28624             return;
28625         }
28626         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28627         var availWidth = Math.floor(w / vcount);
28628         var b = this.stripBody;
28629         if(b.getWidth() > w){
28630             var tabs = this.items;
28631             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28632             if(availWidth < this.minTabWidth){
28633                 /*if(!this.sleft){    // incomplete scrolling code
28634                     this.createScrollButtons();
28635                 }
28636                 this.showScroll();
28637                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28638             }
28639         }else{
28640             if(this.currentTabWidth < this.preferredTabWidth){
28641                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28642             }
28643         }
28644     },
28645
28646     /**
28647      * Returns the number of tabs in this TabPanel.
28648      * @return {Number}
28649      */
28650      getCount : function(){
28651          return this.items.length;
28652      },
28653
28654     /**
28655      * Resizes all the tabs to the passed width
28656      * @param {Number} The new width
28657      */
28658     setTabWidth : function(width){
28659         this.currentTabWidth = width;
28660         for(var i = 0, len = this.items.length; i < len; i++) {
28661                 if(!this.items[i].isHidden()) {
28662                 this.items[i].setWidth(width);
28663             }
28664         }
28665     },
28666
28667     /**
28668      * Destroys this TabPanel
28669      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28670      */
28671     destroy : function(removeEl){
28672         Roo.EventManager.removeResizeListener(this.onResize, this);
28673         for(var i = 0, len = this.items.length; i < len; i++){
28674             this.items[i].purgeListeners();
28675         }
28676         if(removeEl === true){
28677             this.el.update("");
28678             this.el.remove();
28679         }
28680     }
28681 });
28682
28683 /**
28684  * @class Roo.TabPanelItem
28685  * @extends Roo.util.Observable
28686  * Represents an individual item (tab plus body) in a TabPanel.
28687  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28688  * @param {String} id The id of this TabPanelItem
28689  * @param {String} text The text for the tab of this TabPanelItem
28690  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28691  */
28692 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28693     /**
28694      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28695      * @type Roo.TabPanel
28696      */
28697     this.tabPanel = tabPanel;
28698     /**
28699      * The id for this TabPanelItem
28700      * @type String
28701      */
28702     this.id = id;
28703     /** @private */
28704     this.disabled = false;
28705     /** @private */
28706     this.text = text;
28707     /** @private */
28708     this.loaded = false;
28709     this.closable = closable;
28710
28711     /**
28712      * The body element for this TabPanelItem.
28713      * @type Roo.Element
28714      */
28715     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28716     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28717     this.bodyEl.setStyle("display", "block");
28718     this.bodyEl.setStyle("zoom", "1");
28719     this.hideAction();
28720
28721     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28722     /** @private */
28723     this.el = Roo.get(els.el, true);
28724     this.inner = Roo.get(els.inner, true);
28725     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28726     this.pnode = Roo.get(els.el.parentNode, true);
28727     this.el.on("mousedown", this.onTabMouseDown, this);
28728     this.el.on("click", this.onTabClick, this);
28729     /** @private */
28730     if(closable){
28731         var c = Roo.get(els.close, true);
28732         c.dom.title = this.closeText;
28733         c.addClassOnOver("close-over");
28734         c.on("click", this.closeClick, this);
28735      }
28736
28737     this.addEvents({
28738          /**
28739          * @event activate
28740          * Fires when this tab becomes the active tab.
28741          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28742          * @param {Roo.TabPanelItem} this
28743          */
28744         "activate": true,
28745         /**
28746          * @event beforeclose
28747          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28748          * @param {Roo.TabPanelItem} this
28749          * @param {Object} e Set cancel to true on this object to cancel the close.
28750          */
28751         "beforeclose": true,
28752         /**
28753          * @event close
28754          * Fires when this tab is closed.
28755          * @param {Roo.TabPanelItem} this
28756          */
28757          "close": true,
28758         /**
28759          * @event deactivate
28760          * Fires when this tab is no longer the active tab.
28761          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28762          * @param {Roo.TabPanelItem} this
28763          */
28764          "deactivate" : true
28765     });
28766     this.hidden = false;
28767
28768     Roo.TabPanelItem.superclass.constructor.call(this);
28769 };
28770
28771 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28772     purgeListeners : function(){
28773        Roo.util.Observable.prototype.purgeListeners.call(this);
28774        this.el.removeAllListeners();
28775     },
28776     /**
28777      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28778      */
28779     show : function(){
28780         this.pnode.addClass("on");
28781         this.showAction();
28782         if(Roo.isOpera){
28783             this.tabPanel.stripWrap.repaint();
28784         }
28785         this.fireEvent("activate", this.tabPanel, this);
28786     },
28787
28788     /**
28789      * Returns true if this tab is the active tab.
28790      * @return {Boolean}
28791      */
28792     isActive : function(){
28793         return this.tabPanel.getActiveTab() == this;
28794     },
28795
28796     /**
28797      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28798      */
28799     hide : function(){
28800         this.pnode.removeClass("on");
28801         this.hideAction();
28802         this.fireEvent("deactivate", this.tabPanel, this);
28803     },
28804
28805     hideAction : function(){
28806         this.bodyEl.hide();
28807         this.bodyEl.setStyle("position", "absolute");
28808         this.bodyEl.setLeft("-20000px");
28809         this.bodyEl.setTop("-20000px");
28810     },
28811
28812     showAction : function(){
28813         this.bodyEl.setStyle("position", "relative");
28814         this.bodyEl.setTop("");
28815         this.bodyEl.setLeft("");
28816         this.bodyEl.show();
28817     },
28818
28819     /**
28820      * Set the tooltip for the tab.
28821      * @param {String} tooltip The tab's tooltip
28822      */
28823     setTooltip : function(text){
28824         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28825             this.textEl.dom.qtip = text;
28826             this.textEl.dom.removeAttribute('title');
28827         }else{
28828             this.textEl.dom.title = text;
28829         }
28830     },
28831
28832     onTabClick : function(e){
28833         e.preventDefault();
28834         this.tabPanel.activate(this.id);
28835     },
28836
28837     onTabMouseDown : function(e){
28838         e.preventDefault();
28839         this.tabPanel.activate(this.id);
28840     },
28841
28842     getWidth : function(){
28843         return this.inner.getWidth();
28844     },
28845
28846     setWidth : function(width){
28847         var iwidth = width - this.pnode.getPadding("lr");
28848         this.inner.setWidth(iwidth);
28849         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28850         this.pnode.setWidth(width);
28851     },
28852
28853     /**
28854      * Show or hide the tab
28855      * @param {Boolean} hidden True to hide or false to show.
28856      */
28857     setHidden : function(hidden){
28858         this.hidden = hidden;
28859         this.pnode.setStyle("display", hidden ? "none" : "");
28860     },
28861
28862     /**
28863      * Returns true if this tab is "hidden"
28864      * @return {Boolean}
28865      */
28866     isHidden : function(){
28867         return this.hidden;
28868     },
28869
28870     /**
28871      * Returns the text for this tab
28872      * @return {String}
28873      */
28874     getText : function(){
28875         return this.text;
28876     },
28877
28878     autoSize : function(){
28879         //this.el.beginMeasure();
28880         this.textEl.setWidth(1);
28881         /*
28882          *  #2804 [new] Tabs in Roojs
28883          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28884          */
28885         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28886         //this.el.endMeasure();
28887     },
28888
28889     /**
28890      * Sets the text for the tab (Note: this also sets the tooltip text)
28891      * @param {String} text The tab's text and tooltip
28892      */
28893     setText : function(text){
28894         this.text = text;
28895         this.textEl.update(text);
28896         this.setTooltip(text);
28897         if(!this.tabPanel.resizeTabs){
28898             this.autoSize();
28899         }
28900     },
28901     /**
28902      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28903      */
28904     activate : function(){
28905         this.tabPanel.activate(this.id);
28906     },
28907
28908     /**
28909      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28910      */
28911     disable : function(){
28912         if(this.tabPanel.active != this){
28913             this.disabled = true;
28914             this.pnode.addClass("disabled");
28915         }
28916     },
28917
28918     /**
28919      * Enables this TabPanelItem if it was previously disabled.
28920      */
28921     enable : function(){
28922         this.disabled = false;
28923         this.pnode.removeClass("disabled");
28924     },
28925
28926     /**
28927      * Sets the content for this TabPanelItem.
28928      * @param {String} content The content
28929      * @param {Boolean} loadScripts true to look for and load scripts
28930      */
28931     setContent : function(content, loadScripts){
28932         this.bodyEl.update(content, loadScripts);
28933     },
28934
28935     /**
28936      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28937      * @return {Roo.UpdateManager} The UpdateManager
28938      */
28939     getUpdateManager : function(){
28940         return this.bodyEl.getUpdateManager();
28941     },
28942
28943     /**
28944      * Set a URL to be used to load the content for this TabPanelItem.
28945      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28946      * @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)
28947      * @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)
28948      * @return {Roo.UpdateManager} The UpdateManager
28949      */
28950     setUrl : function(url, params, loadOnce){
28951         if(this.refreshDelegate){
28952             this.un('activate', this.refreshDelegate);
28953         }
28954         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28955         this.on("activate", this.refreshDelegate);
28956         return this.bodyEl.getUpdateManager();
28957     },
28958
28959     /** @private */
28960     _handleRefresh : function(url, params, loadOnce){
28961         if(!loadOnce || !this.loaded){
28962             var updater = this.bodyEl.getUpdateManager();
28963             updater.update(url, params, this._setLoaded.createDelegate(this));
28964         }
28965     },
28966
28967     /**
28968      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28969      *   Will fail silently if the setUrl method has not been called.
28970      *   This does not activate the panel, just updates its content.
28971      */
28972     refresh : function(){
28973         if(this.refreshDelegate){
28974            this.loaded = false;
28975            this.refreshDelegate();
28976         }
28977     },
28978
28979     /** @private */
28980     _setLoaded : function(){
28981         this.loaded = true;
28982     },
28983
28984     /** @private */
28985     closeClick : function(e){
28986         var o = {};
28987         e.stopEvent();
28988         this.fireEvent("beforeclose", this, o);
28989         if(o.cancel !== true){
28990             this.tabPanel.removeTab(this.id);
28991         }
28992     },
28993     /**
28994      * The text displayed in the tooltip for the close icon.
28995      * @type String
28996      */
28997     closeText : "Close this tab"
28998 });
28999
29000 /** @private */
29001 Roo.TabPanel.prototype.createStrip = function(container){
29002     var strip = document.createElement("div");
29003     strip.className = "x-tabs-wrap";
29004     container.appendChild(strip);
29005     return strip;
29006 };
29007 /** @private */
29008 Roo.TabPanel.prototype.createStripList = function(strip){
29009     // div wrapper for retard IE
29010     // returns the "tr" element.
29011     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29012         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29013         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29014     return strip.firstChild.firstChild.firstChild.firstChild;
29015 };
29016 /** @private */
29017 Roo.TabPanel.prototype.createBody = function(container){
29018     var body = document.createElement("div");
29019     Roo.id(body, "tab-body");
29020     Roo.fly(body).addClass("x-tabs-body");
29021     container.appendChild(body);
29022     return body;
29023 };
29024 /** @private */
29025 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29026     var body = Roo.getDom(id);
29027     if(!body){
29028         body = document.createElement("div");
29029         body.id = id;
29030     }
29031     Roo.fly(body).addClass("x-tabs-item-body");
29032     bodyEl.insertBefore(body, bodyEl.firstChild);
29033     return body;
29034 };
29035 /** @private */
29036 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29037     var td = document.createElement("td");
29038     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29039     //stripEl.appendChild(td);
29040     if(closable){
29041         td.className = "x-tabs-closable";
29042         if(!this.closeTpl){
29043             this.closeTpl = new Roo.Template(
29044                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29045                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29046                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29047             );
29048         }
29049         var el = this.closeTpl.overwrite(td, {"text": text});
29050         var close = el.getElementsByTagName("div")[0];
29051         var inner = el.getElementsByTagName("em")[0];
29052         return {"el": el, "close": close, "inner": inner};
29053     } else {
29054         if(!this.tabTpl){
29055             this.tabTpl = new Roo.Template(
29056                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29057                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29058             );
29059         }
29060         var el = this.tabTpl.overwrite(td, {"text": text});
29061         var inner = el.getElementsByTagName("em")[0];
29062         return {"el": el, "inner": inner};
29063     }
29064 };/*
29065  * Based on:
29066  * Ext JS Library 1.1.1
29067  * Copyright(c) 2006-2007, Ext JS, LLC.
29068  *
29069  * Originally Released Under LGPL - original licence link has changed is not relivant.
29070  *
29071  * Fork - LGPL
29072  * <script type="text/javascript">
29073  */
29074
29075 /**
29076  * @class Roo.Button
29077  * @extends Roo.util.Observable
29078  * Simple Button class
29079  * @cfg {String} text The button text
29080  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29081  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29082  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29083  * @cfg {Object} scope The scope of the handler
29084  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29085  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29086  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29087  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29088  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29089  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29090    applies if enableToggle = true)
29091  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29092  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29093   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29094  * @constructor
29095  * Create a new button
29096  * @param {Object} config The config object
29097  */
29098 Roo.Button = function(renderTo, config)
29099 {
29100     if (!config) {
29101         config = renderTo;
29102         renderTo = config.renderTo || false;
29103     }
29104     
29105     Roo.apply(this, config);
29106     this.addEvents({
29107         /**
29108              * @event click
29109              * Fires when this button is clicked
29110              * @param {Button} this
29111              * @param {EventObject} e The click event
29112              */
29113             "click" : true,
29114         /**
29115              * @event toggle
29116              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29117              * @param {Button} this
29118              * @param {Boolean} pressed
29119              */
29120             "toggle" : true,
29121         /**
29122              * @event mouseover
29123              * Fires when the mouse hovers over the button
29124              * @param {Button} this
29125              * @param {Event} e The event object
29126              */
29127         'mouseover' : true,
29128         /**
29129              * @event mouseout
29130              * Fires when the mouse exits the button
29131              * @param {Button} this
29132              * @param {Event} e The event object
29133              */
29134         'mouseout': true,
29135          /**
29136              * @event render
29137              * Fires when the button is rendered
29138              * @param {Button} this
29139              */
29140         'render': true
29141     });
29142     if(this.menu){
29143         this.menu = Roo.menu.MenuMgr.get(this.menu);
29144     }
29145     // register listeners first!!  - so render can be captured..
29146     Roo.util.Observable.call(this);
29147     if(renderTo){
29148         this.render(renderTo);
29149     }
29150     
29151   
29152 };
29153
29154 Roo.extend(Roo.Button, Roo.util.Observable, {
29155     /**
29156      * 
29157      */
29158     
29159     /**
29160      * Read-only. True if this button is hidden
29161      * @type Boolean
29162      */
29163     hidden : false,
29164     /**
29165      * Read-only. True if this button is disabled
29166      * @type Boolean
29167      */
29168     disabled : false,
29169     /**
29170      * Read-only. True if this button is pressed (only if enableToggle = true)
29171      * @type Boolean
29172      */
29173     pressed : false,
29174
29175     /**
29176      * @cfg {Number} tabIndex 
29177      * The DOM tabIndex for this button (defaults to undefined)
29178      */
29179     tabIndex : undefined,
29180
29181     /**
29182      * @cfg {Boolean} enableToggle
29183      * True to enable pressed/not pressed toggling (defaults to false)
29184      */
29185     enableToggle: false,
29186     /**
29187      * @cfg {Mixed} menu
29188      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29189      */
29190     menu : undefined,
29191     /**
29192      * @cfg {String} menuAlign
29193      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29194      */
29195     menuAlign : "tl-bl?",
29196
29197     /**
29198      * @cfg {String} iconCls
29199      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29200      */
29201     iconCls : undefined,
29202     /**
29203      * @cfg {String} type
29204      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29205      */
29206     type : 'button',
29207
29208     // private
29209     menuClassTarget: 'tr',
29210
29211     /**
29212      * @cfg {String} clickEvent
29213      * The type of event to map to the button's event handler (defaults to 'click')
29214      */
29215     clickEvent : 'click',
29216
29217     /**
29218      * @cfg {Boolean} handleMouseEvents
29219      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29220      */
29221     handleMouseEvents : true,
29222
29223     /**
29224      * @cfg {String} tooltipType
29225      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29226      */
29227     tooltipType : 'qtip',
29228
29229     /**
29230      * @cfg {String} cls
29231      * A CSS class to apply to the button's main element.
29232      */
29233     
29234     /**
29235      * @cfg {Roo.Template} template (Optional)
29236      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29237      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29238      * require code modifications if required elements (e.g. a button) aren't present.
29239      */
29240
29241     // private
29242     render : function(renderTo){
29243         var btn;
29244         if(this.hideParent){
29245             this.parentEl = Roo.get(renderTo);
29246         }
29247         if(!this.dhconfig){
29248             if(!this.template){
29249                 if(!Roo.Button.buttonTemplate){
29250                     // hideous table template
29251                     Roo.Button.buttonTemplate = new Roo.Template(
29252                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29253                         '<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>',
29254                         "</tr></tbody></table>");
29255                 }
29256                 this.template = Roo.Button.buttonTemplate;
29257             }
29258             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29259             var btnEl = btn.child("button:first");
29260             btnEl.on('focus', this.onFocus, this);
29261             btnEl.on('blur', this.onBlur, this);
29262             if(this.cls){
29263                 btn.addClass(this.cls);
29264             }
29265             if(this.icon){
29266                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29267             }
29268             if(this.iconCls){
29269                 btnEl.addClass(this.iconCls);
29270                 if(!this.cls){
29271                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29272                 }
29273             }
29274             if(this.tabIndex !== undefined){
29275                 btnEl.dom.tabIndex = this.tabIndex;
29276             }
29277             if(this.tooltip){
29278                 if(typeof this.tooltip == 'object'){
29279                     Roo.QuickTips.tips(Roo.apply({
29280                           target: btnEl.id
29281                     }, this.tooltip));
29282                 } else {
29283                     btnEl.dom[this.tooltipType] = this.tooltip;
29284                 }
29285             }
29286         }else{
29287             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29288         }
29289         this.el = btn;
29290         if(this.id){
29291             this.el.dom.id = this.el.id = this.id;
29292         }
29293         if(this.menu){
29294             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29295             this.menu.on("show", this.onMenuShow, this);
29296             this.menu.on("hide", this.onMenuHide, this);
29297         }
29298         btn.addClass("x-btn");
29299         if(Roo.isIE && !Roo.isIE7){
29300             this.autoWidth.defer(1, this);
29301         }else{
29302             this.autoWidth();
29303         }
29304         if(this.handleMouseEvents){
29305             btn.on("mouseover", this.onMouseOver, this);
29306             btn.on("mouseout", this.onMouseOut, this);
29307             btn.on("mousedown", this.onMouseDown, this);
29308         }
29309         btn.on(this.clickEvent, this.onClick, this);
29310         //btn.on("mouseup", this.onMouseUp, this);
29311         if(this.hidden){
29312             this.hide();
29313         }
29314         if(this.disabled){
29315             this.disable();
29316         }
29317         Roo.ButtonToggleMgr.register(this);
29318         if(this.pressed){
29319             this.el.addClass("x-btn-pressed");
29320         }
29321         if(this.repeat){
29322             var repeater = new Roo.util.ClickRepeater(btn,
29323                 typeof this.repeat == "object" ? this.repeat : {}
29324             );
29325             repeater.on("click", this.onClick,  this);
29326         }
29327         
29328         this.fireEvent('render', this);
29329         
29330     },
29331     /**
29332      * Returns the button's underlying element
29333      * @return {Roo.Element} The element
29334      */
29335     getEl : function(){
29336         return this.el;  
29337     },
29338     
29339     /**
29340      * Destroys this Button and removes any listeners.
29341      */
29342     destroy : function(){
29343         Roo.ButtonToggleMgr.unregister(this);
29344         this.el.removeAllListeners();
29345         this.purgeListeners();
29346         this.el.remove();
29347     },
29348
29349     // private
29350     autoWidth : function(){
29351         if(this.el){
29352             this.el.setWidth("auto");
29353             if(Roo.isIE7 && Roo.isStrict){
29354                 var ib = this.el.child('button');
29355                 if(ib && ib.getWidth() > 20){
29356                     ib.clip();
29357                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29358                 }
29359             }
29360             if(this.minWidth){
29361                 if(this.hidden){
29362                     this.el.beginMeasure();
29363                 }
29364                 if(this.el.getWidth() < this.minWidth){
29365                     this.el.setWidth(this.minWidth);
29366                 }
29367                 if(this.hidden){
29368                     this.el.endMeasure();
29369                 }
29370             }
29371         }
29372     },
29373
29374     /**
29375      * Assigns this button's click handler
29376      * @param {Function} handler The function to call when the button is clicked
29377      * @param {Object} scope (optional) Scope for the function passed in
29378      */
29379     setHandler : function(handler, scope){
29380         this.handler = handler;
29381         this.scope = scope;  
29382     },
29383     
29384     /**
29385      * Sets this button's text
29386      * @param {String} text The button text
29387      */
29388     setText : function(text){
29389         this.text = text;
29390         if(this.el){
29391             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29392         }
29393         this.autoWidth();
29394     },
29395     
29396     /**
29397      * Gets the text for this button
29398      * @return {String} The button text
29399      */
29400     getText : function(){
29401         return this.text;  
29402     },
29403     
29404     /**
29405      * Show this button
29406      */
29407     show: function(){
29408         this.hidden = false;
29409         if(this.el){
29410             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29411         }
29412     },
29413     
29414     /**
29415      * Hide this button
29416      */
29417     hide: function(){
29418         this.hidden = true;
29419         if(this.el){
29420             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29421         }
29422     },
29423     
29424     /**
29425      * Convenience function for boolean show/hide
29426      * @param {Boolean} visible True to show, false to hide
29427      */
29428     setVisible: function(visible){
29429         if(visible) {
29430             this.show();
29431         }else{
29432             this.hide();
29433         }
29434     },
29435     
29436     /**
29437      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29438      * @param {Boolean} state (optional) Force a particular state
29439      */
29440     toggle : function(state){
29441         state = state === undefined ? !this.pressed : state;
29442         if(state != this.pressed){
29443             if(state){
29444                 this.el.addClass("x-btn-pressed");
29445                 this.pressed = true;
29446                 this.fireEvent("toggle", this, true);
29447             }else{
29448                 this.el.removeClass("x-btn-pressed");
29449                 this.pressed = false;
29450                 this.fireEvent("toggle", this, false);
29451             }
29452             if(this.toggleHandler){
29453                 this.toggleHandler.call(this.scope || this, this, state);
29454             }
29455         }
29456     },
29457     
29458     /**
29459      * Focus the button
29460      */
29461     focus : function(){
29462         this.el.child('button:first').focus();
29463     },
29464     
29465     /**
29466      * Disable this button
29467      */
29468     disable : function(){
29469         if(this.el){
29470             this.el.addClass("x-btn-disabled");
29471         }
29472         this.disabled = true;
29473     },
29474     
29475     /**
29476      * Enable this button
29477      */
29478     enable : function(){
29479         if(this.el){
29480             this.el.removeClass("x-btn-disabled");
29481         }
29482         this.disabled = false;
29483     },
29484
29485     /**
29486      * Convenience function for boolean enable/disable
29487      * @param {Boolean} enabled True to enable, false to disable
29488      */
29489     setDisabled : function(v){
29490         this[v !== true ? "enable" : "disable"]();
29491     },
29492
29493     // private
29494     onClick : function(e)
29495     {
29496         if(e){
29497             e.preventDefault();
29498         }
29499         if(e.button != 0){
29500             return;
29501         }
29502         if(!this.disabled){
29503             if(this.enableToggle){
29504                 this.toggle();
29505             }
29506             if(this.menu && !this.menu.isVisible()){
29507                 this.menu.show(this.el, this.menuAlign);
29508             }
29509             this.fireEvent("click", this, e);
29510             if(this.handler){
29511                 this.el.removeClass("x-btn-over");
29512                 this.handler.call(this.scope || this, this, e);
29513             }
29514         }
29515     },
29516     // private
29517     onMouseOver : function(e){
29518         if(!this.disabled){
29519             this.el.addClass("x-btn-over");
29520             this.fireEvent('mouseover', this, e);
29521         }
29522     },
29523     // private
29524     onMouseOut : function(e){
29525         if(!e.within(this.el,  true)){
29526             this.el.removeClass("x-btn-over");
29527             this.fireEvent('mouseout', this, e);
29528         }
29529     },
29530     // private
29531     onFocus : function(e){
29532         if(!this.disabled){
29533             this.el.addClass("x-btn-focus");
29534         }
29535     },
29536     // private
29537     onBlur : function(e){
29538         this.el.removeClass("x-btn-focus");
29539     },
29540     // private
29541     onMouseDown : function(e){
29542         if(!this.disabled && e.button == 0){
29543             this.el.addClass("x-btn-click");
29544             Roo.get(document).on('mouseup', this.onMouseUp, this);
29545         }
29546     },
29547     // private
29548     onMouseUp : function(e){
29549         if(e.button == 0){
29550             this.el.removeClass("x-btn-click");
29551             Roo.get(document).un('mouseup', this.onMouseUp, this);
29552         }
29553     },
29554     // private
29555     onMenuShow : function(e){
29556         this.el.addClass("x-btn-menu-active");
29557     },
29558     // private
29559     onMenuHide : function(e){
29560         this.el.removeClass("x-btn-menu-active");
29561     }   
29562 });
29563
29564 // Private utility class used by Button
29565 Roo.ButtonToggleMgr = function(){
29566    var groups = {};
29567    
29568    function toggleGroup(btn, state){
29569        if(state){
29570            var g = groups[btn.toggleGroup];
29571            for(var i = 0, l = g.length; i < l; i++){
29572                if(g[i] != btn){
29573                    g[i].toggle(false);
29574                }
29575            }
29576        }
29577    }
29578    
29579    return {
29580        register : function(btn){
29581            if(!btn.toggleGroup){
29582                return;
29583            }
29584            var g = groups[btn.toggleGroup];
29585            if(!g){
29586                g = groups[btn.toggleGroup] = [];
29587            }
29588            g.push(btn);
29589            btn.on("toggle", toggleGroup);
29590        },
29591        
29592        unregister : function(btn){
29593            if(!btn.toggleGroup){
29594                return;
29595            }
29596            var g = groups[btn.toggleGroup];
29597            if(g){
29598                g.remove(btn);
29599                btn.un("toggle", toggleGroup);
29600            }
29601        }
29602    };
29603 }();/*
29604  * Based on:
29605  * Ext JS Library 1.1.1
29606  * Copyright(c) 2006-2007, Ext JS, LLC.
29607  *
29608  * Originally Released Under LGPL - original licence link has changed is not relivant.
29609  *
29610  * Fork - LGPL
29611  * <script type="text/javascript">
29612  */
29613  
29614 /**
29615  * @class Roo.SplitButton
29616  * @extends Roo.Button
29617  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29618  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29619  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29620  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29621  * @cfg {String} arrowTooltip The title attribute of the arrow
29622  * @constructor
29623  * Create a new menu button
29624  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29625  * @param {Object} config The config object
29626  */
29627 Roo.SplitButton = function(renderTo, config){
29628     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29629     /**
29630      * @event arrowclick
29631      * Fires when this button's arrow is clicked
29632      * @param {SplitButton} this
29633      * @param {EventObject} e The click event
29634      */
29635     this.addEvents({"arrowclick":true});
29636 };
29637
29638 Roo.extend(Roo.SplitButton, Roo.Button, {
29639     render : function(renderTo){
29640         // this is one sweet looking template!
29641         var tpl = new Roo.Template(
29642             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29643             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29644             '<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>',
29645             "</tbody></table></td><td>",
29646             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29647             '<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>',
29648             "</tbody></table></td></tr></table>"
29649         );
29650         var btn = tpl.append(renderTo, [this.text, this.type], true);
29651         var btnEl = btn.child("button");
29652         if(this.cls){
29653             btn.addClass(this.cls);
29654         }
29655         if(this.icon){
29656             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29657         }
29658         if(this.iconCls){
29659             btnEl.addClass(this.iconCls);
29660             if(!this.cls){
29661                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29662             }
29663         }
29664         this.el = btn;
29665         if(this.handleMouseEvents){
29666             btn.on("mouseover", this.onMouseOver, this);
29667             btn.on("mouseout", this.onMouseOut, this);
29668             btn.on("mousedown", this.onMouseDown, this);
29669             btn.on("mouseup", this.onMouseUp, this);
29670         }
29671         btn.on(this.clickEvent, this.onClick, this);
29672         if(this.tooltip){
29673             if(typeof this.tooltip == 'object'){
29674                 Roo.QuickTips.tips(Roo.apply({
29675                       target: btnEl.id
29676                 }, this.tooltip));
29677             } else {
29678                 btnEl.dom[this.tooltipType] = this.tooltip;
29679             }
29680         }
29681         if(this.arrowTooltip){
29682             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29683         }
29684         if(this.hidden){
29685             this.hide();
29686         }
29687         if(this.disabled){
29688             this.disable();
29689         }
29690         if(this.pressed){
29691             this.el.addClass("x-btn-pressed");
29692         }
29693         if(Roo.isIE && !Roo.isIE7){
29694             this.autoWidth.defer(1, this);
29695         }else{
29696             this.autoWidth();
29697         }
29698         if(this.menu){
29699             this.menu.on("show", this.onMenuShow, this);
29700             this.menu.on("hide", this.onMenuHide, this);
29701         }
29702         this.fireEvent('render', this);
29703     },
29704
29705     // private
29706     autoWidth : function(){
29707         if(this.el){
29708             var tbl = this.el.child("table:first");
29709             var tbl2 = this.el.child("table:last");
29710             this.el.setWidth("auto");
29711             tbl.setWidth("auto");
29712             if(Roo.isIE7 && Roo.isStrict){
29713                 var ib = this.el.child('button:first');
29714                 if(ib && ib.getWidth() > 20){
29715                     ib.clip();
29716                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29717                 }
29718             }
29719             if(this.minWidth){
29720                 if(this.hidden){
29721                     this.el.beginMeasure();
29722                 }
29723                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29724                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29725                 }
29726                 if(this.hidden){
29727                     this.el.endMeasure();
29728                 }
29729             }
29730             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29731         } 
29732     },
29733     /**
29734      * Sets this button's click handler
29735      * @param {Function} handler The function to call when the button is clicked
29736      * @param {Object} scope (optional) Scope for the function passed above
29737      */
29738     setHandler : function(handler, scope){
29739         this.handler = handler;
29740         this.scope = scope;  
29741     },
29742     
29743     /**
29744      * Sets this button's arrow click handler
29745      * @param {Function} handler The function to call when the arrow is clicked
29746      * @param {Object} scope (optional) Scope for the function passed above
29747      */
29748     setArrowHandler : function(handler, scope){
29749         this.arrowHandler = handler;
29750         this.scope = scope;  
29751     },
29752     
29753     /**
29754      * Focus the button
29755      */
29756     focus : function(){
29757         if(this.el){
29758             this.el.child("button:first").focus();
29759         }
29760     },
29761
29762     // private
29763     onClick : function(e){
29764         e.preventDefault();
29765         if(!this.disabled){
29766             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29767                 if(this.menu && !this.menu.isVisible()){
29768                     this.menu.show(this.el, this.menuAlign);
29769                 }
29770                 this.fireEvent("arrowclick", this, e);
29771                 if(this.arrowHandler){
29772                     this.arrowHandler.call(this.scope || this, this, e);
29773                 }
29774             }else{
29775                 this.fireEvent("click", this, e);
29776                 if(this.handler){
29777                     this.handler.call(this.scope || this, this, e);
29778                 }
29779             }
29780         }
29781     },
29782     // private
29783     onMouseDown : function(e){
29784         if(!this.disabled){
29785             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29786         }
29787     },
29788     // private
29789     onMouseUp : function(e){
29790         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29791     }   
29792 });
29793
29794
29795 // backwards compat
29796 Roo.MenuButton = Roo.SplitButton;/*
29797  * Based on:
29798  * Ext JS Library 1.1.1
29799  * Copyright(c) 2006-2007, Ext JS, LLC.
29800  *
29801  * Originally Released Under LGPL - original licence link has changed is not relivant.
29802  *
29803  * Fork - LGPL
29804  * <script type="text/javascript">
29805  */
29806
29807 /**
29808  * @class Roo.Toolbar
29809  * Basic Toolbar class.
29810  * @constructor
29811  * Creates a new Toolbar
29812  * @param {Object} container The config object
29813  */ 
29814 Roo.Toolbar = function(container, buttons, config)
29815 {
29816     /// old consturctor format still supported..
29817     if(container instanceof Array){ // omit the container for later rendering
29818         buttons = container;
29819         config = buttons;
29820         container = null;
29821     }
29822     if (typeof(container) == 'object' && container.xtype) {
29823         config = container;
29824         container = config.container;
29825         buttons = config.buttons || []; // not really - use items!!
29826     }
29827     var xitems = [];
29828     if (config && config.items) {
29829         xitems = config.items;
29830         delete config.items;
29831     }
29832     Roo.apply(this, config);
29833     this.buttons = buttons;
29834     
29835     if(container){
29836         this.render(container);
29837     }
29838     this.xitems = xitems;
29839     Roo.each(xitems, function(b) {
29840         this.add(b);
29841     }, this);
29842     
29843 };
29844
29845 Roo.Toolbar.prototype = {
29846     /**
29847      * @cfg {Array} items
29848      * array of button configs or elements to add (will be converted to a MixedCollection)
29849      */
29850     
29851     /**
29852      * @cfg {String/HTMLElement/Element} container
29853      * The id or element that will contain the toolbar
29854      */
29855     // private
29856     render : function(ct){
29857         this.el = Roo.get(ct);
29858         if(this.cls){
29859             this.el.addClass(this.cls);
29860         }
29861         // using a table allows for vertical alignment
29862         // 100% width is needed by Safari...
29863         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29864         this.tr = this.el.child("tr", true);
29865         var autoId = 0;
29866         this.items = new Roo.util.MixedCollection(false, function(o){
29867             return o.id || ("item" + (++autoId));
29868         });
29869         if(this.buttons){
29870             this.add.apply(this, this.buttons);
29871             delete this.buttons;
29872         }
29873     },
29874
29875     /**
29876      * Adds element(s) to the toolbar -- this function takes a variable number of 
29877      * arguments of mixed type and adds them to the toolbar.
29878      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29879      * <ul>
29880      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29881      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29882      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29883      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29884      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29885      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29886      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29887      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29888      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29889      * </ul>
29890      * @param {Mixed} arg2
29891      * @param {Mixed} etc.
29892      */
29893     add : function(){
29894         var a = arguments, l = a.length;
29895         for(var i = 0; i < l; i++){
29896             this._add(a[i]);
29897         }
29898     },
29899     // private..
29900     _add : function(el) {
29901         
29902         if (el.xtype) {
29903             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29904         }
29905         
29906         if (el.applyTo){ // some kind of form field
29907             return this.addField(el);
29908         } 
29909         if (el.render){ // some kind of Toolbar.Item
29910             return this.addItem(el);
29911         }
29912         if (typeof el == "string"){ // string
29913             if(el == "separator" || el == "-"){
29914                 return this.addSeparator();
29915             }
29916             if (el == " "){
29917                 return this.addSpacer();
29918             }
29919             if(el == "->"){
29920                 return this.addFill();
29921             }
29922             return this.addText(el);
29923             
29924         }
29925         if(el.tagName){ // element
29926             return this.addElement(el);
29927         }
29928         if(typeof el == "object"){ // must be button config?
29929             return this.addButton(el);
29930         }
29931         // and now what?!?!
29932         return false;
29933         
29934     },
29935     
29936     /**
29937      * Add an Xtype element
29938      * @param {Object} xtype Xtype Object
29939      * @return {Object} created Object
29940      */
29941     addxtype : function(e){
29942         return this.add(e);  
29943     },
29944     
29945     /**
29946      * Returns the Element for this toolbar.
29947      * @return {Roo.Element}
29948      */
29949     getEl : function(){
29950         return this.el;  
29951     },
29952     
29953     /**
29954      * Adds a separator
29955      * @return {Roo.Toolbar.Item} The separator item
29956      */
29957     addSeparator : function(){
29958         return this.addItem(new Roo.Toolbar.Separator());
29959     },
29960
29961     /**
29962      * Adds a spacer element
29963      * @return {Roo.Toolbar.Spacer} The spacer item
29964      */
29965     addSpacer : function(){
29966         return this.addItem(new Roo.Toolbar.Spacer());
29967     },
29968
29969     /**
29970      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29971      * @return {Roo.Toolbar.Fill} The fill item
29972      */
29973     addFill : function(){
29974         return this.addItem(new Roo.Toolbar.Fill());
29975     },
29976
29977     /**
29978      * Adds any standard HTML element to the toolbar
29979      * @param {String/HTMLElement/Element} el The element or id of the element to add
29980      * @return {Roo.Toolbar.Item} The element's item
29981      */
29982     addElement : function(el){
29983         return this.addItem(new Roo.Toolbar.Item(el));
29984     },
29985     /**
29986      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29987      * @type Roo.util.MixedCollection  
29988      */
29989     items : false,
29990      
29991     /**
29992      * Adds any Toolbar.Item or subclass
29993      * @param {Roo.Toolbar.Item} item
29994      * @return {Roo.Toolbar.Item} The item
29995      */
29996     addItem : function(item){
29997         var td = this.nextBlock();
29998         item.render(td);
29999         this.items.add(item);
30000         return item;
30001     },
30002     
30003     /**
30004      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30005      * @param {Object/Array} config A button config or array of configs
30006      * @return {Roo.Toolbar.Button/Array}
30007      */
30008     addButton : function(config){
30009         if(config instanceof Array){
30010             var buttons = [];
30011             for(var i = 0, len = config.length; i < len; i++) {
30012                 buttons.push(this.addButton(config[i]));
30013             }
30014             return buttons;
30015         }
30016         var b = config;
30017         if(!(config instanceof Roo.Toolbar.Button)){
30018             b = config.split ?
30019                 new Roo.Toolbar.SplitButton(config) :
30020                 new Roo.Toolbar.Button(config);
30021         }
30022         var td = this.nextBlock();
30023         b.render(td);
30024         this.items.add(b);
30025         return b;
30026     },
30027     
30028     /**
30029      * Adds text to the toolbar
30030      * @param {String} text The text to add
30031      * @return {Roo.Toolbar.Item} The element's item
30032      */
30033     addText : function(text){
30034         return this.addItem(new Roo.Toolbar.TextItem(text));
30035     },
30036     
30037     /**
30038      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30039      * @param {Number} index The index where the item is to be inserted
30040      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30041      * @return {Roo.Toolbar.Button/Item}
30042      */
30043     insertButton : function(index, item){
30044         if(item instanceof Array){
30045             var buttons = [];
30046             for(var i = 0, len = item.length; i < len; i++) {
30047                buttons.push(this.insertButton(index + i, item[i]));
30048             }
30049             return buttons;
30050         }
30051         if (!(item instanceof Roo.Toolbar.Button)){
30052            item = new Roo.Toolbar.Button(item);
30053         }
30054         var td = document.createElement("td");
30055         this.tr.insertBefore(td, this.tr.childNodes[index]);
30056         item.render(td);
30057         this.items.insert(index, item);
30058         return item;
30059     },
30060     
30061     /**
30062      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30063      * @param {Object} config
30064      * @return {Roo.Toolbar.Item} The element's item
30065      */
30066     addDom : function(config, returnEl){
30067         var td = this.nextBlock();
30068         Roo.DomHelper.overwrite(td, config);
30069         var ti = new Roo.Toolbar.Item(td.firstChild);
30070         ti.render(td);
30071         this.items.add(ti);
30072         return ti;
30073     },
30074
30075     /**
30076      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30077      * @type Roo.util.MixedCollection  
30078      */
30079     fields : false,
30080     
30081     /**
30082      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30083      * Note: the field should not have been rendered yet. For a field that has already been
30084      * rendered, use {@link #addElement}.
30085      * @param {Roo.form.Field} field
30086      * @return {Roo.ToolbarItem}
30087      */
30088      
30089       
30090     addField : function(field) {
30091         if (!this.fields) {
30092             var autoId = 0;
30093             this.fields = new Roo.util.MixedCollection(false, function(o){
30094                 return o.id || ("item" + (++autoId));
30095             });
30096
30097         }
30098         
30099         var td = this.nextBlock();
30100         field.render(td);
30101         var ti = new Roo.Toolbar.Item(td.firstChild);
30102         ti.render(td);
30103         this.items.add(ti);
30104         this.fields.add(field);
30105         return ti;
30106     },
30107     /**
30108      * Hide the toolbar
30109      * @method hide
30110      */
30111      
30112       
30113     hide : function()
30114     {
30115         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30116         this.el.child('div').hide();
30117     },
30118     /**
30119      * Show the toolbar
30120      * @method show
30121      */
30122     show : function()
30123     {
30124         this.el.child('div').show();
30125     },
30126       
30127     // private
30128     nextBlock : function(){
30129         var td = document.createElement("td");
30130         this.tr.appendChild(td);
30131         return td;
30132     },
30133
30134     // private
30135     destroy : function(){
30136         if(this.items){ // rendered?
30137             Roo.destroy.apply(Roo, this.items.items);
30138         }
30139         if(this.fields){ // rendered?
30140             Roo.destroy.apply(Roo, this.fields.items);
30141         }
30142         Roo.Element.uncache(this.el, this.tr);
30143     }
30144 };
30145
30146 /**
30147  * @class Roo.Toolbar.Item
30148  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30149  * @constructor
30150  * Creates a new Item
30151  * @param {HTMLElement} el 
30152  */
30153 Roo.Toolbar.Item = function(el){
30154     var cfg = {};
30155     if (typeof (el.xtype) != 'undefined') {
30156         cfg = el;
30157         el = cfg.el;
30158     }
30159     
30160     this.el = Roo.getDom(el);
30161     this.id = Roo.id(this.el);
30162     this.hidden = false;
30163     
30164     this.addEvents({
30165          /**
30166              * @event render
30167              * Fires when the button is rendered
30168              * @param {Button} this
30169              */
30170         'render': true
30171     });
30172     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30173 };
30174 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30175 //Roo.Toolbar.Item.prototype = {
30176     
30177     /**
30178      * Get this item's HTML Element
30179      * @return {HTMLElement}
30180      */
30181     getEl : function(){
30182        return this.el;  
30183     },
30184
30185     // private
30186     render : function(td){
30187         
30188          this.td = td;
30189         td.appendChild(this.el);
30190         
30191         this.fireEvent('render', this);
30192     },
30193     
30194     /**
30195      * Removes and destroys this item.
30196      */
30197     destroy : function(){
30198         this.td.parentNode.removeChild(this.td);
30199     },
30200     
30201     /**
30202      * Shows this item.
30203      */
30204     show: function(){
30205         this.hidden = false;
30206         this.td.style.display = "";
30207     },
30208     
30209     /**
30210      * Hides this item.
30211      */
30212     hide: function(){
30213         this.hidden = true;
30214         this.td.style.display = "none";
30215     },
30216     
30217     /**
30218      * Convenience function for boolean show/hide.
30219      * @param {Boolean} visible true to show/false to hide
30220      */
30221     setVisible: function(visible){
30222         if(visible) {
30223             this.show();
30224         }else{
30225             this.hide();
30226         }
30227     },
30228     
30229     /**
30230      * Try to focus this item.
30231      */
30232     focus : function(){
30233         Roo.fly(this.el).focus();
30234     },
30235     
30236     /**
30237      * Disables this item.
30238      */
30239     disable : function(){
30240         Roo.fly(this.td).addClass("x-item-disabled");
30241         this.disabled = true;
30242         this.el.disabled = true;
30243     },
30244     
30245     /**
30246      * Enables this item.
30247      */
30248     enable : function(){
30249         Roo.fly(this.td).removeClass("x-item-disabled");
30250         this.disabled = false;
30251         this.el.disabled = false;
30252     }
30253 });
30254
30255
30256 /**
30257  * @class Roo.Toolbar.Separator
30258  * @extends Roo.Toolbar.Item
30259  * A simple toolbar separator class
30260  * @constructor
30261  * Creates a new Separator
30262  */
30263 Roo.Toolbar.Separator = function(cfg){
30264     
30265     var s = document.createElement("span");
30266     s.className = "ytb-sep";
30267     if (cfg) {
30268         cfg.el = s;
30269     }
30270     
30271     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30272 };
30273 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30274     enable:Roo.emptyFn,
30275     disable:Roo.emptyFn,
30276     focus:Roo.emptyFn
30277 });
30278
30279 /**
30280  * @class Roo.Toolbar.Spacer
30281  * @extends Roo.Toolbar.Item
30282  * A simple element that adds extra horizontal space to a toolbar.
30283  * @constructor
30284  * Creates a new Spacer
30285  */
30286 Roo.Toolbar.Spacer = function(cfg){
30287     var s = document.createElement("div");
30288     s.className = "ytb-spacer";
30289     if (cfg) {
30290         cfg.el = s;
30291     }
30292     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30293 };
30294 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30295     enable:Roo.emptyFn,
30296     disable:Roo.emptyFn,
30297     focus:Roo.emptyFn
30298 });
30299
30300 /**
30301  * @class Roo.Toolbar.Fill
30302  * @extends Roo.Toolbar.Spacer
30303  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30304  * @constructor
30305  * Creates a new Spacer
30306  */
30307 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30308     // private
30309     render : function(td){
30310         td.style.width = '100%';
30311         Roo.Toolbar.Fill.superclass.render.call(this, td);
30312     }
30313 });
30314
30315 /**
30316  * @class Roo.Toolbar.TextItem
30317  * @extends Roo.Toolbar.Item
30318  * A simple class that renders text directly into a toolbar.
30319  * @constructor
30320  * Creates a new TextItem
30321  * @param {String} text
30322  */
30323 Roo.Toolbar.TextItem = function(cfg){
30324     var  text = cfg || "";
30325     if (typeof(cfg) == 'object') {
30326         text = cfg.text || "";
30327     }  else {
30328         cfg = null;
30329     }
30330     var s = document.createElement("span");
30331     s.className = "ytb-text";
30332     s.innerHTML = text;
30333     if (cfg) {
30334         cfg.el  = s;
30335     }
30336     
30337     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30338 };
30339 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30340     
30341      
30342     enable:Roo.emptyFn,
30343     disable:Roo.emptyFn,
30344     focus:Roo.emptyFn
30345 });
30346
30347 /**
30348  * @class Roo.Toolbar.Button
30349  * @extends Roo.Button
30350  * A button that renders into a toolbar.
30351  * @constructor
30352  * Creates a new Button
30353  * @param {Object} config A standard {@link Roo.Button} config object
30354  */
30355 Roo.Toolbar.Button = function(config){
30356     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30357 };
30358 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30359     render : function(td){
30360         this.td = td;
30361         Roo.Toolbar.Button.superclass.render.call(this, td);
30362     },
30363     
30364     /**
30365      * Removes and destroys this button
30366      */
30367     destroy : function(){
30368         Roo.Toolbar.Button.superclass.destroy.call(this);
30369         this.td.parentNode.removeChild(this.td);
30370     },
30371     
30372     /**
30373      * Shows this button
30374      */
30375     show: function(){
30376         this.hidden = false;
30377         this.td.style.display = "";
30378     },
30379     
30380     /**
30381      * Hides this button
30382      */
30383     hide: function(){
30384         this.hidden = true;
30385         this.td.style.display = "none";
30386     },
30387
30388     /**
30389      * Disables this item
30390      */
30391     disable : function(){
30392         Roo.fly(this.td).addClass("x-item-disabled");
30393         this.disabled = true;
30394     },
30395
30396     /**
30397      * Enables this item
30398      */
30399     enable : function(){
30400         Roo.fly(this.td).removeClass("x-item-disabled");
30401         this.disabled = false;
30402     }
30403 });
30404 // backwards compat
30405 Roo.ToolbarButton = Roo.Toolbar.Button;
30406
30407 /**
30408  * @class Roo.Toolbar.SplitButton
30409  * @extends Roo.SplitButton
30410  * A menu button that renders into a toolbar.
30411  * @constructor
30412  * Creates a new SplitButton
30413  * @param {Object} config A standard {@link Roo.SplitButton} config object
30414  */
30415 Roo.Toolbar.SplitButton = function(config){
30416     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30417 };
30418 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30419     render : function(td){
30420         this.td = td;
30421         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30422     },
30423     
30424     /**
30425      * Removes and destroys this button
30426      */
30427     destroy : function(){
30428         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30429         this.td.parentNode.removeChild(this.td);
30430     },
30431     
30432     /**
30433      * Shows this button
30434      */
30435     show: function(){
30436         this.hidden = false;
30437         this.td.style.display = "";
30438     },
30439     
30440     /**
30441      * Hides this button
30442      */
30443     hide: function(){
30444         this.hidden = true;
30445         this.td.style.display = "none";
30446     }
30447 });
30448
30449 // backwards compat
30450 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30451  * Based on:
30452  * Ext JS Library 1.1.1
30453  * Copyright(c) 2006-2007, Ext JS, LLC.
30454  *
30455  * Originally Released Under LGPL - original licence link has changed is not relivant.
30456  *
30457  * Fork - LGPL
30458  * <script type="text/javascript">
30459  */
30460  
30461 /**
30462  * @class Roo.PagingToolbar
30463  * @extends Roo.Toolbar
30464  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30465  * @constructor
30466  * Create a new PagingToolbar
30467  * @param {Object} config The config object
30468  */
30469 Roo.PagingToolbar = function(el, ds, config)
30470 {
30471     // old args format still supported... - xtype is prefered..
30472     if (typeof(el) == 'object' && el.xtype) {
30473         // created from xtype...
30474         config = el;
30475         ds = el.dataSource;
30476         el = config.container;
30477     }
30478     var items = [];
30479     if (config.items) {
30480         items = config.items;
30481         config.items = [];
30482     }
30483     
30484     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30485     this.ds = ds;
30486     this.cursor = 0;
30487     this.renderButtons(this.el);
30488     this.bind(ds);
30489     
30490     // supprot items array.
30491    
30492     Roo.each(items, function(e) {
30493         this.add(Roo.factory(e));
30494     },this);
30495     
30496 };
30497
30498 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30499     /**
30500      * @cfg {Roo.data.Store} dataSource
30501      * The underlying data store providing the paged data
30502      */
30503     /**
30504      * @cfg {String/HTMLElement/Element} container
30505      * container The id or element that will contain the toolbar
30506      */
30507     /**
30508      * @cfg {Boolean} displayInfo
30509      * True to display the displayMsg (defaults to false)
30510      */
30511     /**
30512      * @cfg {Number} pageSize
30513      * The number of records to display per page (defaults to 20)
30514      */
30515     pageSize: 20,
30516     /**
30517      * @cfg {String} displayMsg
30518      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30519      */
30520     displayMsg : 'Displaying {0} - {1} of {2}',
30521     /**
30522      * @cfg {String} emptyMsg
30523      * The message to display when no records are found (defaults to "No data to display")
30524      */
30525     emptyMsg : 'No data to display',
30526     /**
30527      * Customizable piece of the default paging text (defaults to "Page")
30528      * @type String
30529      */
30530     beforePageText : "Page",
30531     /**
30532      * Customizable piece of the default paging text (defaults to "of %0")
30533      * @type String
30534      */
30535     afterPageText : "of {0}",
30536     /**
30537      * Customizable piece of the default paging text (defaults to "First Page")
30538      * @type String
30539      */
30540     firstText : "First Page",
30541     /**
30542      * Customizable piece of the default paging text (defaults to "Previous Page")
30543      * @type String
30544      */
30545     prevText : "Previous Page",
30546     /**
30547      * Customizable piece of the default paging text (defaults to "Next Page")
30548      * @type String
30549      */
30550     nextText : "Next Page",
30551     /**
30552      * Customizable piece of the default paging text (defaults to "Last Page")
30553      * @type String
30554      */
30555     lastText : "Last Page",
30556     /**
30557      * Customizable piece of the default paging text (defaults to "Refresh")
30558      * @type String
30559      */
30560     refreshText : "Refresh",
30561
30562     // private
30563     renderButtons : function(el){
30564         Roo.PagingToolbar.superclass.render.call(this, el);
30565         this.first = this.addButton({
30566             tooltip: this.firstText,
30567             cls: "x-btn-icon x-grid-page-first",
30568             disabled: true,
30569             handler: this.onClick.createDelegate(this, ["first"])
30570         });
30571         this.prev = this.addButton({
30572             tooltip: this.prevText,
30573             cls: "x-btn-icon x-grid-page-prev",
30574             disabled: true,
30575             handler: this.onClick.createDelegate(this, ["prev"])
30576         });
30577         //this.addSeparator();
30578         this.add(this.beforePageText);
30579         this.field = Roo.get(this.addDom({
30580            tag: "input",
30581            type: "text",
30582            size: "3",
30583            value: "1",
30584            cls: "x-grid-page-number"
30585         }).el);
30586         this.field.on("keydown", this.onPagingKeydown, this);
30587         this.field.on("focus", function(){this.dom.select();});
30588         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30589         this.field.setHeight(18);
30590         //this.addSeparator();
30591         this.next = this.addButton({
30592             tooltip: this.nextText,
30593             cls: "x-btn-icon x-grid-page-next",
30594             disabled: true,
30595             handler: this.onClick.createDelegate(this, ["next"])
30596         });
30597         this.last = this.addButton({
30598             tooltip: this.lastText,
30599             cls: "x-btn-icon x-grid-page-last",
30600             disabled: true,
30601             handler: this.onClick.createDelegate(this, ["last"])
30602         });
30603         //this.addSeparator();
30604         this.loading = this.addButton({
30605             tooltip: this.refreshText,
30606             cls: "x-btn-icon x-grid-loading",
30607             handler: this.onClick.createDelegate(this, ["refresh"])
30608         });
30609
30610         if(this.displayInfo){
30611             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30612         }
30613     },
30614
30615     // private
30616     updateInfo : function(){
30617         if(this.displayEl){
30618             var count = this.ds.getCount();
30619             var msg = count == 0 ?
30620                 this.emptyMsg :
30621                 String.format(
30622                     this.displayMsg,
30623                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30624                 );
30625             this.displayEl.update(msg);
30626         }
30627     },
30628
30629     // private
30630     onLoad : function(ds, r, o){
30631        this.cursor = o.params ? o.params.start : 0;
30632        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30633
30634        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30635        this.field.dom.value = ap;
30636        this.first.setDisabled(ap == 1);
30637        this.prev.setDisabled(ap == 1);
30638        this.next.setDisabled(ap == ps);
30639        this.last.setDisabled(ap == ps);
30640        this.loading.enable();
30641        this.updateInfo();
30642     },
30643
30644     // private
30645     getPageData : function(){
30646         var total = this.ds.getTotalCount();
30647         return {
30648             total : total,
30649             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30650             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30651         };
30652     },
30653
30654     // private
30655     onLoadError : function(){
30656         this.loading.enable();
30657     },
30658
30659     // private
30660     onPagingKeydown : function(e){
30661         var k = e.getKey();
30662         var d = this.getPageData();
30663         if(k == e.RETURN){
30664             var v = this.field.dom.value, pageNum;
30665             if(!v || isNaN(pageNum = parseInt(v, 10))){
30666                 this.field.dom.value = d.activePage;
30667                 return;
30668             }
30669             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30670             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30671             e.stopEvent();
30672         }
30673         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))
30674         {
30675           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30676           this.field.dom.value = pageNum;
30677           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30678           e.stopEvent();
30679         }
30680         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30681         {
30682           var v = this.field.dom.value, pageNum; 
30683           var increment = (e.shiftKey) ? 10 : 1;
30684           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30685             increment *= -1;
30686           }
30687           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30688             this.field.dom.value = d.activePage;
30689             return;
30690           }
30691           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30692           {
30693             this.field.dom.value = parseInt(v, 10) + increment;
30694             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30695             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30696           }
30697           e.stopEvent();
30698         }
30699     },
30700
30701     // private
30702     beforeLoad : function(){
30703         if(this.loading){
30704             this.loading.disable();
30705         }
30706     },
30707
30708     // private
30709     onClick : function(which){
30710         var ds = this.ds;
30711         switch(which){
30712             case "first":
30713                 ds.load({params:{start: 0, limit: this.pageSize}});
30714             break;
30715             case "prev":
30716                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30717             break;
30718             case "next":
30719                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30720             break;
30721             case "last":
30722                 var total = ds.getTotalCount();
30723                 var extra = total % this.pageSize;
30724                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30725                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30726             break;
30727             case "refresh":
30728                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30729             break;
30730         }
30731     },
30732
30733     /**
30734      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30735      * @param {Roo.data.Store} store The data store to unbind
30736      */
30737     unbind : function(ds){
30738         ds.un("beforeload", this.beforeLoad, this);
30739         ds.un("load", this.onLoad, this);
30740         ds.un("loadexception", this.onLoadError, this);
30741         ds.un("remove", this.updateInfo, this);
30742         ds.un("add", this.updateInfo, this);
30743         this.ds = undefined;
30744     },
30745
30746     /**
30747      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30748      * @param {Roo.data.Store} store The data store to bind
30749      */
30750     bind : function(ds){
30751         ds.on("beforeload", this.beforeLoad, this);
30752         ds.on("load", this.onLoad, this);
30753         ds.on("loadexception", this.onLoadError, this);
30754         ds.on("remove", this.updateInfo, this);
30755         ds.on("add", this.updateInfo, this);
30756         this.ds = ds;
30757     }
30758 });/*
30759  * Based on:
30760  * Ext JS Library 1.1.1
30761  * Copyright(c) 2006-2007, Ext JS, LLC.
30762  *
30763  * Originally Released Under LGPL - original licence link has changed is not relivant.
30764  *
30765  * Fork - LGPL
30766  * <script type="text/javascript">
30767  */
30768
30769 /**
30770  * @class Roo.Resizable
30771  * @extends Roo.util.Observable
30772  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30773  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30774  * 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
30775  * the element will be wrapped for you automatically.</p>
30776  * <p>Here is the list of valid resize handles:</p>
30777  * <pre>
30778 Value   Description
30779 ------  -------------------
30780  'n'     north
30781  's'     south
30782  'e'     east
30783  'w'     west
30784  'nw'    northwest
30785  'sw'    southwest
30786  'se'    southeast
30787  'ne'    northeast
30788  'hd'    horizontal drag
30789  'all'   all
30790 </pre>
30791  * <p>Here's an example showing the creation of a typical Resizable:</p>
30792  * <pre><code>
30793 var resizer = new Roo.Resizable("element-id", {
30794     handles: 'all',
30795     minWidth: 200,
30796     minHeight: 100,
30797     maxWidth: 500,
30798     maxHeight: 400,
30799     pinned: true
30800 });
30801 resizer.on("resize", myHandler);
30802 </code></pre>
30803  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30804  * resizer.east.setDisplayed(false);</p>
30805  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30806  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30807  * resize operation's new size (defaults to [0, 0])
30808  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30809  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30810  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30811  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30812  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30813  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30814  * @cfg {Number} width The width of the element in pixels (defaults to null)
30815  * @cfg {Number} height The height of the element in pixels (defaults to null)
30816  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30817  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30818  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30819  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30820  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30821  * in favor of the handles config option (defaults to false)
30822  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30823  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30824  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30825  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30826  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30827  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30828  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30829  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30830  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30831  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30832  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30833  * @constructor
30834  * Create a new resizable component
30835  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30836  * @param {Object} config configuration options
30837   */
30838 Roo.Resizable = function(el, config)
30839 {
30840     this.el = Roo.get(el);
30841
30842     if(config && config.wrap){
30843         config.resizeChild = this.el;
30844         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30845         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30846         this.el.setStyle("overflow", "hidden");
30847         this.el.setPositioning(config.resizeChild.getPositioning());
30848         config.resizeChild.clearPositioning();
30849         if(!config.width || !config.height){
30850             var csize = config.resizeChild.getSize();
30851             this.el.setSize(csize.width, csize.height);
30852         }
30853         if(config.pinned && !config.adjustments){
30854             config.adjustments = "auto";
30855         }
30856     }
30857
30858     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30859     this.proxy.unselectable();
30860     this.proxy.enableDisplayMode('block');
30861
30862     Roo.apply(this, config);
30863
30864     if(this.pinned){
30865         this.disableTrackOver = true;
30866         this.el.addClass("x-resizable-pinned");
30867     }
30868     // if the element isn't positioned, make it relative
30869     var position = this.el.getStyle("position");
30870     if(position != "absolute" && position != "fixed"){
30871         this.el.setStyle("position", "relative");
30872     }
30873     if(!this.handles){ // no handles passed, must be legacy style
30874         this.handles = 's,e,se';
30875         if(this.multiDirectional){
30876             this.handles += ',n,w';
30877         }
30878     }
30879     if(this.handles == "all"){
30880         this.handles = "n s e w ne nw se sw";
30881     }
30882     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30883     var ps = Roo.Resizable.positions;
30884     for(var i = 0, len = hs.length; i < len; i++){
30885         if(hs[i] && ps[hs[i]]){
30886             var pos = ps[hs[i]];
30887             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30888         }
30889     }
30890     // legacy
30891     this.corner = this.southeast;
30892     
30893     // updateBox = the box can move..
30894     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30895         this.updateBox = true;
30896     }
30897
30898     this.activeHandle = null;
30899
30900     if(this.resizeChild){
30901         if(typeof this.resizeChild == "boolean"){
30902             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30903         }else{
30904             this.resizeChild = Roo.get(this.resizeChild, true);
30905         }
30906     }
30907     
30908     if(this.adjustments == "auto"){
30909         var rc = this.resizeChild;
30910         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30911         if(rc && (hw || hn)){
30912             rc.position("relative");
30913             rc.setLeft(hw ? hw.el.getWidth() : 0);
30914             rc.setTop(hn ? hn.el.getHeight() : 0);
30915         }
30916         this.adjustments = [
30917             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30918             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30919         ];
30920     }
30921
30922     if(this.draggable){
30923         this.dd = this.dynamic ?
30924             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30925         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30926     }
30927
30928     // public events
30929     this.addEvents({
30930         /**
30931          * @event beforeresize
30932          * Fired before resize is allowed. Set enabled to false to cancel resize.
30933          * @param {Roo.Resizable} this
30934          * @param {Roo.EventObject} e The mousedown event
30935          */
30936         "beforeresize" : true,
30937         /**
30938          * @event resizing
30939          * Fired a resizing.
30940          * @param {Roo.Resizable} this
30941          * @param {Number} x The new x position
30942          * @param {Number} y The new y position
30943          * @param {Number} w The new w width
30944          * @param {Number} h The new h hight
30945          * @param {Roo.EventObject} e The mouseup event
30946          */
30947         "resizing" : true,
30948         /**
30949          * @event resize
30950          * Fired after a resize.
30951          * @param {Roo.Resizable} this
30952          * @param {Number} width The new width
30953          * @param {Number} height The new height
30954          * @param {Roo.EventObject} e The mouseup event
30955          */
30956         "resize" : true
30957     });
30958
30959     if(this.width !== null && this.height !== null){
30960         this.resizeTo(this.width, this.height);
30961     }else{
30962         this.updateChildSize();
30963     }
30964     if(Roo.isIE){
30965         this.el.dom.style.zoom = 1;
30966     }
30967     Roo.Resizable.superclass.constructor.call(this);
30968 };
30969
30970 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30971         resizeChild : false,
30972         adjustments : [0, 0],
30973         minWidth : 5,
30974         minHeight : 5,
30975         maxWidth : 10000,
30976         maxHeight : 10000,
30977         enabled : true,
30978         animate : false,
30979         duration : .35,
30980         dynamic : false,
30981         handles : false,
30982         multiDirectional : false,
30983         disableTrackOver : false,
30984         easing : 'easeOutStrong',
30985         widthIncrement : 0,
30986         heightIncrement : 0,
30987         pinned : false,
30988         width : null,
30989         height : null,
30990         preserveRatio : false,
30991         transparent: false,
30992         minX: 0,
30993         minY: 0,
30994         draggable: false,
30995
30996         /**
30997          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30998          */
30999         constrainTo: undefined,
31000         /**
31001          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31002          */
31003         resizeRegion: undefined,
31004
31005
31006     /**
31007      * Perform a manual resize
31008      * @param {Number} width
31009      * @param {Number} height
31010      */
31011     resizeTo : function(width, height){
31012         this.el.setSize(width, height);
31013         this.updateChildSize();
31014         this.fireEvent("resize", this, width, height, null);
31015     },
31016
31017     // private
31018     startSizing : function(e, handle){
31019         this.fireEvent("beforeresize", this, e);
31020         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31021
31022             if(!this.overlay){
31023                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31024                 this.overlay.unselectable();
31025                 this.overlay.enableDisplayMode("block");
31026                 this.overlay.on("mousemove", this.onMouseMove, this);
31027                 this.overlay.on("mouseup", this.onMouseUp, this);
31028             }
31029             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31030
31031             this.resizing = true;
31032             this.startBox = this.el.getBox();
31033             this.startPoint = e.getXY();
31034             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31035                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31036
31037             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31038             this.overlay.show();
31039
31040             if(this.constrainTo) {
31041                 var ct = Roo.get(this.constrainTo);
31042                 this.resizeRegion = ct.getRegion().adjust(
31043                     ct.getFrameWidth('t'),
31044                     ct.getFrameWidth('l'),
31045                     -ct.getFrameWidth('b'),
31046                     -ct.getFrameWidth('r')
31047                 );
31048             }
31049
31050             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31051             this.proxy.show();
31052             this.proxy.setBox(this.startBox);
31053             if(!this.dynamic){
31054                 this.proxy.setStyle('visibility', 'visible');
31055             }
31056         }
31057     },
31058
31059     // private
31060     onMouseDown : function(handle, e){
31061         if(this.enabled){
31062             e.stopEvent();
31063             this.activeHandle = handle;
31064             this.startSizing(e, handle);
31065         }
31066     },
31067
31068     // private
31069     onMouseUp : function(e){
31070         var size = this.resizeElement();
31071         this.resizing = false;
31072         this.handleOut();
31073         this.overlay.hide();
31074         this.proxy.hide();
31075         this.fireEvent("resize", this, size.width, size.height, e);
31076     },
31077
31078     // private
31079     updateChildSize : function(){
31080         
31081         if(this.resizeChild){
31082             var el = this.el;
31083             var child = this.resizeChild;
31084             var adj = this.adjustments;
31085             if(el.dom.offsetWidth){
31086                 var b = el.getSize(true);
31087                 child.setSize(b.width+adj[0], b.height+adj[1]);
31088             }
31089             // Second call here for IE
31090             // The first call enables instant resizing and
31091             // the second call corrects scroll bars if they
31092             // exist
31093             if(Roo.isIE){
31094                 setTimeout(function(){
31095                     if(el.dom.offsetWidth){
31096                         var b = el.getSize(true);
31097                         child.setSize(b.width+adj[0], b.height+adj[1]);
31098                     }
31099                 }, 10);
31100             }
31101         }
31102     },
31103
31104     // private
31105     snap : function(value, inc, min){
31106         if(!inc || !value) {
31107             return value;
31108         }
31109         var newValue = value;
31110         var m = value % inc;
31111         if(m > 0){
31112             if(m > (inc/2)){
31113                 newValue = value + (inc-m);
31114             }else{
31115                 newValue = value - m;
31116             }
31117         }
31118         return Math.max(min, newValue);
31119     },
31120
31121     // private
31122     resizeElement : function(){
31123         var box = this.proxy.getBox();
31124         if(this.updateBox){
31125             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31126         }else{
31127             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31128         }
31129         this.updateChildSize();
31130         if(!this.dynamic){
31131             this.proxy.hide();
31132         }
31133         return box;
31134     },
31135
31136     // private
31137     constrain : function(v, diff, m, mx){
31138         if(v - diff < m){
31139             diff = v - m;
31140         }else if(v - diff > mx){
31141             diff = mx - v;
31142         }
31143         return diff;
31144     },
31145
31146     // private
31147     onMouseMove : function(e){
31148         
31149         if(this.enabled){
31150             try{// try catch so if something goes wrong the user doesn't get hung
31151
31152             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31153                 return;
31154             }
31155
31156             //var curXY = this.startPoint;
31157             var curSize = this.curSize || this.startBox;
31158             var x = this.startBox.x, y = this.startBox.y;
31159             var ox = x, oy = y;
31160             var w = curSize.width, h = curSize.height;
31161             var ow = w, oh = h;
31162             var mw = this.minWidth, mh = this.minHeight;
31163             var mxw = this.maxWidth, mxh = this.maxHeight;
31164             var wi = this.widthIncrement;
31165             var hi = this.heightIncrement;
31166
31167             var eventXY = e.getXY();
31168             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31169             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31170
31171             var pos = this.activeHandle.position;
31172
31173             switch(pos){
31174                 case "east":
31175                     w += diffX;
31176                     w = Math.min(Math.max(mw, w), mxw);
31177                     break;
31178              
31179                 case "south":
31180                     h += diffY;
31181                     h = Math.min(Math.max(mh, h), mxh);
31182                     break;
31183                 case "southeast":
31184                     w += diffX;
31185                     h += diffY;
31186                     w = Math.min(Math.max(mw, w), mxw);
31187                     h = Math.min(Math.max(mh, h), mxh);
31188                     break;
31189                 case "north":
31190                     diffY = this.constrain(h, diffY, mh, mxh);
31191                     y += diffY;
31192                     h -= diffY;
31193                     break;
31194                 case "hdrag":
31195                     
31196                     if (wi) {
31197                         var adiffX = Math.abs(diffX);
31198                         var sub = (adiffX % wi); // how much 
31199                         if (sub > (wi/2)) { // far enough to snap
31200                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31201                         } else {
31202                             // remove difference.. 
31203                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31204                         }
31205                     }
31206                     x += diffX;
31207                     x = Math.max(this.minX, x);
31208                     break;
31209                 case "west":
31210                     diffX = this.constrain(w, diffX, mw, mxw);
31211                     x += diffX;
31212                     w -= diffX;
31213                     break;
31214                 case "northeast":
31215                     w += diffX;
31216                     w = Math.min(Math.max(mw, w), mxw);
31217                     diffY = this.constrain(h, diffY, mh, mxh);
31218                     y += diffY;
31219                     h -= diffY;
31220                     break;
31221                 case "northwest":
31222                     diffX = this.constrain(w, diffX, mw, mxw);
31223                     diffY = this.constrain(h, diffY, mh, mxh);
31224                     y += diffY;
31225                     h -= diffY;
31226                     x += diffX;
31227                     w -= diffX;
31228                     break;
31229                case "southwest":
31230                     diffX = this.constrain(w, diffX, mw, mxw);
31231                     h += diffY;
31232                     h = Math.min(Math.max(mh, h), mxh);
31233                     x += diffX;
31234                     w -= diffX;
31235                     break;
31236             }
31237
31238             var sw = this.snap(w, wi, mw);
31239             var sh = this.snap(h, hi, mh);
31240             if(sw != w || sh != h){
31241                 switch(pos){
31242                     case "northeast":
31243                         y -= sh - h;
31244                     break;
31245                     case "north":
31246                         y -= sh - h;
31247                         break;
31248                     case "southwest":
31249                         x -= sw - w;
31250                     break;
31251                     case "west":
31252                         x -= sw - w;
31253                         break;
31254                     case "northwest":
31255                         x -= sw - w;
31256                         y -= sh - h;
31257                     break;
31258                 }
31259                 w = sw;
31260                 h = sh;
31261             }
31262
31263             if(this.preserveRatio){
31264                 switch(pos){
31265                     case "southeast":
31266                     case "east":
31267                         h = oh * (w/ow);
31268                         h = Math.min(Math.max(mh, h), mxh);
31269                         w = ow * (h/oh);
31270                        break;
31271                     case "south":
31272                         w = ow * (h/oh);
31273                         w = Math.min(Math.max(mw, w), mxw);
31274                         h = oh * (w/ow);
31275                         break;
31276                     case "northeast":
31277                         w = ow * (h/oh);
31278                         w = Math.min(Math.max(mw, w), mxw);
31279                         h = oh * (w/ow);
31280                     break;
31281                     case "north":
31282                         var tw = w;
31283                         w = ow * (h/oh);
31284                         w = Math.min(Math.max(mw, w), mxw);
31285                         h = oh * (w/ow);
31286                         x += (tw - w) / 2;
31287                         break;
31288                     case "southwest":
31289                         h = oh * (w/ow);
31290                         h = Math.min(Math.max(mh, h), mxh);
31291                         var tw = w;
31292                         w = ow * (h/oh);
31293                         x += tw - w;
31294                         break;
31295                     case "west":
31296                         var th = h;
31297                         h = oh * (w/ow);
31298                         h = Math.min(Math.max(mh, h), mxh);
31299                         y += (th - h) / 2;
31300                         var tw = w;
31301                         w = ow * (h/oh);
31302                         x += tw - w;
31303                        break;
31304                     case "northwest":
31305                         var tw = w;
31306                         var th = h;
31307                         h = oh * (w/ow);
31308                         h = Math.min(Math.max(mh, h), mxh);
31309                         w = ow * (h/oh);
31310                         y += th - h;
31311                         x += tw - w;
31312                        break;
31313
31314                 }
31315             }
31316             if (pos == 'hdrag') {
31317                 w = ow;
31318             }
31319             this.proxy.setBounds(x, y, w, h);
31320             if(this.dynamic){
31321                 this.resizeElement();
31322             }
31323             }catch(e){}
31324         }
31325         this.fireEvent("resizing", this, x, y, w, h, e);
31326     },
31327
31328     // private
31329     handleOver : function(){
31330         if(this.enabled){
31331             this.el.addClass("x-resizable-over");
31332         }
31333     },
31334
31335     // private
31336     handleOut : function(){
31337         if(!this.resizing){
31338             this.el.removeClass("x-resizable-over");
31339         }
31340     },
31341
31342     /**
31343      * Returns the element this component is bound to.
31344      * @return {Roo.Element}
31345      */
31346     getEl : function(){
31347         return this.el;
31348     },
31349
31350     /**
31351      * Returns the resizeChild element (or null).
31352      * @return {Roo.Element}
31353      */
31354     getResizeChild : function(){
31355         return this.resizeChild;
31356     },
31357     groupHandler : function()
31358     {
31359         
31360     },
31361     /**
31362      * Destroys this resizable. If the element was wrapped and
31363      * removeEl is not true then the element remains.
31364      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31365      */
31366     destroy : function(removeEl){
31367         this.proxy.remove();
31368         if(this.overlay){
31369             this.overlay.removeAllListeners();
31370             this.overlay.remove();
31371         }
31372         var ps = Roo.Resizable.positions;
31373         for(var k in ps){
31374             if(typeof ps[k] != "function" && this[ps[k]]){
31375                 var h = this[ps[k]];
31376                 h.el.removeAllListeners();
31377                 h.el.remove();
31378             }
31379         }
31380         if(removeEl){
31381             this.el.update("");
31382             this.el.remove();
31383         }
31384     }
31385 });
31386
31387 // private
31388 // hash to map config positions to true positions
31389 Roo.Resizable.positions = {
31390     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31391     hd: "hdrag"
31392 };
31393
31394 // private
31395 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31396     if(!this.tpl){
31397         // only initialize the template if resizable is used
31398         var tpl = Roo.DomHelper.createTemplate(
31399             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31400         );
31401         tpl.compile();
31402         Roo.Resizable.Handle.prototype.tpl = tpl;
31403     }
31404     this.position = pos;
31405     this.rz = rz;
31406     // show north drag fro topdra
31407     var handlepos = pos == 'hdrag' ? 'north' : pos;
31408     
31409     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31410     if (pos == 'hdrag') {
31411         this.el.setStyle('cursor', 'pointer');
31412     }
31413     this.el.unselectable();
31414     if(transparent){
31415         this.el.setOpacity(0);
31416     }
31417     this.el.on("mousedown", this.onMouseDown, this);
31418     if(!disableTrackOver){
31419         this.el.on("mouseover", this.onMouseOver, this);
31420         this.el.on("mouseout", this.onMouseOut, this);
31421     }
31422 };
31423
31424 // private
31425 Roo.Resizable.Handle.prototype = {
31426     afterResize : function(rz){
31427         Roo.log('after?');
31428         // do nothing
31429     },
31430     // private
31431     onMouseDown : function(e){
31432         this.rz.onMouseDown(this, e);
31433     },
31434     // private
31435     onMouseOver : function(e){
31436         this.rz.handleOver(this, e);
31437     },
31438     // private
31439     onMouseOut : function(e){
31440         this.rz.handleOut(this, e);
31441     }
31442 };/*
31443  * Based on:
31444  * Ext JS Library 1.1.1
31445  * Copyright(c) 2006-2007, Ext JS, LLC.
31446  *
31447  * Originally Released Under LGPL - original licence link has changed is not relivant.
31448  *
31449  * Fork - LGPL
31450  * <script type="text/javascript">
31451  */
31452
31453 /**
31454  * @class Roo.Editor
31455  * @extends Roo.Component
31456  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31457  * @constructor
31458  * Create a new Editor
31459  * @param {Roo.form.Field} field The Field object (or descendant)
31460  * @param {Object} config The config object
31461  */
31462 Roo.Editor = function(field, config){
31463     Roo.Editor.superclass.constructor.call(this, config);
31464     this.field = field;
31465     this.addEvents({
31466         /**
31467              * @event beforestartedit
31468              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31469              * false from the handler of this event.
31470              * @param {Editor} this
31471              * @param {Roo.Element} boundEl The underlying element bound to this editor
31472              * @param {Mixed} value The field value being set
31473              */
31474         "beforestartedit" : true,
31475         /**
31476              * @event startedit
31477              * Fires when this editor is displayed
31478              * @param {Roo.Element} boundEl The underlying element bound to this editor
31479              * @param {Mixed} value The starting field value
31480              */
31481         "startedit" : true,
31482         /**
31483              * @event beforecomplete
31484              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31485              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31486              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31487              * event will not fire since no edit actually occurred.
31488              * @param {Editor} this
31489              * @param {Mixed} value The current field value
31490              * @param {Mixed} startValue The original field value
31491              */
31492         "beforecomplete" : true,
31493         /**
31494              * @event complete
31495              * Fires after editing is complete and any changed value has been written to the underlying field.
31496              * @param {Editor} this
31497              * @param {Mixed} value The current field value
31498              * @param {Mixed} startValue The original field value
31499              */
31500         "complete" : true,
31501         /**
31502          * @event specialkey
31503          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31504          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31505          * @param {Roo.form.Field} this
31506          * @param {Roo.EventObject} e The event object
31507          */
31508         "specialkey" : true
31509     });
31510 };
31511
31512 Roo.extend(Roo.Editor, Roo.Component, {
31513     /**
31514      * @cfg {Boolean/String} autosize
31515      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31516      * or "height" to adopt the height only (defaults to false)
31517      */
31518     /**
31519      * @cfg {Boolean} revertInvalid
31520      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31521      * validation fails (defaults to true)
31522      */
31523     /**
31524      * @cfg {Boolean} ignoreNoChange
31525      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31526      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31527      * will never be ignored.
31528      */
31529     /**
31530      * @cfg {Boolean} hideEl
31531      * False to keep the bound element visible while the editor is displayed (defaults to true)
31532      */
31533     /**
31534      * @cfg {Mixed} value
31535      * The data value of the underlying field (defaults to "")
31536      */
31537     value : "",
31538     /**
31539      * @cfg {String} alignment
31540      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31541      */
31542     alignment: "c-c?",
31543     /**
31544      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31545      * for bottom-right shadow (defaults to "frame")
31546      */
31547     shadow : "frame",
31548     /**
31549      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31550      */
31551     constrain : false,
31552     /**
31553      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31554      */
31555     completeOnEnter : false,
31556     /**
31557      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31558      */
31559     cancelOnEsc : false,
31560     /**
31561      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31562      */
31563     updateEl : false,
31564
31565     // private
31566     onRender : function(ct, position){
31567         this.el = new Roo.Layer({
31568             shadow: this.shadow,
31569             cls: "x-editor",
31570             parentEl : ct,
31571             shim : this.shim,
31572             shadowOffset:4,
31573             id: this.id,
31574             constrain: this.constrain
31575         });
31576         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31577         if(this.field.msgTarget != 'title'){
31578             this.field.msgTarget = 'qtip';
31579         }
31580         this.field.render(this.el);
31581         if(Roo.isGecko){
31582             this.field.el.dom.setAttribute('autocomplete', 'off');
31583         }
31584         this.field.on("specialkey", this.onSpecialKey, this);
31585         if(this.swallowKeys){
31586             this.field.el.swallowEvent(['keydown','keypress']);
31587         }
31588         this.field.show();
31589         this.field.on("blur", this.onBlur, this);
31590         if(this.field.grow){
31591             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31592         }
31593     },
31594
31595     onSpecialKey : function(field, e)
31596     {
31597         //Roo.log('editor onSpecialKey');
31598         if(this.completeOnEnter && e.getKey() == e.ENTER){
31599             e.stopEvent();
31600             this.completeEdit();
31601             return;
31602         }
31603         // do not fire special key otherwise it might hide close the editor...
31604         if(e.getKey() == e.ENTER){    
31605             return;
31606         }
31607         if(this.cancelOnEsc && e.getKey() == e.ESC){
31608             this.cancelEdit();
31609             return;
31610         } 
31611         this.fireEvent('specialkey', field, e);
31612     
31613     },
31614
31615     /**
31616      * Starts the editing process and shows the editor.
31617      * @param {String/HTMLElement/Element} el The element to edit
31618      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31619       * to the innerHTML of el.
31620      */
31621     startEdit : function(el, value){
31622         if(this.editing){
31623             this.completeEdit();
31624         }
31625         this.boundEl = Roo.get(el);
31626         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31627         if(!this.rendered){
31628             this.render(this.parentEl || document.body);
31629         }
31630         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31631             return;
31632         }
31633         this.startValue = v;
31634         this.field.setValue(v);
31635         if(this.autoSize){
31636             var sz = this.boundEl.getSize();
31637             switch(this.autoSize){
31638                 case "width":
31639                 this.setSize(sz.width,  "");
31640                 break;
31641                 case "height":
31642                 this.setSize("",  sz.height);
31643                 break;
31644                 default:
31645                 this.setSize(sz.width,  sz.height);
31646             }
31647         }
31648         this.el.alignTo(this.boundEl, this.alignment);
31649         this.editing = true;
31650         if(Roo.QuickTips){
31651             Roo.QuickTips.disable();
31652         }
31653         this.show();
31654     },
31655
31656     /**
31657      * Sets the height and width of this editor.
31658      * @param {Number} width The new width
31659      * @param {Number} height The new height
31660      */
31661     setSize : function(w, h){
31662         this.field.setSize(w, h);
31663         if(this.el){
31664             this.el.sync();
31665         }
31666     },
31667
31668     /**
31669      * Realigns the editor to the bound field based on the current alignment config value.
31670      */
31671     realign : function(){
31672         this.el.alignTo(this.boundEl, this.alignment);
31673     },
31674
31675     /**
31676      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31677      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31678      */
31679     completeEdit : function(remainVisible){
31680         if(!this.editing){
31681             return;
31682         }
31683         var v = this.getValue();
31684         if(this.revertInvalid !== false && !this.field.isValid()){
31685             v = this.startValue;
31686             this.cancelEdit(true);
31687         }
31688         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31689             this.editing = false;
31690             this.hide();
31691             return;
31692         }
31693         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31694             this.editing = false;
31695             if(this.updateEl && this.boundEl){
31696                 this.boundEl.update(v);
31697             }
31698             if(remainVisible !== true){
31699                 this.hide();
31700             }
31701             this.fireEvent("complete", this, v, this.startValue);
31702         }
31703     },
31704
31705     // private
31706     onShow : function(){
31707         this.el.show();
31708         if(this.hideEl !== false){
31709             this.boundEl.hide();
31710         }
31711         this.field.show();
31712         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31713             this.fixIEFocus = true;
31714             this.deferredFocus.defer(50, this);
31715         }else{
31716             this.field.focus();
31717         }
31718         this.fireEvent("startedit", this.boundEl, this.startValue);
31719     },
31720
31721     deferredFocus : function(){
31722         if(this.editing){
31723             this.field.focus();
31724         }
31725     },
31726
31727     /**
31728      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31729      * reverted to the original starting value.
31730      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31731      * cancel (defaults to false)
31732      */
31733     cancelEdit : function(remainVisible){
31734         if(this.editing){
31735             this.setValue(this.startValue);
31736             if(remainVisible !== true){
31737                 this.hide();
31738             }
31739         }
31740     },
31741
31742     // private
31743     onBlur : function(){
31744         if(this.allowBlur !== true && this.editing){
31745             this.completeEdit();
31746         }
31747     },
31748
31749     // private
31750     onHide : function(){
31751         if(this.editing){
31752             this.completeEdit();
31753             return;
31754         }
31755         this.field.blur();
31756         if(this.field.collapse){
31757             this.field.collapse();
31758         }
31759         this.el.hide();
31760         if(this.hideEl !== false){
31761             this.boundEl.show();
31762         }
31763         if(Roo.QuickTips){
31764             Roo.QuickTips.enable();
31765         }
31766     },
31767
31768     /**
31769      * Sets the data value of the editor
31770      * @param {Mixed} value Any valid value supported by the underlying field
31771      */
31772     setValue : function(v){
31773         this.field.setValue(v);
31774     },
31775
31776     /**
31777      * Gets the data value of the editor
31778      * @return {Mixed} The data value
31779      */
31780     getValue : function(){
31781         return this.field.getValue();
31782     }
31783 });/*
31784  * Based on:
31785  * Ext JS Library 1.1.1
31786  * Copyright(c) 2006-2007, Ext JS, LLC.
31787  *
31788  * Originally Released Under LGPL - original licence link has changed is not relivant.
31789  *
31790  * Fork - LGPL
31791  * <script type="text/javascript">
31792  */
31793  
31794 /**
31795  * @class Roo.BasicDialog
31796  * @extends Roo.util.Observable
31797  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31798  * <pre><code>
31799 var dlg = new Roo.BasicDialog("my-dlg", {
31800     height: 200,
31801     width: 300,
31802     minHeight: 100,
31803     minWidth: 150,
31804     modal: true,
31805     proxyDrag: true,
31806     shadow: true
31807 });
31808 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31809 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31810 dlg.addButton('Cancel', dlg.hide, dlg);
31811 dlg.show();
31812 </code></pre>
31813   <b>A Dialog should always be a direct child of the body element.</b>
31814  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31815  * @cfg {String} title Default text to display in the title bar (defaults to null)
31816  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31817  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31818  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31819  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31820  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31821  * (defaults to null with no animation)
31822  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31823  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31824  * property for valid values (defaults to 'all')
31825  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31826  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31827  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31828  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31829  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31830  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31831  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31832  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31833  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31834  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31835  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31836  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31837  * draggable = true (defaults to false)
31838  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31839  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31840  * shadow (defaults to false)
31841  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31842  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31843  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31844  * @cfg {Array} buttons Array of buttons
31845  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31846  * @constructor
31847  * Create a new BasicDialog.
31848  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31849  * @param {Object} config Configuration options
31850  */
31851 Roo.BasicDialog = function(el, config){
31852     this.el = Roo.get(el);
31853     var dh = Roo.DomHelper;
31854     if(!this.el && config && config.autoCreate){
31855         if(typeof config.autoCreate == "object"){
31856             if(!config.autoCreate.id){
31857                 config.autoCreate.id = el;
31858             }
31859             this.el = dh.append(document.body,
31860                         config.autoCreate, true);
31861         }else{
31862             this.el = dh.append(document.body,
31863                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31864         }
31865     }
31866     el = this.el;
31867     el.setDisplayed(true);
31868     el.hide = this.hideAction;
31869     this.id = el.id;
31870     el.addClass("x-dlg");
31871
31872     Roo.apply(this, config);
31873
31874     this.proxy = el.createProxy("x-dlg-proxy");
31875     this.proxy.hide = this.hideAction;
31876     this.proxy.setOpacity(.5);
31877     this.proxy.hide();
31878
31879     if(config.width){
31880         el.setWidth(config.width);
31881     }
31882     if(config.height){
31883         el.setHeight(config.height);
31884     }
31885     this.size = el.getSize();
31886     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31887         this.xy = [config.x,config.y];
31888     }else{
31889         this.xy = el.getCenterXY(true);
31890     }
31891     /** The header element @type Roo.Element */
31892     this.header = el.child("> .x-dlg-hd");
31893     /** The body element @type Roo.Element */
31894     this.body = el.child("> .x-dlg-bd");
31895     /** The footer element @type Roo.Element */
31896     this.footer = el.child("> .x-dlg-ft");
31897
31898     if(!this.header){
31899         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31900     }
31901     if(!this.body){
31902         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31903     }
31904
31905     this.header.unselectable();
31906     if(this.title){
31907         this.header.update(this.title);
31908     }
31909     // this element allows the dialog to be focused for keyboard event
31910     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31911     this.focusEl.swallowEvent("click", true);
31912
31913     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31914
31915     // wrap the body and footer for special rendering
31916     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31917     if(this.footer){
31918         this.bwrap.dom.appendChild(this.footer.dom);
31919     }
31920
31921     this.bg = this.el.createChild({
31922         tag: "div", cls:"x-dlg-bg",
31923         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31924     });
31925     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31926
31927
31928     if(this.autoScroll !== false && !this.autoTabs){
31929         this.body.setStyle("overflow", "auto");
31930     }
31931
31932     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31933
31934     if(this.closable !== false){
31935         this.el.addClass("x-dlg-closable");
31936         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31937         this.close.on("click", this.closeClick, this);
31938         this.close.addClassOnOver("x-dlg-close-over");
31939     }
31940     if(this.collapsible !== false){
31941         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31942         this.collapseBtn.on("click", this.collapseClick, this);
31943         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31944         this.header.on("dblclick", this.collapseClick, this);
31945     }
31946     if(this.resizable !== false){
31947         this.el.addClass("x-dlg-resizable");
31948         this.resizer = new Roo.Resizable(el, {
31949             minWidth: this.minWidth || 80,
31950             minHeight:this.minHeight || 80,
31951             handles: this.resizeHandles || "all",
31952             pinned: true
31953         });
31954         this.resizer.on("beforeresize", this.beforeResize, this);
31955         this.resizer.on("resize", this.onResize, this);
31956     }
31957     if(this.draggable !== false){
31958         el.addClass("x-dlg-draggable");
31959         if (!this.proxyDrag) {
31960             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31961         }
31962         else {
31963             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31964         }
31965         dd.setHandleElId(this.header.id);
31966         dd.endDrag = this.endMove.createDelegate(this);
31967         dd.startDrag = this.startMove.createDelegate(this);
31968         dd.onDrag = this.onDrag.createDelegate(this);
31969         dd.scroll = false;
31970         this.dd = dd;
31971     }
31972     if(this.modal){
31973         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31974         this.mask.enableDisplayMode("block");
31975         this.mask.hide();
31976         this.el.addClass("x-dlg-modal");
31977     }
31978     if(this.shadow){
31979         this.shadow = new Roo.Shadow({
31980             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31981             offset : this.shadowOffset
31982         });
31983     }else{
31984         this.shadowOffset = 0;
31985     }
31986     if(Roo.useShims && this.shim !== false){
31987         this.shim = this.el.createShim();
31988         this.shim.hide = this.hideAction;
31989         this.shim.hide();
31990     }else{
31991         this.shim = false;
31992     }
31993     if(this.autoTabs){
31994         this.initTabs();
31995     }
31996     if (this.buttons) { 
31997         var bts= this.buttons;
31998         this.buttons = [];
31999         Roo.each(bts, function(b) {
32000             this.addButton(b);
32001         }, this);
32002     }
32003     
32004     
32005     this.addEvents({
32006         /**
32007          * @event keydown
32008          * Fires when a key is pressed
32009          * @param {Roo.BasicDialog} this
32010          * @param {Roo.EventObject} e
32011          */
32012         "keydown" : true,
32013         /**
32014          * @event move
32015          * Fires when this dialog is moved by the user.
32016          * @param {Roo.BasicDialog} this
32017          * @param {Number} x The new page X
32018          * @param {Number} y The new page Y
32019          */
32020         "move" : true,
32021         /**
32022          * @event resize
32023          * Fires when this dialog is resized by the user.
32024          * @param {Roo.BasicDialog} this
32025          * @param {Number} width The new width
32026          * @param {Number} height The new height
32027          */
32028         "resize" : true,
32029         /**
32030          * @event beforehide
32031          * Fires before this dialog is hidden.
32032          * @param {Roo.BasicDialog} this
32033          */
32034         "beforehide" : true,
32035         /**
32036          * @event hide
32037          * Fires when this dialog is hidden.
32038          * @param {Roo.BasicDialog} this
32039          */
32040         "hide" : true,
32041         /**
32042          * @event beforeshow
32043          * Fires before this dialog is shown.
32044          * @param {Roo.BasicDialog} this
32045          */
32046         "beforeshow" : true,
32047         /**
32048          * @event show
32049          * Fires when this dialog is shown.
32050          * @param {Roo.BasicDialog} this
32051          */
32052         "show" : true
32053     });
32054     el.on("keydown", this.onKeyDown, this);
32055     el.on("mousedown", this.toFront, this);
32056     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32057     this.el.hide();
32058     Roo.DialogManager.register(this);
32059     Roo.BasicDialog.superclass.constructor.call(this);
32060 };
32061
32062 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32063     shadowOffset: Roo.isIE ? 6 : 5,
32064     minHeight: 80,
32065     minWidth: 200,
32066     minButtonWidth: 75,
32067     defaultButton: null,
32068     buttonAlign: "right",
32069     tabTag: 'div',
32070     firstShow: true,
32071
32072     /**
32073      * Sets the dialog title text
32074      * @param {String} text The title text to display
32075      * @return {Roo.BasicDialog} this
32076      */
32077     setTitle : function(text){
32078         this.header.update(text);
32079         return this;
32080     },
32081
32082     // private
32083     closeClick : function(){
32084         this.hide();
32085     },
32086
32087     // private
32088     collapseClick : function(){
32089         this[this.collapsed ? "expand" : "collapse"]();
32090     },
32091
32092     /**
32093      * Collapses the dialog to its minimized state (only the title bar is visible).
32094      * Equivalent to the user clicking the collapse dialog button.
32095      */
32096     collapse : function(){
32097         if(!this.collapsed){
32098             this.collapsed = true;
32099             this.el.addClass("x-dlg-collapsed");
32100             this.restoreHeight = this.el.getHeight();
32101             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32102         }
32103     },
32104
32105     /**
32106      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32107      * clicking the expand dialog button.
32108      */
32109     expand : function(){
32110         if(this.collapsed){
32111             this.collapsed = false;
32112             this.el.removeClass("x-dlg-collapsed");
32113             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32114         }
32115     },
32116
32117     /**
32118      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32119      * @return {Roo.TabPanel} The tabs component
32120      */
32121     initTabs : function(){
32122         var tabs = this.getTabs();
32123         while(tabs.getTab(0)){
32124             tabs.removeTab(0);
32125         }
32126         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32127             var dom = el.dom;
32128             tabs.addTab(Roo.id(dom), dom.title);
32129             dom.title = "";
32130         });
32131         tabs.activate(0);
32132         return tabs;
32133     },
32134
32135     // private
32136     beforeResize : function(){
32137         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32138     },
32139
32140     // private
32141     onResize : function(){
32142         this.refreshSize();
32143         this.syncBodyHeight();
32144         this.adjustAssets();
32145         this.focus();
32146         this.fireEvent("resize", this, this.size.width, this.size.height);
32147     },
32148
32149     // private
32150     onKeyDown : function(e){
32151         if(this.isVisible()){
32152             this.fireEvent("keydown", this, e);
32153         }
32154     },
32155
32156     /**
32157      * Resizes the dialog.
32158      * @param {Number} width
32159      * @param {Number} height
32160      * @return {Roo.BasicDialog} this
32161      */
32162     resizeTo : function(width, height){
32163         this.el.setSize(width, height);
32164         this.size = {width: width, height: height};
32165         this.syncBodyHeight();
32166         if(this.fixedcenter){
32167             this.center();
32168         }
32169         if(this.isVisible()){
32170             this.constrainXY();
32171             this.adjustAssets();
32172         }
32173         this.fireEvent("resize", this, width, height);
32174         return this;
32175     },
32176
32177
32178     /**
32179      * Resizes the dialog to fit the specified content size.
32180      * @param {Number} width
32181      * @param {Number} height
32182      * @return {Roo.BasicDialog} this
32183      */
32184     setContentSize : function(w, h){
32185         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32186         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32187         //if(!this.el.isBorderBox()){
32188             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32189             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32190         //}
32191         if(this.tabs){
32192             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32193             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32194         }
32195         this.resizeTo(w, h);
32196         return this;
32197     },
32198
32199     /**
32200      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32201      * executed in response to a particular key being pressed while the dialog is active.
32202      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32203      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32204      * @param {Function} fn The function to call
32205      * @param {Object} scope (optional) The scope of the function
32206      * @return {Roo.BasicDialog} this
32207      */
32208     addKeyListener : function(key, fn, scope){
32209         var keyCode, shift, ctrl, alt;
32210         if(typeof key == "object" && !(key instanceof Array)){
32211             keyCode = key["key"];
32212             shift = key["shift"];
32213             ctrl = key["ctrl"];
32214             alt = key["alt"];
32215         }else{
32216             keyCode = key;
32217         }
32218         var handler = function(dlg, e){
32219             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32220                 var k = e.getKey();
32221                 if(keyCode instanceof Array){
32222                     for(var i = 0, len = keyCode.length; i < len; i++){
32223                         if(keyCode[i] == k){
32224                           fn.call(scope || window, dlg, k, e);
32225                           return;
32226                         }
32227                     }
32228                 }else{
32229                     if(k == keyCode){
32230                         fn.call(scope || window, dlg, k, e);
32231                     }
32232                 }
32233             }
32234         };
32235         this.on("keydown", handler);
32236         return this;
32237     },
32238
32239     /**
32240      * Returns the TabPanel component (creates it if it doesn't exist).
32241      * Note: If you wish to simply check for the existence of tabs without creating them,
32242      * check for a null 'tabs' property.
32243      * @return {Roo.TabPanel} The tabs component
32244      */
32245     getTabs : function(){
32246         if(!this.tabs){
32247             this.el.addClass("x-dlg-auto-tabs");
32248             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32249             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32250         }
32251         return this.tabs;
32252     },
32253
32254     /**
32255      * Adds a button to the footer section of the dialog.
32256      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32257      * object or a valid Roo.DomHelper element config
32258      * @param {Function} handler The function called when the button is clicked
32259      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32260      * @return {Roo.Button} The new button
32261      */
32262     addButton : function(config, handler, scope){
32263         var dh = Roo.DomHelper;
32264         if(!this.footer){
32265             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32266         }
32267         if(!this.btnContainer){
32268             var tb = this.footer.createChild({
32269
32270                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32271                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32272             }, null, true);
32273             this.btnContainer = tb.firstChild.firstChild.firstChild;
32274         }
32275         var bconfig = {
32276             handler: handler,
32277             scope: scope,
32278             minWidth: this.minButtonWidth,
32279             hideParent:true
32280         };
32281         if(typeof config == "string"){
32282             bconfig.text = config;
32283         }else{
32284             if(config.tag){
32285                 bconfig.dhconfig = config;
32286             }else{
32287                 Roo.apply(bconfig, config);
32288             }
32289         }
32290         var fc = false;
32291         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32292             bconfig.position = Math.max(0, bconfig.position);
32293             fc = this.btnContainer.childNodes[bconfig.position];
32294         }
32295          
32296         var btn = new Roo.Button(
32297             fc ? 
32298                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32299                 : this.btnContainer.appendChild(document.createElement("td")),
32300             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32301             bconfig
32302         );
32303         this.syncBodyHeight();
32304         if(!this.buttons){
32305             /**
32306              * Array of all the buttons that have been added to this dialog via addButton
32307              * @type Array
32308              */
32309             this.buttons = [];
32310         }
32311         this.buttons.push(btn);
32312         return btn;
32313     },
32314
32315     /**
32316      * Sets the default button to be focused when the dialog is displayed.
32317      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32318      * @return {Roo.BasicDialog} this
32319      */
32320     setDefaultButton : function(btn){
32321         this.defaultButton = btn;
32322         return this;
32323     },
32324
32325     // private
32326     getHeaderFooterHeight : function(safe){
32327         var height = 0;
32328         if(this.header){
32329            height += this.header.getHeight();
32330         }
32331         if(this.footer){
32332            var fm = this.footer.getMargins();
32333             height += (this.footer.getHeight()+fm.top+fm.bottom);
32334         }
32335         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32336         height += this.centerBg.getPadding("tb");
32337         return height;
32338     },
32339
32340     // private
32341     syncBodyHeight : function()
32342     {
32343         var bd = this.body, // the text
32344             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32345             bw = this.bwrap;
32346         var height = this.size.height - this.getHeaderFooterHeight(false);
32347         bd.setHeight(height-bd.getMargins("tb"));
32348         var hh = this.header.getHeight();
32349         var h = this.size.height-hh;
32350         cb.setHeight(h);
32351         
32352         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32353         bw.setHeight(h-cb.getPadding("tb"));
32354         
32355         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32356         bd.setWidth(bw.getWidth(true));
32357         if(this.tabs){
32358             this.tabs.syncHeight();
32359             if(Roo.isIE){
32360                 this.tabs.el.repaint();
32361             }
32362         }
32363     },
32364
32365     /**
32366      * Restores the previous state of the dialog if Roo.state is configured.
32367      * @return {Roo.BasicDialog} this
32368      */
32369     restoreState : function(){
32370         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32371         if(box && box.width){
32372             this.xy = [box.x, box.y];
32373             this.resizeTo(box.width, box.height);
32374         }
32375         return this;
32376     },
32377
32378     // private
32379     beforeShow : function(){
32380         this.expand();
32381         if(this.fixedcenter){
32382             this.xy = this.el.getCenterXY(true);
32383         }
32384         if(this.modal){
32385             Roo.get(document.body).addClass("x-body-masked");
32386             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32387             this.mask.show();
32388         }
32389         this.constrainXY();
32390     },
32391
32392     // private
32393     animShow : function(){
32394         var b = Roo.get(this.animateTarget).getBox();
32395         this.proxy.setSize(b.width, b.height);
32396         this.proxy.setLocation(b.x, b.y);
32397         this.proxy.show();
32398         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32399                     true, .35, this.showEl.createDelegate(this));
32400     },
32401
32402     /**
32403      * Shows the dialog.
32404      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32405      * @return {Roo.BasicDialog} this
32406      */
32407     show : function(animateTarget){
32408         if (this.fireEvent("beforeshow", this) === false){
32409             return;
32410         }
32411         if(this.syncHeightBeforeShow){
32412             this.syncBodyHeight();
32413         }else if(this.firstShow){
32414             this.firstShow = false;
32415             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32416         }
32417         this.animateTarget = animateTarget || this.animateTarget;
32418         if(!this.el.isVisible()){
32419             this.beforeShow();
32420             if(this.animateTarget && Roo.get(this.animateTarget)){
32421                 this.animShow();
32422             }else{
32423                 this.showEl();
32424             }
32425         }
32426         return this;
32427     },
32428
32429     // private
32430     showEl : function(){
32431         this.proxy.hide();
32432         this.el.setXY(this.xy);
32433         this.el.show();
32434         this.adjustAssets(true);
32435         this.toFront();
32436         this.focus();
32437         // IE peekaboo bug - fix found by Dave Fenwick
32438         if(Roo.isIE){
32439             this.el.repaint();
32440         }
32441         this.fireEvent("show", this);
32442     },
32443
32444     /**
32445      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32446      * dialog itself will receive focus.
32447      */
32448     focus : function(){
32449         if(this.defaultButton){
32450             this.defaultButton.focus();
32451         }else{
32452             this.focusEl.focus();
32453         }
32454     },
32455
32456     // private
32457     constrainXY : function(){
32458         if(this.constraintoviewport !== false){
32459             if(!this.viewSize){
32460                 if(this.container){
32461                     var s = this.container.getSize();
32462                     this.viewSize = [s.width, s.height];
32463                 }else{
32464                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32465                 }
32466             }
32467             var s = Roo.get(this.container||document).getScroll();
32468
32469             var x = this.xy[0], y = this.xy[1];
32470             var w = this.size.width, h = this.size.height;
32471             var vw = this.viewSize[0], vh = this.viewSize[1];
32472             // only move it if it needs it
32473             var moved = false;
32474             // first validate right/bottom
32475             if(x + w > vw+s.left){
32476                 x = vw - w;
32477                 moved = true;
32478             }
32479             if(y + h > vh+s.top){
32480                 y = vh - h;
32481                 moved = true;
32482             }
32483             // then make sure top/left isn't negative
32484             if(x < s.left){
32485                 x = s.left;
32486                 moved = true;
32487             }
32488             if(y < s.top){
32489                 y = s.top;
32490                 moved = true;
32491             }
32492             if(moved){
32493                 // cache xy
32494                 this.xy = [x, y];
32495                 if(this.isVisible()){
32496                     this.el.setLocation(x, y);
32497                     this.adjustAssets();
32498                 }
32499             }
32500         }
32501     },
32502
32503     // private
32504     onDrag : function(){
32505         if(!this.proxyDrag){
32506             this.xy = this.el.getXY();
32507             this.adjustAssets();
32508         }
32509     },
32510
32511     // private
32512     adjustAssets : function(doShow){
32513         var x = this.xy[0], y = this.xy[1];
32514         var w = this.size.width, h = this.size.height;
32515         if(doShow === true){
32516             if(this.shadow){
32517                 this.shadow.show(this.el);
32518             }
32519             if(this.shim){
32520                 this.shim.show();
32521             }
32522         }
32523         if(this.shadow && this.shadow.isVisible()){
32524             this.shadow.show(this.el);
32525         }
32526         if(this.shim && this.shim.isVisible()){
32527             this.shim.setBounds(x, y, w, h);
32528         }
32529     },
32530
32531     // private
32532     adjustViewport : function(w, h){
32533         if(!w || !h){
32534             w = Roo.lib.Dom.getViewWidth();
32535             h = Roo.lib.Dom.getViewHeight();
32536         }
32537         // cache the size
32538         this.viewSize = [w, h];
32539         if(this.modal && this.mask.isVisible()){
32540             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32541             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32542         }
32543         if(this.isVisible()){
32544             this.constrainXY();
32545         }
32546     },
32547
32548     /**
32549      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32550      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32551      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32552      */
32553     destroy : function(removeEl){
32554         if(this.isVisible()){
32555             this.animateTarget = null;
32556             this.hide();
32557         }
32558         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32559         if(this.tabs){
32560             this.tabs.destroy(removeEl);
32561         }
32562         Roo.destroy(
32563              this.shim,
32564              this.proxy,
32565              this.resizer,
32566              this.close,
32567              this.mask
32568         );
32569         if(this.dd){
32570             this.dd.unreg();
32571         }
32572         if(this.buttons){
32573            for(var i = 0, len = this.buttons.length; i < len; i++){
32574                this.buttons[i].destroy();
32575            }
32576         }
32577         this.el.removeAllListeners();
32578         if(removeEl === true){
32579             this.el.update("");
32580             this.el.remove();
32581         }
32582         Roo.DialogManager.unregister(this);
32583     },
32584
32585     // private
32586     startMove : function(){
32587         if(this.proxyDrag){
32588             this.proxy.show();
32589         }
32590         if(this.constraintoviewport !== false){
32591             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32592         }
32593     },
32594
32595     // private
32596     endMove : function(){
32597         if(!this.proxyDrag){
32598             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32599         }else{
32600             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32601             this.proxy.hide();
32602         }
32603         this.refreshSize();
32604         this.adjustAssets();
32605         this.focus();
32606         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32607     },
32608
32609     /**
32610      * Brings this dialog to the front of any other visible dialogs
32611      * @return {Roo.BasicDialog} this
32612      */
32613     toFront : function(){
32614         Roo.DialogManager.bringToFront(this);
32615         return this;
32616     },
32617
32618     /**
32619      * Sends this dialog to the back (under) of any other visible dialogs
32620      * @return {Roo.BasicDialog} this
32621      */
32622     toBack : function(){
32623         Roo.DialogManager.sendToBack(this);
32624         return this;
32625     },
32626
32627     /**
32628      * Centers this dialog in the viewport
32629      * @return {Roo.BasicDialog} this
32630      */
32631     center : function(){
32632         var xy = this.el.getCenterXY(true);
32633         this.moveTo(xy[0], xy[1]);
32634         return this;
32635     },
32636
32637     /**
32638      * Moves the dialog's top-left corner to the specified point
32639      * @param {Number} x
32640      * @param {Number} y
32641      * @return {Roo.BasicDialog} this
32642      */
32643     moveTo : function(x, y){
32644         this.xy = [x,y];
32645         if(this.isVisible()){
32646             this.el.setXY(this.xy);
32647             this.adjustAssets();
32648         }
32649         return this;
32650     },
32651
32652     /**
32653      * Aligns the dialog to the specified element
32654      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32655      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32656      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32657      * @return {Roo.BasicDialog} this
32658      */
32659     alignTo : function(element, position, offsets){
32660         this.xy = this.el.getAlignToXY(element, position, offsets);
32661         if(this.isVisible()){
32662             this.el.setXY(this.xy);
32663             this.adjustAssets();
32664         }
32665         return this;
32666     },
32667
32668     /**
32669      * Anchors an element to another element and realigns it when the window is resized.
32670      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32671      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32672      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32673      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32674      * is a number, it is used as the buffer delay (defaults to 50ms).
32675      * @return {Roo.BasicDialog} this
32676      */
32677     anchorTo : function(el, alignment, offsets, monitorScroll){
32678         var action = function(){
32679             this.alignTo(el, alignment, offsets);
32680         };
32681         Roo.EventManager.onWindowResize(action, this);
32682         var tm = typeof monitorScroll;
32683         if(tm != 'undefined'){
32684             Roo.EventManager.on(window, 'scroll', action, this,
32685                 {buffer: tm == 'number' ? monitorScroll : 50});
32686         }
32687         action.call(this);
32688         return this;
32689     },
32690
32691     /**
32692      * Returns true if the dialog is visible
32693      * @return {Boolean}
32694      */
32695     isVisible : function(){
32696         return this.el.isVisible();
32697     },
32698
32699     // private
32700     animHide : function(callback){
32701         var b = Roo.get(this.animateTarget).getBox();
32702         this.proxy.show();
32703         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32704         this.el.hide();
32705         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32706                     this.hideEl.createDelegate(this, [callback]));
32707     },
32708
32709     /**
32710      * Hides the dialog.
32711      * @param {Function} callback (optional) Function to call when the dialog is hidden
32712      * @return {Roo.BasicDialog} this
32713      */
32714     hide : function(callback){
32715         if (this.fireEvent("beforehide", this) === false){
32716             return;
32717         }
32718         if(this.shadow){
32719             this.shadow.hide();
32720         }
32721         if(this.shim) {
32722           this.shim.hide();
32723         }
32724         // sometimes animateTarget seems to get set.. causing problems...
32725         // this just double checks..
32726         if(this.animateTarget && Roo.get(this.animateTarget)) {
32727            this.animHide(callback);
32728         }else{
32729             this.el.hide();
32730             this.hideEl(callback);
32731         }
32732         return this;
32733     },
32734
32735     // private
32736     hideEl : function(callback){
32737         this.proxy.hide();
32738         if(this.modal){
32739             this.mask.hide();
32740             Roo.get(document.body).removeClass("x-body-masked");
32741         }
32742         this.fireEvent("hide", this);
32743         if(typeof callback == "function"){
32744             callback();
32745         }
32746     },
32747
32748     // private
32749     hideAction : function(){
32750         this.setLeft("-10000px");
32751         this.setTop("-10000px");
32752         this.setStyle("visibility", "hidden");
32753     },
32754
32755     // private
32756     refreshSize : function(){
32757         this.size = this.el.getSize();
32758         this.xy = this.el.getXY();
32759         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32760     },
32761
32762     // private
32763     // z-index is managed by the DialogManager and may be overwritten at any time
32764     setZIndex : function(index){
32765         if(this.modal){
32766             this.mask.setStyle("z-index", index);
32767         }
32768         if(this.shim){
32769             this.shim.setStyle("z-index", ++index);
32770         }
32771         if(this.shadow){
32772             this.shadow.setZIndex(++index);
32773         }
32774         this.el.setStyle("z-index", ++index);
32775         if(this.proxy){
32776             this.proxy.setStyle("z-index", ++index);
32777         }
32778         if(this.resizer){
32779             this.resizer.proxy.setStyle("z-index", ++index);
32780         }
32781
32782         this.lastZIndex = index;
32783     },
32784
32785     /**
32786      * Returns the element for this dialog
32787      * @return {Roo.Element} The underlying dialog Element
32788      */
32789     getEl : function(){
32790         return this.el;
32791     }
32792 });
32793
32794 /**
32795  * @class Roo.DialogManager
32796  * Provides global access to BasicDialogs that have been created and
32797  * support for z-indexing (layering) multiple open dialogs.
32798  */
32799 Roo.DialogManager = function(){
32800     var list = {};
32801     var accessList = [];
32802     var front = null;
32803
32804     // private
32805     var sortDialogs = function(d1, d2){
32806         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32807     };
32808
32809     // private
32810     var orderDialogs = function(){
32811         accessList.sort(sortDialogs);
32812         var seed = Roo.DialogManager.zseed;
32813         for(var i = 0, len = accessList.length; i < len; i++){
32814             var dlg = accessList[i];
32815             if(dlg){
32816                 dlg.setZIndex(seed + (i*10));
32817             }
32818         }
32819     };
32820
32821     return {
32822         /**
32823          * The starting z-index for BasicDialogs (defaults to 9000)
32824          * @type Number The z-index value
32825          */
32826         zseed : 9000,
32827
32828         // private
32829         register : function(dlg){
32830             list[dlg.id] = dlg;
32831             accessList.push(dlg);
32832         },
32833
32834         // private
32835         unregister : function(dlg){
32836             delete list[dlg.id];
32837             var i=0;
32838             var len=0;
32839             if(!accessList.indexOf){
32840                 for(  i = 0, len = accessList.length; i < len; i++){
32841                     if(accessList[i] == dlg){
32842                         accessList.splice(i, 1);
32843                         return;
32844                     }
32845                 }
32846             }else{
32847                  i = accessList.indexOf(dlg);
32848                 if(i != -1){
32849                     accessList.splice(i, 1);
32850                 }
32851             }
32852         },
32853
32854         /**
32855          * Gets a registered dialog by id
32856          * @param {String/Object} id The id of the dialog or a dialog
32857          * @return {Roo.BasicDialog} this
32858          */
32859         get : function(id){
32860             return typeof id == "object" ? id : list[id];
32861         },
32862
32863         /**
32864          * Brings the specified dialog to the front
32865          * @param {String/Object} dlg The id of the dialog or a dialog
32866          * @return {Roo.BasicDialog} this
32867          */
32868         bringToFront : function(dlg){
32869             dlg = this.get(dlg);
32870             if(dlg != front){
32871                 front = dlg;
32872                 dlg._lastAccess = new Date().getTime();
32873                 orderDialogs();
32874             }
32875             return dlg;
32876         },
32877
32878         /**
32879          * Sends the specified dialog to the back
32880          * @param {String/Object} dlg The id of the dialog or a dialog
32881          * @return {Roo.BasicDialog} this
32882          */
32883         sendToBack : function(dlg){
32884             dlg = this.get(dlg);
32885             dlg._lastAccess = -(new Date().getTime());
32886             orderDialogs();
32887             return dlg;
32888         },
32889
32890         /**
32891          * Hides all dialogs
32892          */
32893         hideAll : function(){
32894             for(var id in list){
32895                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32896                     list[id].hide();
32897                 }
32898             }
32899         }
32900     };
32901 }();
32902
32903 /**
32904  * @class Roo.LayoutDialog
32905  * @extends Roo.BasicDialog
32906  * Dialog which provides adjustments for working with a layout in a Dialog.
32907  * Add your necessary layout config options to the dialog's config.<br>
32908  * Example usage (including a nested layout):
32909  * <pre><code>
32910 if(!dialog){
32911     dialog = new Roo.LayoutDialog("download-dlg", {
32912         modal: true,
32913         width:600,
32914         height:450,
32915         shadow:true,
32916         minWidth:500,
32917         minHeight:350,
32918         autoTabs:true,
32919         proxyDrag:true,
32920         // layout config merges with the dialog config
32921         center:{
32922             tabPosition: "top",
32923             alwaysShowTabs: true
32924         }
32925     });
32926     dialog.addKeyListener(27, dialog.hide, dialog);
32927     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32928     dialog.addButton("Build It!", this.getDownload, this);
32929
32930     // we can even add nested layouts
32931     var innerLayout = new Roo.BorderLayout("dl-inner", {
32932         east: {
32933             initialSize: 200,
32934             autoScroll:true,
32935             split:true
32936         },
32937         center: {
32938             autoScroll:true
32939         }
32940     });
32941     innerLayout.beginUpdate();
32942     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32943     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32944     innerLayout.endUpdate(true);
32945
32946     var layout = dialog.getLayout();
32947     layout.beginUpdate();
32948     layout.add("center", new Roo.ContentPanel("standard-panel",
32949                         {title: "Download the Source", fitToFrame:true}));
32950     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32951                {title: "Build your own roo.js"}));
32952     layout.getRegion("center").showPanel(sp);
32953     layout.endUpdate();
32954 }
32955 </code></pre>
32956     * @constructor
32957     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32958     * @param {Object} config configuration options
32959   */
32960 Roo.LayoutDialog = function(el, cfg){
32961     
32962     var config=  cfg;
32963     if (typeof(cfg) == 'undefined') {
32964         config = Roo.apply({}, el);
32965         // not sure why we use documentElement here.. - it should always be body.
32966         // IE7 borks horribly if we use documentElement.
32967         // webkit also does not like documentElement - it creates a body element...
32968         el = Roo.get( document.body || document.documentElement ).createChild();
32969         //config.autoCreate = true;
32970     }
32971     
32972     
32973     config.autoTabs = false;
32974     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32975     this.body.setStyle({overflow:"hidden", position:"relative"});
32976     this.layout = new Roo.BorderLayout(this.body.dom, config);
32977     this.layout.monitorWindowResize = false;
32978     this.el.addClass("x-dlg-auto-layout");
32979     // fix case when center region overwrites center function
32980     this.center = Roo.BasicDialog.prototype.center;
32981     this.on("show", this.layout.layout, this.layout, true);
32982     if (config.items) {
32983         var xitems = config.items;
32984         delete config.items;
32985         Roo.each(xitems, this.addxtype, this);
32986     }
32987     
32988     
32989 };
32990 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32991     /**
32992      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32993      * @deprecated
32994      */
32995     endUpdate : function(){
32996         this.layout.endUpdate();
32997     },
32998
32999     /**
33000      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33001      *  @deprecated
33002      */
33003     beginUpdate : function(){
33004         this.layout.beginUpdate();
33005     },
33006
33007     /**
33008      * Get the BorderLayout for this dialog
33009      * @return {Roo.BorderLayout}
33010      */
33011     getLayout : function(){
33012         return this.layout;
33013     },
33014
33015     showEl : function(){
33016         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33017         if(Roo.isIE7){
33018             this.layout.layout();
33019         }
33020     },
33021
33022     // private
33023     // Use the syncHeightBeforeShow config option to control this automatically
33024     syncBodyHeight : function(){
33025         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33026         if(this.layout){this.layout.layout();}
33027     },
33028     
33029       /**
33030      * Add an xtype element (actually adds to the layout.)
33031      * @return {Object} xdata xtype object data.
33032      */
33033     
33034     addxtype : function(c) {
33035         return this.layout.addxtype(c);
33036     }
33037 });/*
33038  * Based on:
33039  * Ext JS Library 1.1.1
33040  * Copyright(c) 2006-2007, Ext JS, LLC.
33041  *
33042  * Originally Released Under LGPL - original licence link has changed is not relivant.
33043  *
33044  * Fork - LGPL
33045  * <script type="text/javascript">
33046  */
33047  
33048 /**
33049  * @class Roo.MessageBox
33050  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33051  * Example usage:
33052  *<pre><code>
33053 // Basic alert:
33054 Roo.Msg.alert('Status', 'Changes saved successfully.');
33055
33056 // Prompt for user data:
33057 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33058     if (btn == 'ok'){
33059         // process text value...
33060     }
33061 });
33062
33063 // Show a dialog using config options:
33064 Roo.Msg.show({
33065    title:'Save Changes?',
33066    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33067    buttons: Roo.Msg.YESNOCANCEL,
33068    fn: processResult,
33069    animEl: 'elId'
33070 });
33071 </code></pre>
33072  * @singleton
33073  */
33074 Roo.MessageBox = function(){
33075     var dlg, opt, mask, waitTimer;
33076     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33077     var buttons, activeTextEl, bwidth;
33078
33079     // private
33080     var handleButton = function(button){
33081         dlg.hide();
33082         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33083     };
33084
33085     // private
33086     var handleHide = function(){
33087         if(opt && opt.cls){
33088             dlg.el.removeClass(opt.cls);
33089         }
33090         if(waitTimer){
33091             Roo.TaskMgr.stop(waitTimer);
33092             waitTimer = null;
33093         }
33094     };
33095
33096     // private
33097     var updateButtons = function(b){
33098         var width = 0;
33099         if(!b){
33100             buttons["ok"].hide();
33101             buttons["cancel"].hide();
33102             buttons["yes"].hide();
33103             buttons["no"].hide();
33104             dlg.footer.dom.style.display = 'none';
33105             return width;
33106         }
33107         dlg.footer.dom.style.display = '';
33108         for(var k in buttons){
33109             if(typeof buttons[k] != "function"){
33110                 if(b[k]){
33111                     buttons[k].show();
33112                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33113                     width += buttons[k].el.getWidth()+15;
33114                 }else{
33115                     buttons[k].hide();
33116                 }
33117             }
33118         }
33119         return width;
33120     };
33121
33122     // private
33123     var handleEsc = function(d, k, e){
33124         if(opt && opt.closable !== false){
33125             dlg.hide();
33126         }
33127         if(e){
33128             e.stopEvent();
33129         }
33130     };
33131
33132     return {
33133         /**
33134          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33135          * @return {Roo.BasicDialog} The BasicDialog element
33136          */
33137         getDialog : function(){
33138            if(!dlg){
33139                 dlg = new Roo.BasicDialog("x-msg-box", {
33140                     autoCreate : true,
33141                     shadow: true,
33142                     draggable: true,
33143                     resizable:false,
33144                     constraintoviewport:false,
33145                     fixedcenter:true,
33146                     collapsible : false,
33147                     shim:true,
33148                     modal: true,
33149                     width:400, height:100,
33150                     buttonAlign:"center",
33151                     closeClick : function(){
33152                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33153                             handleButton("no");
33154                         }else{
33155                             handleButton("cancel");
33156                         }
33157                     }
33158                 });
33159                 dlg.on("hide", handleHide);
33160                 mask = dlg.mask;
33161                 dlg.addKeyListener(27, handleEsc);
33162                 buttons = {};
33163                 var bt = this.buttonText;
33164                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33165                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33166                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33167                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33168                 bodyEl = dlg.body.createChild({
33169
33170                     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>'
33171                 });
33172                 msgEl = bodyEl.dom.firstChild;
33173                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33174                 textboxEl.enableDisplayMode();
33175                 textboxEl.addKeyListener([10,13], function(){
33176                     if(dlg.isVisible() && opt && opt.buttons){
33177                         if(opt.buttons.ok){
33178                             handleButton("ok");
33179                         }else if(opt.buttons.yes){
33180                             handleButton("yes");
33181                         }
33182                     }
33183                 });
33184                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33185                 textareaEl.enableDisplayMode();
33186                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33187                 progressEl.enableDisplayMode();
33188                 var pf = progressEl.dom.firstChild;
33189                 if (pf) {
33190                     pp = Roo.get(pf.firstChild);
33191                     pp.setHeight(pf.offsetHeight);
33192                 }
33193                 
33194             }
33195             return dlg;
33196         },
33197
33198         /**
33199          * Updates the message box body text
33200          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33201          * the XHTML-compliant non-breaking space character '&amp;#160;')
33202          * @return {Roo.MessageBox} This message box
33203          */
33204         updateText : function(text){
33205             if(!dlg.isVisible() && !opt.width){
33206                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33207             }
33208             msgEl.innerHTML = text || '&#160;';
33209       
33210             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33211             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33212             var w = Math.max(
33213                     Math.min(opt.width || cw , this.maxWidth), 
33214                     Math.max(opt.minWidth || this.minWidth, bwidth)
33215             );
33216             if(opt.prompt){
33217                 activeTextEl.setWidth(w);
33218             }
33219             if(dlg.isVisible()){
33220                 dlg.fixedcenter = false;
33221             }
33222             // to big, make it scroll. = But as usual stupid IE does not support
33223             // !important..
33224             
33225             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33226                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33227                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33228             } else {
33229                 bodyEl.dom.style.height = '';
33230                 bodyEl.dom.style.overflowY = '';
33231             }
33232             if (cw > w) {
33233                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33234             } else {
33235                 bodyEl.dom.style.overflowX = '';
33236             }
33237             
33238             dlg.setContentSize(w, bodyEl.getHeight());
33239             if(dlg.isVisible()){
33240                 dlg.fixedcenter = true;
33241             }
33242             return this;
33243         },
33244
33245         /**
33246          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33247          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33248          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33249          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33250          * @return {Roo.MessageBox} This message box
33251          */
33252         updateProgress : function(value, text){
33253             if(text){
33254                 this.updateText(text);
33255             }
33256             if (pp) { // weird bug on my firefox - for some reason this is not defined
33257                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33258             }
33259             return this;
33260         },        
33261
33262         /**
33263          * Returns true if the message box is currently displayed
33264          * @return {Boolean} True if the message box is visible, else false
33265          */
33266         isVisible : function(){
33267             return dlg && dlg.isVisible();  
33268         },
33269
33270         /**
33271          * Hides the message box if it is displayed
33272          */
33273         hide : function(){
33274             if(this.isVisible()){
33275                 dlg.hide();
33276             }  
33277         },
33278
33279         /**
33280          * Displays a new message box, or reinitializes an existing message box, based on the config options
33281          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33282          * The following config object properties are supported:
33283          * <pre>
33284 Property    Type             Description
33285 ----------  ---------------  ------------------------------------------------------------------------------------
33286 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33287                                    closes (defaults to undefined)
33288 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33289                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33290 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33291                                    progress and wait dialogs will ignore this property and always hide the
33292                                    close button as they can only be closed programmatically.
33293 cls               String           A custom CSS class to apply to the message box element
33294 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33295                                    displayed (defaults to 75)
33296 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33297                                    function will be btn (the name of the button that was clicked, if applicable,
33298                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33299                                    Progress and wait dialogs will ignore this option since they do not respond to
33300                                    user actions and can only be closed programmatically, so any required function
33301                                    should be called by the same code after it closes the dialog.
33302 icon              String           A CSS class that provides a background image to be used as an icon for
33303                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33304 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33305 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33306 modal             Boolean          False to allow user interaction with the page while the message box is
33307                                    displayed (defaults to true)
33308 msg               String           A string that will replace the existing message box body text (defaults
33309                                    to the XHTML-compliant non-breaking space character '&#160;')
33310 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33311 progress          Boolean          True to display a progress bar (defaults to false)
33312 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33313 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33314 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33315 title             String           The title text
33316 value             String           The string value to set into the active textbox element if displayed
33317 wait              Boolean          True to display a progress bar (defaults to false)
33318 width             Number           The width of the dialog in pixels
33319 </pre>
33320          *
33321          * Example usage:
33322          * <pre><code>
33323 Roo.Msg.show({
33324    title: 'Address',
33325    msg: 'Please enter your address:',
33326    width: 300,
33327    buttons: Roo.MessageBox.OKCANCEL,
33328    multiline: true,
33329    fn: saveAddress,
33330    animEl: 'addAddressBtn'
33331 });
33332 </code></pre>
33333          * @param {Object} config Configuration options
33334          * @return {Roo.MessageBox} This message box
33335          */
33336         show : function(options)
33337         {
33338             
33339             // this causes nightmares if you show one dialog after another
33340             // especially on callbacks..
33341              
33342             if(this.isVisible()){
33343                 
33344                 this.hide();
33345                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33346                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33347                 Roo.log("New Dialog Message:" +  options.msg )
33348                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33349                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33350                 
33351             }
33352             var d = this.getDialog();
33353             opt = options;
33354             d.setTitle(opt.title || "&#160;");
33355             d.close.setDisplayed(opt.closable !== false);
33356             activeTextEl = textboxEl;
33357             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33358             if(opt.prompt){
33359                 if(opt.multiline){
33360                     textboxEl.hide();
33361                     textareaEl.show();
33362                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33363                         opt.multiline : this.defaultTextHeight);
33364                     activeTextEl = textareaEl;
33365                 }else{
33366                     textboxEl.show();
33367                     textareaEl.hide();
33368                 }
33369             }else{
33370                 textboxEl.hide();
33371                 textareaEl.hide();
33372             }
33373             progressEl.setDisplayed(opt.progress === true);
33374             this.updateProgress(0);
33375             activeTextEl.dom.value = opt.value || "";
33376             if(opt.prompt){
33377                 dlg.setDefaultButton(activeTextEl);
33378             }else{
33379                 var bs = opt.buttons;
33380                 var db = null;
33381                 if(bs && bs.ok){
33382                     db = buttons["ok"];
33383                 }else if(bs && bs.yes){
33384                     db = buttons["yes"];
33385                 }
33386                 dlg.setDefaultButton(db);
33387             }
33388             bwidth = updateButtons(opt.buttons);
33389             this.updateText(opt.msg);
33390             if(opt.cls){
33391                 d.el.addClass(opt.cls);
33392             }
33393             d.proxyDrag = opt.proxyDrag === true;
33394             d.modal = opt.modal !== false;
33395             d.mask = opt.modal !== false ? mask : false;
33396             if(!d.isVisible()){
33397                 // force it to the end of the z-index stack so it gets a cursor in FF
33398                 document.body.appendChild(dlg.el.dom);
33399                 d.animateTarget = null;
33400                 d.show(options.animEl);
33401             }
33402             return this;
33403         },
33404
33405         /**
33406          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33407          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33408          * and closing the message box when the process is complete.
33409          * @param {String} title The title bar text
33410          * @param {String} msg The message box body text
33411          * @return {Roo.MessageBox} This message box
33412          */
33413         progress : function(title, msg){
33414             this.show({
33415                 title : title,
33416                 msg : msg,
33417                 buttons: false,
33418                 progress:true,
33419                 closable:false,
33420                 minWidth: this.minProgressWidth,
33421                 modal : true
33422             });
33423             return this;
33424         },
33425
33426         /**
33427          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33428          * If a callback function is passed it will be called after the user clicks the button, and the
33429          * id of the button that was clicked will be passed as the only parameter to the callback
33430          * (could also be the top-right close button).
33431          * @param {String} title The title bar text
33432          * @param {String} msg The message box body text
33433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33434          * @param {Object} scope (optional) The scope of the callback function
33435          * @return {Roo.MessageBox} This message box
33436          */
33437         alert : function(title, msg, fn, scope){
33438             this.show({
33439                 title : title,
33440                 msg : msg,
33441                 buttons: this.OK,
33442                 fn: fn,
33443                 scope : scope,
33444                 modal : true
33445             });
33446             return this;
33447         },
33448
33449         /**
33450          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33451          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33452          * You are responsible for closing the message box when the process is complete.
33453          * @param {String} msg The message box body text
33454          * @param {String} title (optional) The title bar text
33455          * @return {Roo.MessageBox} This message box
33456          */
33457         wait : function(msg, title){
33458             this.show({
33459                 title : title,
33460                 msg : msg,
33461                 buttons: false,
33462                 closable:false,
33463                 progress:true,
33464                 modal:true,
33465                 width:300,
33466                 wait:true
33467             });
33468             waitTimer = Roo.TaskMgr.start({
33469                 run: function(i){
33470                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33471                 },
33472                 interval: 1000
33473             });
33474             return this;
33475         },
33476
33477         /**
33478          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33479          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33480          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33481          * @param {String} title The title bar text
33482          * @param {String} msg The message box body text
33483          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33484          * @param {Object} scope (optional) The scope of the callback function
33485          * @return {Roo.MessageBox} This message box
33486          */
33487         confirm : function(title, msg, fn, scope){
33488             this.show({
33489                 title : title,
33490                 msg : msg,
33491                 buttons: this.YESNO,
33492                 fn: fn,
33493                 scope : scope,
33494                 modal : true
33495             });
33496             return this;
33497         },
33498
33499         /**
33500          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33501          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33502          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33503          * (could also be the top-right close button) and the text that was entered will be passed as the two
33504          * parameters to the callback.
33505          * @param {String} title The title bar text
33506          * @param {String} msg The message box body text
33507          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33508          * @param {Object} scope (optional) The scope of the callback function
33509          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33510          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33511          * @return {Roo.MessageBox} This message box
33512          */
33513         prompt : function(title, msg, fn, scope, multiline){
33514             this.show({
33515                 title : title,
33516                 msg : msg,
33517                 buttons: this.OKCANCEL,
33518                 fn: fn,
33519                 minWidth:250,
33520                 scope : scope,
33521                 prompt:true,
33522                 multiline: multiline,
33523                 modal : true
33524             });
33525             return this;
33526         },
33527
33528         /**
33529          * Button config that displays a single OK button
33530          * @type Object
33531          */
33532         OK : {ok:true},
33533         /**
33534          * Button config that displays Yes and No buttons
33535          * @type Object
33536          */
33537         YESNO : {yes:true, no:true},
33538         /**
33539          * Button config that displays OK and Cancel buttons
33540          * @type Object
33541          */
33542         OKCANCEL : {ok:true, cancel:true},
33543         /**
33544          * Button config that displays Yes, No and Cancel buttons
33545          * @type Object
33546          */
33547         YESNOCANCEL : {yes:true, no:true, cancel:true},
33548
33549         /**
33550          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33551          * @type Number
33552          */
33553         defaultTextHeight : 75,
33554         /**
33555          * The maximum width in pixels of the message box (defaults to 600)
33556          * @type Number
33557          */
33558         maxWidth : 600,
33559         /**
33560          * The minimum width in pixels of the message box (defaults to 100)
33561          * @type Number
33562          */
33563         minWidth : 100,
33564         /**
33565          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33566          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33567          * @type Number
33568          */
33569         minProgressWidth : 250,
33570         /**
33571          * An object containing the default button text strings that can be overriden for localized language support.
33572          * Supported properties are: ok, cancel, yes and no.
33573          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33574          * @type Object
33575          */
33576         buttonText : {
33577             ok : "OK",
33578             cancel : "Cancel",
33579             yes : "Yes",
33580             no : "No"
33581         }
33582     };
33583 }();
33584
33585 /**
33586  * Shorthand for {@link Roo.MessageBox}
33587  */
33588 Roo.Msg = Roo.MessageBox;/*
33589  * Based on:
33590  * Ext JS Library 1.1.1
33591  * Copyright(c) 2006-2007, Ext JS, LLC.
33592  *
33593  * Originally Released Under LGPL - original licence link has changed is not relivant.
33594  *
33595  * Fork - LGPL
33596  * <script type="text/javascript">
33597  */
33598 /**
33599  * @class Roo.QuickTips
33600  * Provides attractive and customizable tooltips for any element.
33601  * @singleton
33602  */
33603 Roo.QuickTips = function(){
33604     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33605     var ce, bd, xy, dd;
33606     var visible = false, disabled = true, inited = false;
33607     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33608     
33609     var onOver = function(e){
33610         if(disabled){
33611             return;
33612         }
33613         var t = e.getTarget();
33614         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33615             return;
33616         }
33617         if(ce && t == ce.el){
33618             clearTimeout(hideProc);
33619             return;
33620         }
33621         if(t && tagEls[t.id]){
33622             tagEls[t.id].el = t;
33623             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33624             return;
33625         }
33626         var ttp, et = Roo.fly(t);
33627         var ns = cfg.namespace;
33628         if(tm.interceptTitles && t.title){
33629             ttp = t.title;
33630             t.qtip = ttp;
33631             t.removeAttribute("title");
33632             e.preventDefault();
33633         }else{
33634             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33635         }
33636         if(ttp){
33637             showProc = show.defer(tm.showDelay, tm, [{
33638                 el: t, 
33639                 text: ttp.replace(/\\n/g,'<br/>'),
33640                 width: et.getAttributeNS(ns, cfg.width),
33641                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33642                 title: et.getAttributeNS(ns, cfg.title),
33643                     cls: et.getAttributeNS(ns, cfg.cls)
33644             }]);
33645         }
33646     };
33647     
33648     var onOut = function(e){
33649         clearTimeout(showProc);
33650         var t = e.getTarget();
33651         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33652             hideProc = setTimeout(hide, tm.hideDelay);
33653         }
33654     };
33655     
33656     var onMove = function(e){
33657         if(disabled){
33658             return;
33659         }
33660         xy = e.getXY();
33661         xy[1] += 18;
33662         if(tm.trackMouse && ce){
33663             el.setXY(xy);
33664         }
33665     };
33666     
33667     var onDown = function(e){
33668         clearTimeout(showProc);
33669         clearTimeout(hideProc);
33670         if(!e.within(el)){
33671             if(tm.hideOnClick){
33672                 hide();
33673                 tm.disable();
33674                 tm.enable.defer(100, tm);
33675             }
33676         }
33677     };
33678     
33679     var getPad = function(){
33680         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33681     };
33682
33683     var show = function(o){
33684         if(disabled){
33685             return;
33686         }
33687         clearTimeout(dismissProc);
33688         ce = o;
33689         if(removeCls){ // in case manually hidden
33690             el.removeClass(removeCls);
33691             removeCls = null;
33692         }
33693         if(ce.cls){
33694             el.addClass(ce.cls);
33695             removeCls = ce.cls;
33696         }
33697         if(ce.title){
33698             tipTitle.update(ce.title);
33699             tipTitle.show();
33700         }else{
33701             tipTitle.update('');
33702             tipTitle.hide();
33703         }
33704         el.dom.style.width  = tm.maxWidth+'px';
33705         //tipBody.dom.style.width = '';
33706         tipBodyText.update(o.text);
33707         var p = getPad(), w = ce.width;
33708         if(!w){
33709             var td = tipBodyText.dom;
33710             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33711             if(aw > tm.maxWidth){
33712                 w = tm.maxWidth;
33713             }else if(aw < tm.minWidth){
33714                 w = tm.minWidth;
33715             }else{
33716                 w = aw;
33717             }
33718         }
33719         //tipBody.setWidth(w);
33720         el.setWidth(parseInt(w, 10) + p);
33721         if(ce.autoHide === false){
33722             close.setDisplayed(true);
33723             if(dd){
33724                 dd.unlock();
33725             }
33726         }else{
33727             close.setDisplayed(false);
33728             if(dd){
33729                 dd.lock();
33730             }
33731         }
33732         if(xy){
33733             el.avoidY = xy[1]-18;
33734             el.setXY(xy);
33735         }
33736         if(tm.animate){
33737             el.setOpacity(.1);
33738             el.setStyle("visibility", "visible");
33739             el.fadeIn({callback: afterShow});
33740         }else{
33741             afterShow();
33742         }
33743     };
33744     
33745     var afterShow = function(){
33746         if(ce){
33747             el.show();
33748             esc.enable();
33749             if(tm.autoDismiss && ce.autoHide !== false){
33750                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33751             }
33752         }
33753     };
33754     
33755     var hide = function(noanim){
33756         clearTimeout(dismissProc);
33757         clearTimeout(hideProc);
33758         ce = null;
33759         if(el.isVisible()){
33760             esc.disable();
33761             if(noanim !== true && tm.animate){
33762                 el.fadeOut({callback: afterHide});
33763             }else{
33764                 afterHide();
33765             } 
33766         }
33767     };
33768     
33769     var afterHide = function(){
33770         el.hide();
33771         if(removeCls){
33772             el.removeClass(removeCls);
33773             removeCls = null;
33774         }
33775     };
33776     
33777     return {
33778         /**
33779         * @cfg {Number} minWidth
33780         * The minimum width of the quick tip (defaults to 40)
33781         */
33782        minWidth : 40,
33783         /**
33784         * @cfg {Number} maxWidth
33785         * The maximum width of the quick tip (defaults to 300)
33786         */
33787        maxWidth : 300,
33788         /**
33789         * @cfg {Boolean} interceptTitles
33790         * True to automatically use the element's DOM title value if available (defaults to false)
33791         */
33792        interceptTitles : false,
33793         /**
33794         * @cfg {Boolean} trackMouse
33795         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33796         */
33797        trackMouse : false,
33798         /**
33799         * @cfg {Boolean} hideOnClick
33800         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33801         */
33802        hideOnClick : true,
33803         /**
33804         * @cfg {Number} showDelay
33805         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33806         */
33807        showDelay : 500,
33808         /**
33809         * @cfg {Number} hideDelay
33810         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33811         */
33812        hideDelay : 200,
33813         /**
33814         * @cfg {Boolean} autoHide
33815         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33816         * Used in conjunction with hideDelay.
33817         */
33818        autoHide : true,
33819         /**
33820         * @cfg {Boolean}
33821         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33822         * (defaults to true).  Used in conjunction with autoDismissDelay.
33823         */
33824        autoDismiss : true,
33825         /**
33826         * @cfg {Number}
33827         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33828         */
33829        autoDismissDelay : 5000,
33830        /**
33831         * @cfg {Boolean} animate
33832         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33833         */
33834        animate : false,
33835
33836        /**
33837         * @cfg {String} title
33838         * Title text to display (defaults to '').  This can be any valid HTML markup.
33839         */
33840         title: '',
33841        /**
33842         * @cfg {String} text
33843         * Body text to display (defaults to '').  This can be any valid HTML markup.
33844         */
33845         text : '',
33846        /**
33847         * @cfg {String} cls
33848         * A CSS class to apply to the base quick tip element (defaults to '').
33849         */
33850         cls : '',
33851        /**
33852         * @cfg {Number} width
33853         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33854         * minWidth or maxWidth.
33855         */
33856         width : null,
33857
33858     /**
33859      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33860      * or display QuickTips in a page.
33861      */
33862        init : function(){
33863           tm = Roo.QuickTips;
33864           cfg = tm.tagConfig;
33865           if(!inited){
33866               if(!Roo.isReady){ // allow calling of init() before onReady
33867                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33868                   return;
33869               }
33870               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33871               el.fxDefaults = {stopFx: true};
33872               // maximum custom styling
33873               //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>');
33874               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>');              
33875               tipTitle = el.child('h3');
33876               tipTitle.enableDisplayMode("block");
33877               tipBody = el.child('div.x-tip-bd');
33878               tipBodyText = el.child('div.x-tip-bd-inner');
33879               //bdLeft = el.child('div.x-tip-bd-left');
33880               //bdRight = el.child('div.x-tip-bd-right');
33881               close = el.child('div.x-tip-close');
33882               close.enableDisplayMode("block");
33883               close.on("click", hide);
33884               var d = Roo.get(document);
33885               d.on("mousedown", onDown);
33886               d.on("mouseover", onOver);
33887               d.on("mouseout", onOut);
33888               d.on("mousemove", onMove);
33889               esc = d.addKeyListener(27, hide);
33890               esc.disable();
33891               if(Roo.dd.DD){
33892                   dd = el.initDD("default", null, {
33893                       onDrag : function(){
33894                           el.sync();  
33895                       }
33896                   });
33897                   dd.setHandleElId(tipTitle.id);
33898                   dd.lock();
33899               }
33900               inited = true;
33901           }
33902           this.enable(); 
33903        },
33904
33905     /**
33906      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33907      * are supported:
33908      * <pre>
33909 Property    Type                   Description
33910 ----------  ---------------------  ------------------------------------------------------------------------
33911 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33912      * </ul>
33913      * @param {Object} config The config object
33914      */
33915        register : function(config){
33916            var cs = config instanceof Array ? config : arguments;
33917            for(var i = 0, len = cs.length; i < len; i++) {
33918                var c = cs[i];
33919                var target = c.target;
33920                if(target){
33921                    if(target instanceof Array){
33922                        for(var j = 0, jlen = target.length; j < jlen; j++){
33923                            tagEls[target[j]] = c;
33924                        }
33925                    }else{
33926                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33927                    }
33928                }
33929            }
33930        },
33931
33932     /**
33933      * Removes this quick tip from its element and destroys it.
33934      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33935      */
33936        unregister : function(el){
33937            delete tagEls[Roo.id(el)];
33938        },
33939
33940     /**
33941      * Enable this quick tip.
33942      */
33943        enable : function(){
33944            if(inited && disabled){
33945                locks.pop();
33946                if(locks.length < 1){
33947                    disabled = false;
33948                }
33949            }
33950        },
33951
33952     /**
33953      * Disable this quick tip.
33954      */
33955        disable : function(){
33956           disabled = true;
33957           clearTimeout(showProc);
33958           clearTimeout(hideProc);
33959           clearTimeout(dismissProc);
33960           if(ce){
33961               hide(true);
33962           }
33963           locks.push(1);
33964        },
33965
33966     /**
33967      * Returns true if the quick tip is enabled, else false.
33968      */
33969        isEnabled : function(){
33970             return !disabled;
33971        },
33972
33973         // private
33974        tagConfig : {
33975            namespace : "roo", // was ext?? this may break..
33976            alt_namespace : "ext",
33977            attribute : "qtip",
33978            width : "width",
33979            target : "target",
33980            title : "qtitle",
33981            hide : "hide",
33982            cls : "qclass"
33983        }
33984    };
33985 }();
33986
33987 // backwards compat
33988 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33989  * Based on:
33990  * Ext JS Library 1.1.1
33991  * Copyright(c) 2006-2007, Ext JS, LLC.
33992  *
33993  * Originally Released Under LGPL - original licence link has changed is not relivant.
33994  *
33995  * Fork - LGPL
33996  * <script type="text/javascript">
33997  */
33998  
33999
34000 /**
34001  * @class Roo.tree.TreePanel
34002  * @extends Roo.data.Tree
34003
34004  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34005  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34006  * @cfg {Boolean} enableDD true to enable drag and drop
34007  * @cfg {Boolean} enableDrag true to enable just drag
34008  * @cfg {Boolean} enableDrop true to enable just drop
34009  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34010  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34011  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34012  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34013  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34014  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34015  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34016  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34017  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34018  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34019  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34020  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34021  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34022  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34023  * @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>
34024  * @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>
34025  * 
34026  * @constructor
34027  * @param {String/HTMLElement/Element} el The container element
34028  * @param {Object} config
34029  */
34030 Roo.tree.TreePanel = function(el, config){
34031     var root = false;
34032     var loader = false;
34033     if (config.root) {
34034         root = config.root;
34035         delete config.root;
34036     }
34037     if (config.loader) {
34038         loader = config.loader;
34039         delete config.loader;
34040     }
34041     
34042     Roo.apply(this, config);
34043     Roo.tree.TreePanel.superclass.constructor.call(this);
34044     this.el = Roo.get(el);
34045     this.el.addClass('x-tree');
34046     //console.log(root);
34047     if (root) {
34048         this.setRootNode( Roo.factory(root, Roo.tree));
34049     }
34050     if (loader) {
34051         this.loader = Roo.factory(loader, Roo.tree);
34052     }
34053    /**
34054     * Read-only. The id of the container element becomes this TreePanel's id.
34055     */
34056     this.id = this.el.id;
34057     this.addEvents({
34058         /**
34059         * @event beforeload
34060         * Fires before a node is loaded, return false to cancel
34061         * @param {Node} node The node being loaded
34062         */
34063         "beforeload" : true,
34064         /**
34065         * @event load
34066         * Fires when a node is loaded
34067         * @param {Node} node The node that was loaded
34068         */
34069         "load" : true,
34070         /**
34071         * @event textchange
34072         * Fires when the text for a node is changed
34073         * @param {Node} node The node
34074         * @param {String} text The new text
34075         * @param {String} oldText The old text
34076         */
34077         "textchange" : true,
34078         /**
34079         * @event beforeexpand
34080         * Fires before a node is expanded, return false to cancel.
34081         * @param {Node} node The node
34082         * @param {Boolean} deep
34083         * @param {Boolean} anim
34084         */
34085         "beforeexpand" : true,
34086         /**
34087         * @event beforecollapse
34088         * Fires before a node is collapsed, return false to cancel.
34089         * @param {Node} node The node
34090         * @param {Boolean} deep
34091         * @param {Boolean} anim
34092         */
34093         "beforecollapse" : true,
34094         /**
34095         * @event expand
34096         * Fires when a node is expanded
34097         * @param {Node} node The node
34098         */
34099         "expand" : true,
34100         /**
34101         * @event disabledchange
34102         * Fires when the disabled status of a node changes
34103         * @param {Node} node The node
34104         * @param {Boolean} disabled
34105         */
34106         "disabledchange" : true,
34107         /**
34108         * @event collapse
34109         * Fires when a node is collapsed
34110         * @param {Node} node The node
34111         */
34112         "collapse" : true,
34113         /**
34114         * @event beforeclick
34115         * Fires before click processing on a node. Return false to cancel the default action.
34116         * @param {Node} node The node
34117         * @param {Roo.EventObject} e The event object
34118         */
34119         "beforeclick":true,
34120         /**
34121         * @event checkchange
34122         * Fires when a node with a checkbox's checked property changes
34123         * @param {Node} this This node
34124         * @param {Boolean} checked
34125         */
34126         "checkchange":true,
34127         /**
34128         * @event click
34129         * Fires when a node is clicked
34130         * @param {Node} node The node
34131         * @param {Roo.EventObject} e The event object
34132         */
34133         "click":true,
34134         /**
34135         * @event dblclick
34136         * Fires when a node is double clicked
34137         * @param {Node} node The node
34138         * @param {Roo.EventObject} e The event object
34139         */
34140         "dblclick":true,
34141         /**
34142         * @event contextmenu
34143         * Fires when a node is right clicked
34144         * @param {Node} node The node
34145         * @param {Roo.EventObject} e The event object
34146         */
34147         "contextmenu":true,
34148         /**
34149         * @event beforechildrenrendered
34150         * Fires right before the child nodes for a node are rendered
34151         * @param {Node} node The node
34152         */
34153         "beforechildrenrendered":true,
34154         /**
34155         * @event startdrag
34156         * Fires when a node starts being dragged
34157         * @param {Roo.tree.TreePanel} this
34158         * @param {Roo.tree.TreeNode} node
34159         * @param {event} e The raw browser event
34160         */ 
34161        "startdrag" : true,
34162        /**
34163         * @event enddrag
34164         * Fires when a drag operation is complete
34165         * @param {Roo.tree.TreePanel} this
34166         * @param {Roo.tree.TreeNode} node
34167         * @param {event} e The raw browser event
34168         */
34169        "enddrag" : true,
34170        /**
34171         * @event dragdrop
34172         * Fires when a dragged node is dropped on a valid DD target
34173         * @param {Roo.tree.TreePanel} this
34174         * @param {Roo.tree.TreeNode} node
34175         * @param {DD} dd The dd it was dropped on
34176         * @param {event} e The raw browser event
34177         */
34178        "dragdrop" : true,
34179        /**
34180         * @event beforenodedrop
34181         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34182         * passed to handlers has the following properties:<br />
34183         * <ul style="padding:5px;padding-left:16px;">
34184         * <li>tree - The TreePanel</li>
34185         * <li>target - The node being targeted for the drop</li>
34186         * <li>data - The drag data from the drag source</li>
34187         * <li>point - The point of the drop - append, above or below</li>
34188         * <li>source - The drag source</li>
34189         * <li>rawEvent - Raw mouse event</li>
34190         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34191         * to be inserted by setting them on this object.</li>
34192         * <li>cancel - Set this to true to cancel the drop.</li>
34193         * </ul>
34194         * @param {Object} dropEvent
34195         */
34196        "beforenodedrop" : true,
34197        /**
34198         * @event nodedrop
34199         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34200         * passed to handlers has the following properties:<br />
34201         * <ul style="padding:5px;padding-left:16px;">
34202         * <li>tree - The TreePanel</li>
34203         * <li>target - The node being targeted for the drop</li>
34204         * <li>data - The drag data from the drag source</li>
34205         * <li>point - The point of the drop - append, above or below</li>
34206         * <li>source - The drag source</li>
34207         * <li>rawEvent - Raw mouse event</li>
34208         * <li>dropNode - Dropped node(s).</li>
34209         * </ul>
34210         * @param {Object} dropEvent
34211         */
34212        "nodedrop" : true,
34213         /**
34214         * @event nodedragover
34215         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34216         * passed to handlers has the following properties:<br />
34217         * <ul style="padding:5px;padding-left:16px;">
34218         * <li>tree - The TreePanel</li>
34219         * <li>target - The node being targeted for the drop</li>
34220         * <li>data - The drag data from the drag source</li>
34221         * <li>point - The point of the drop - append, above or below</li>
34222         * <li>source - The drag source</li>
34223         * <li>rawEvent - Raw mouse event</li>
34224         * <li>dropNode - Drop node(s) provided by the source.</li>
34225         * <li>cancel - Set this to true to signal drop not allowed.</li>
34226         * </ul>
34227         * @param {Object} dragOverEvent
34228         */
34229        "nodedragover" : true
34230         
34231     });
34232     if(this.singleExpand){
34233        this.on("beforeexpand", this.restrictExpand, this);
34234     }
34235     if (this.editor) {
34236         this.editor.tree = this;
34237         this.editor = Roo.factory(this.editor, Roo.tree);
34238     }
34239     
34240     if (this.selModel) {
34241         this.selModel = Roo.factory(this.selModel, Roo.tree);
34242     }
34243    
34244 };
34245 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34246     rootVisible : true,
34247     animate: Roo.enableFx,
34248     lines : true,
34249     enableDD : false,
34250     hlDrop : Roo.enableFx,
34251   
34252     renderer: false,
34253     
34254     rendererTip: false,
34255     // private
34256     restrictExpand : function(node){
34257         var p = node.parentNode;
34258         if(p){
34259             if(p.expandedChild && p.expandedChild.parentNode == p){
34260                 p.expandedChild.collapse();
34261             }
34262             p.expandedChild = node;
34263         }
34264     },
34265
34266     // private override
34267     setRootNode : function(node){
34268         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34269         if(!this.rootVisible){
34270             node.ui = new Roo.tree.RootTreeNodeUI(node);
34271         }
34272         return node;
34273     },
34274
34275     /**
34276      * Returns the container element for this TreePanel
34277      */
34278     getEl : function(){
34279         return this.el;
34280     },
34281
34282     /**
34283      * Returns the default TreeLoader for this TreePanel
34284      */
34285     getLoader : function(){
34286         return this.loader;
34287     },
34288
34289     /**
34290      * Expand all nodes
34291      */
34292     expandAll : function(){
34293         this.root.expand(true);
34294     },
34295
34296     /**
34297      * Collapse all nodes
34298      */
34299     collapseAll : function(){
34300         this.root.collapse(true);
34301     },
34302
34303     /**
34304      * Returns the selection model used by this TreePanel
34305      */
34306     getSelectionModel : function(){
34307         if(!this.selModel){
34308             this.selModel = new Roo.tree.DefaultSelectionModel();
34309         }
34310         return this.selModel;
34311     },
34312
34313     /**
34314      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34315      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34316      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34317      * @return {Array}
34318      */
34319     getChecked : function(a, startNode){
34320         startNode = startNode || this.root;
34321         var r = [];
34322         var f = function(){
34323             if(this.attributes.checked){
34324                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34325             }
34326         }
34327         startNode.cascade(f);
34328         return r;
34329     },
34330
34331     /**
34332      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34333      * @param {String} path
34334      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34335      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34336      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34337      */
34338     expandPath : function(path, attr, callback){
34339         attr = attr || "id";
34340         var keys = path.split(this.pathSeparator);
34341         var curNode = this.root;
34342         if(curNode.attributes[attr] != keys[1]){ // invalid root
34343             if(callback){
34344                 callback(false, null);
34345             }
34346             return;
34347         }
34348         var index = 1;
34349         var f = function(){
34350             if(++index == keys.length){
34351                 if(callback){
34352                     callback(true, curNode);
34353                 }
34354                 return;
34355             }
34356             var c = curNode.findChild(attr, keys[index]);
34357             if(!c){
34358                 if(callback){
34359                     callback(false, curNode);
34360                 }
34361                 return;
34362             }
34363             curNode = c;
34364             c.expand(false, false, f);
34365         };
34366         curNode.expand(false, false, f);
34367     },
34368
34369     /**
34370      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34371      * @param {String} path
34372      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34373      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34374      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34375      */
34376     selectPath : function(path, attr, callback){
34377         attr = attr || "id";
34378         var keys = path.split(this.pathSeparator);
34379         var v = keys.pop();
34380         if(keys.length > 0){
34381             var f = function(success, node){
34382                 if(success && node){
34383                     var n = node.findChild(attr, v);
34384                     if(n){
34385                         n.select();
34386                         if(callback){
34387                             callback(true, n);
34388                         }
34389                     }else if(callback){
34390                         callback(false, n);
34391                     }
34392                 }else{
34393                     if(callback){
34394                         callback(false, n);
34395                     }
34396                 }
34397             };
34398             this.expandPath(keys.join(this.pathSeparator), attr, f);
34399         }else{
34400             this.root.select();
34401             if(callback){
34402                 callback(true, this.root);
34403             }
34404         }
34405     },
34406
34407     getTreeEl : function(){
34408         return this.el;
34409     },
34410
34411     /**
34412      * Trigger rendering of this TreePanel
34413      */
34414     render : function(){
34415         if (this.innerCt) {
34416             return this; // stop it rendering more than once!!
34417         }
34418         
34419         this.innerCt = this.el.createChild({tag:"ul",
34420                cls:"x-tree-root-ct " +
34421                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34422
34423         if(this.containerScroll){
34424             Roo.dd.ScrollManager.register(this.el);
34425         }
34426         if((this.enableDD || this.enableDrop) && !this.dropZone){
34427            /**
34428             * The dropZone used by this tree if drop is enabled
34429             * @type Roo.tree.TreeDropZone
34430             */
34431              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34432                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34433            });
34434         }
34435         if((this.enableDD || this.enableDrag) && !this.dragZone){
34436            /**
34437             * The dragZone used by this tree if drag is enabled
34438             * @type Roo.tree.TreeDragZone
34439             */
34440             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34441                ddGroup: this.ddGroup || "TreeDD",
34442                scroll: this.ddScroll
34443            });
34444         }
34445         this.getSelectionModel().init(this);
34446         if (!this.root) {
34447             Roo.log("ROOT not set in tree");
34448             return this;
34449         }
34450         this.root.render();
34451         if(!this.rootVisible){
34452             this.root.renderChildren();
34453         }
34454         return this;
34455     }
34456 });/*
34457  * Based on:
34458  * Ext JS Library 1.1.1
34459  * Copyright(c) 2006-2007, Ext JS, LLC.
34460  *
34461  * Originally Released Under LGPL - original licence link has changed is not relivant.
34462  *
34463  * Fork - LGPL
34464  * <script type="text/javascript">
34465  */
34466  
34467
34468 /**
34469  * @class Roo.tree.DefaultSelectionModel
34470  * @extends Roo.util.Observable
34471  * The default single selection for a TreePanel.
34472  * @param {Object} cfg Configuration
34473  */
34474 Roo.tree.DefaultSelectionModel = function(cfg){
34475    this.selNode = null;
34476    
34477    
34478    
34479    this.addEvents({
34480        /**
34481         * @event selectionchange
34482         * Fires when the selected node changes
34483         * @param {DefaultSelectionModel} this
34484         * @param {TreeNode} node the new selection
34485         */
34486        "selectionchange" : true,
34487
34488        /**
34489         * @event beforeselect
34490         * Fires before the selected node changes, return false to cancel the change
34491         * @param {DefaultSelectionModel} this
34492         * @param {TreeNode} node the new selection
34493         * @param {TreeNode} node the old selection
34494         */
34495        "beforeselect" : true
34496    });
34497    
34498     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34499 };
34500
34501 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34502     init : function(tree){
34503         this.tree = tree;
34504         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34505         tree.on("click", this.onNodeClick, this);
34506     },
34507     
34508     onNodeClick : function(node, e){
34509         if (e.ctrlKey && this.selNode == node)  {
34510             this.unselect(node);
34511             return;
34512         }
34513         this.select(node);
34514     },
34515     
34516     /**
34517      * Select a node.
34518      * @param {TreeNode} node The node to select
34519      * @return {TreeNode} The selected node
34520      */
34521     select : function(node){
34522         var last = this.selNode;
34523         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34524             if(last){
34525                 last.ui.onSelectedChange(false);
34526             }
34527             this.selNode = node;
34528             node.ui.onSelectedChange(true);
34529             this.fireEvent("selectionchange", this, node, last);
34530         }
34531         return node;
34532     },
34533     
34534     /**
34535      * Deselect a node.
34536      * @param {TreeNode} node The node to unselect
34537      */
34538     unselect : function(node){
34539         if(this.selNode == node){
34540             this.clearSelections();
34541         }    
34542     },
34543     
34544     /**
34545      * Clear all selections
34546      */
34547     clearSelections : function(){
34548         var n = this.selNode;
34549         if(n){
34550             n.ui.onSelectedChange(false);
34551             this.selNode = null;
34552             this.fireEvent("selectionchange", this, null);
34553         }
34554         return n;
34555     },
34556     
34557     /**
34558      * Get the selected node
34559      * @return {TreeNode} The selected node
34560      */
34561     getSelectedNode : function(){
34562         return this.selNode;    
34563     },
34564     
34565     /**
34566      * Returns true if the node is selected
34567      * @param {TreeNode} node The node to check
34568      * @return {Boolean}
34569      */
34570     isSelected : function(node){
34571         return this.selNode == node;  
34572     },
34573
34574     /**
34575      * Selects the node above the selected node in the tree, intelligently walking the nodes
34576      * @return TreeNode The new selection
34577      */
34578     selectPrevious : function(){
34579         var s = this.selNode || this.lastSelNode;
34580         if(!s){
34581             return null;
34582         }
34583         var ps = s.previousSibling;
34584         if(ps){
34585             if(!ps.isExpanded() || ps.childNodes.length < 1){
34586                 return this.select(ps);
34587             } else{
34588                 var lc = ps.lastChild;
34589                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34590                     lc = lc.lastChild;
34591                 }
34592                 return this.select(lc);
34593             }
34594         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34595             return this.select(s.parentNode);
34596         }
34597         return null;
34598     },
34599
34600     /**
34601      * Selects the node above the selected node in the tree, intelligently walking the nodes
34602      * @return TreeNode The new selection
34603      */
34604     selectNext : function(){
34605         var s = this.selNode || this.lastSelNode;
34606         if(!s){
34607             return null;
34608         }
34609         if(s.firstChild && s.isExpanded()){
34610              return this.select(s.firstChild);
34611          }else if(s.nextSibling){
34612              return this.select(s.nextSibling);
34613          }else if(s.parentNode){
34614             var newS = null;
34615             s.parentNode.bubble(function(){
34616                 if(this.nextSibling){
34617                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34618                     return false;
34619                 }
34620             });
34621             return newS;
34622          }
34623         return null;
34624     },
34625
34626     onKeyDown : function(e){
34627         var s = this.selNode || this.lastSelNode;
34628         // undesirable, but required
34629         var sm = this;
34630         if(!s){
34631             return;
34632         }
34633         var k = e.getKey();
34634         switch(k){
34635              case e.DOWN:
34636                  e.stopEvent();
34637                  this.selectNext();
34638              break;
34639              case e.UP:
34640                  e.stopEvent();
34641                  this.selectPrevious();
34642              break;
34643              case e.RIGHT:
34644                  e.preventDefault();
34645                  if(s.hasChildNodes()){
34646                      if(!s.isExpanded()){
34647                          s.expand();
34648                      }else if(s.firstChild){
34649                          this.select(s.firstChild, e);
34650                      }
34651                  }
34652              break;
34653              case e.LEFT:
34654                  e.preventDefault();
34655                  if(s.hasChildNodes() && s.isExpanded()){
34656                      s.collapse();
34657                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34658                      this.select(s.parentNode, e);
34659                  }
34660              break;
34661         };
34662     }
34663 });
34664
34665 /**
34666  * @class Roo.tree.MultiSelectionModel
34667  * @extends Roo.util.Observable
34668  * Multi selection for a TreePanel.
34669  * @param {Object} cfg Configuration
34670  */
34671 Roo.tree.MultiSelectionModel = function(){
34672    this.selNodes = [];
34673    this.selMap = {};
34674    this.addEvents({
34675        /**
34676         * @event selectionchange
34677         * Fires when the selected nodes change
34678         * @param {MultiSelectionModel} this
34679         * @param {Array} nodes Array of the selected nodes
34680         */
34681        "selectionchange" : true
34682    });
34683    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34684    
34685 };
34686
34687 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34688     init : function(tree){
34689         this.tree = tree;
34690         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34691         tree.on("click", this.onNodeClick, this);
34692     },
34693     
34694     onNodeClick : function(node, e){
34695         this.select(node, e, e.ctrlKey);
34696     },
34697     
34698     /**
34699      * Select a node.
34700      * @param {TreeNode} node The node to select
34701      * @param {EventObject} e (optional) An event associated with the selection
34702      * @param {Boolean} keepExisting True to retain existing selections
34703      * @return {TreeNode} The selected node
34704      */
34705     select : function(node, e, keepExisting){
34706         if(keepExisting !== true){
34707             this.clearSelections(true);
34708         }
34709         if(this.isSelected(node)){
34710             this.lastSelNode = node;
34711             return node;
34712         }
34713         this.selNodes.push(node);
34714         this.selMap[node.id] = node;
34715         this.lastSelNode = node;
34716         node.ui.onSelectedChange(true);
34717         this.fireEvent("selectionchange", this, this.selNodes);
34718         return node;
34719     },
34720     
34721     /**
34722      * Deselect a node.
34723      * @param {TreeNode} node The node to unselect
34724      */
34725     unselect : function(node){
34726         if(this.selMap[node.id]){
34727             node.ui.onSelectedChange(false);
34728             var sn = this.selNodes;
34729             var index = -1;
34730             if(sn.indexOf){
34731                 index = sn.indexOf(node);
34732             }else{
34733                 for(var i = 0, len = sn.length; i < len; i++){
34734                     if(sn[i] == node){
34735                         index = i;
34736                         break;
34737                     }
34738                 }
34739             }
34740             if(index != -1){
34741                 this.selNodes.splice(index, 1);
34742             }
34743             delete this.selMap[node.id];
34744             this.fireEvent("selectionchange", this, this.selNodes);
34745         }
34746     },
34747     
34748     /**
34749      * Clear all selections
34750      */
34751     clearSelections : function(suppressEvent){
34752         var sn = this.selNodes;
34753         if(sn.length > 0){
34754             for(var i = 0, len = sn.length; i < len; i++){
34755                 sn[i].ui.onSelectedChange(false);
34756             }
34757             this.selNodes = [];
34758             this.selMap = {};
34759             if(suppressEvent !== true){
34760                 this.fireEvent("selectionchange", this, this.selNodes);
34761             }
34762         }
34763     },
34764     
34765     /**
34766      * Returns true if the node is selected
34767      * @param {TreeNode} node The node to check
34768      * @return {Boolean}
34769      */
34770     isSelected : function(node){
34771         return this.selMap[node.id] ? true : false;  
34772     },
34773     
34774     /**
34775      * Returns an array of the selected nodes
34776      * @return {Array}
34777      */
34778     getSelectedNodes : function(){
34779         return this.selNodes;    
34780     },
34781
34782     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34783
34784     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34785
34786     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34787 });/*
34788  * Based on:
34789  * Ext JS Library 1.1.1
34790  * Copyright(c) 2006-2007, Ext JS, LLC.
34791  *
34792  * Originally Released Under LGPL - original licence link has changed is not relivant.
34793  *
34794  * Fork - LGPL
34795  * <script type="text/javascript">
34796  */
34797  
34798 /**
34799  * @class Roo.tree.TreeNode
34800  * @extends Roo.data.Node
34801  * @cfg {String} text The text for this node
34802  * @cfg {Boolean} expanded true to start the node expanded
34803  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34804  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34805  * @cfg {Boolean} disabled true to start the node disabled
34806  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34807  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34808  * @cfg {String} cls A css class to be added to the node
34809  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34810  * @cfg {String} href URL of the link used for the node (defaults to #)
34811  * @cfg {String} hrefTarget target frame for the link
34812  * @cfg {String} qtip An Ext QuickTip for the node
34813  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34814  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34815  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34816  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34817  * (defaults to undefined with no checkbox rendered)
34818  * @constructor
34819  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34820  */
34821 Roo.tree.TreeNode = function(attributes){
34822     attributes = attributes || {};
34823     if(typeof attributes == "string"){
34824         attributes = {text: attributes};
34825     }
34826     this.childrenRendered = false;
34827     this.rendered = false;
34828     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34829     this.expanded = attributes.expanded === true;
34830     this.isTarget = attributes.isTarget !== false;
34831     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34832     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34833
34834     /**
34835      * Read-only. The text for this node. To change it use setText().
34836      * @type String
34837      */
34838     this.text = attributes.text;
34839     /**
34840      * True if this node is disabled.
34841      * @type Boolean
34842      */
34843     this.disabled = attributes.disabled === true;
34844
34845     this.addEvents({
34846         /**
34847         * @event textchange
34848         * Fires when the text for this node is changed
34849         * @param {Node} this This node
34850         * @param {String} text The new text
34851         * @param {String} oldText The old text
34852         */
34853         "textchange" : true,
34854         /**
34855         * @event beforeexpand
34856         * Fires before this node is expanded, return false to cancel.
34857         * @param {Node} this This node
34858         * @param {Boolean} deep
34859         * @param {Boolean} anim
34860         */
34861         "beforeexpand" : true,
34862         /**
34863         * @event beforecollapse
34864         * Fires before this node is collapsed, return false to cancel.
34865         * @param {Node} this This node
34866         * @param {Boolean} deep
34867         * @param {Boolean} anim
34868         */
34869         "beforecollapse" : true,
34870         /**
34871         * @event expand
34872         * Fires when this node is expanded
34873         * @param {Node} this This node
34874         */
34875         "expand" : true,
34876         /**
34877         * @event disabledchange
34878         * Fires when the disabled status of this node changes
34879         * @param {Node} this This node
34880         * @param {Boolean} disabled
34881         */
34882         "disabledchange" : true,
34883         /**
34884         * @event collapse
34885         * Fires when this node is collapsed
34886         * @param {Node} this This node
34887         */
34888         "collapse" : true,
34889         /**
34890         * @event beforeclick
34891         * Fires before click processing. Return false to cancel the default action.
34892         * @param {Node} this This node
34893         * @param {Roo.EventObject} e The event object
34894         */
34895         "beforeclick":true,
34896         /**
34897         * @event checkchange
34898         * Fires when a node with a checkbox's checked property changes
34899         * @param {Node} this This node
34900         * @param {Boolean} checked
34901         */
34902         "checkchange":true,
34903         /**
34904         * @event click
34905         * Fires when this node is clicked
34906         * @param {Node} this This node
34907         * @param {Roo.EventObject} e The event object
34908         */
34909         "click":true,
34910         /**
34911         * @event dblclick
34912         * Fires when this node is double clicked
34913         * @param {Node} this This node
34914         * @param {Roo.EventObject} e The event object
34915         */
34916         "dblclick":true,
34917         /**
34918         * @event contextmenu
34919         * Fires when this node is right clicked
34920         * @param {Node} this This node
34921         * @param {Roo.EventObject} e The event object
34922         */
34923         "contextmenu":true,
34924         /**
34925         * @event beforechildrenrendered
34926         * Fires right before the child nodes for this node are rendered
34927         * @param {Node} this This node
34928         */
34929         "beforechildrenrendered":true
34930     });
34931
34932     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34933
34934     /**
34935      * Read-only. The UI for this node
34936      * @type TreeNodeUI
34937      */
34938     this.ui = new uiClass(this);
34939     
34940     // finally support items[]
34941     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34942         return;
34943     }
34944     
34945     
34946     Roo.each(this.attributes.items, function(c) {
34947         this.appendChild(Roo.factory(c,Roo.Tree));
34948     }, this);
34949     delete this.attributes.items;
34950     
34951     
34952     
34953 };
34954 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34955     preventHScroll: true,
34956     /**
34957      * Returns true if this node is expanded
34958      * @return {Boolean}
34959      */
34960     isExpanded : function(){
34961         return this.expanded;
34962     },
34963
34964     /**
34965      * Returns the UI object for this node
34966      * @return {TreeNodeUI}
34967      */
34968     getUI : function(){
34969         return this.ui;
34970     },
34971
34972     // private override
34973     setFirstChild : function(node){
34974         var of = this.firstChild;
34975         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34976         if(this.childrenRendered && of && node != of){
34977             of.renderIndent(true, true);
34978         }
34979         if(this.rendered){
34980             this.renderIndent(true, true);
34981         }
34982     },
34983
34984     // private override
34985     setLastChild : function(node){
34986         var ol = this.lastChild;
34987         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34988         if(this.childrenRendered && ol && node != ol){
34989             ol.renderIndent(true, true);
34990         }
34991         if(this.rendered){
34992             this.renderIndent(true, true);
34993         }
34994     },
34995
34996     // these methods are overridden to provide lazy rendering support
34997     // private override
34998     appendChild : function()
34999     {
35000         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35001         if(node && this.childrenRendered){
35002             node.render();
35003         }
35004         this.ui.updateExpandIcon();
35005         return node;
35006     },
35007
35008     // private override
35009     removeChild : function(node){
35010         this.ownerTree.getSelectionModel().unselect(node);
35011         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35012         // if it's been rendered remove dom node
35013         if(this.childrenRendered){
35014             node.ui.remove();
35015         }
35016         if(this.childNodes.length < 1){
35017             this.collapse(false, false);
35018         }else{
35019             this.ui.updateExpandIcon();
35020         }
35021         if(!this.firstChild) {
35022             this.childrenRendered = false;
35023         }
35024         return node;
35025     },
35026
35027     // private override
35028     insertBefore : function(node, refNode){
35029         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35030         if(newNode && refNode && this.childrenRendered){
35031             node.render();
35032         }
35033         this.ui.updateExpandIcon();
35034         return newNode;
35035     },
35036
35037     /**
35038      * Sets the text for this node
35039      * @param {String} text
35040      */
35041     setText : function(text){
35042         var oldText = this.text;
35043         this.text = text;
35044         this.attributes.text = text;
35045         if(this.rendered){ // event without subscribing
35046             this.ui.onTextChange(this, text, oldText);
35047         }
35048         this.fireEvent("textchange", this, text, oldText);
35049     },
35050
35051     /**
35052      * Triggers selection of this node
35053      */
35054     select : function(){
35055         this.getOwnerTree().getSelectionModel().select(this);
35056     },
35057
35058     /**
35059      * Triggers deselection of this node
35060      */
35061     unselect : function(){
35062         this.getOwnerTree().getSelectionModel().unselect(this);
35063     },
35064
35065     /**
35066      * Returns true if this node is selected
35067      * @return {Boolean}
35068      */
35069     isSelected : function(){
35070         return this.getOwnerTree().getSelectionModel().isSelected(this);
35071     },
35072
35073     /**
35074      * Expand this node.
35075      * @param {Boolean} deep (optional) True to expand all children as well
35076      * @param {Boolean} anim (optional) false to cancel the default animation
35077      * @param {Function} callback (optional) A callback to be called when
35078      * expanding this node completes (does not wait for deep expand to complete).
35079      * Called with 1 parameter, this node.
35080      */
35081     expand : function(deep, anim, callback){
35082         if(!this.expanded){
35083             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35084                 return;
35085             }
35086             if(!this.childrenRendered){
35087                 this.renderChildren();
35088             }
35089             this.expanded = true;
35090             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35091                 this.ui.animExpand(function(){
35092                     this.fireEvent("expand", this);
35093                     if(typeof callback == "function"){
35094                         callback(this);
35095                     }
35096                     if(deep === true){
35097                         this.expandChildNodes(true);
35098                     }
35099                 }.createDelegate(this));
35100                 return;
35101             }else{
35102                 this.ui.expand();
35103                 this.fireEvent("expand", this);
35104                 if(typeof callback == "function"){
35105                     callback(this);
35106                 }
35107             }
35108         }else{
35109            if(typeof callback == "function"){
35110                callback(this);
35111            }
35112         }
35113         if(deep === true){
35114             this.expandChildNodes(true);
35115         }
35116     },
35117
35118     isHiddenRoot : function(){
35119         return this.isRoot && !this.getOwnerTree().rootVisible;
35120     },
35121
35122     /**
35123      * Collapse this node.
35124      * @param {Boolean} deep (optional) True to collapse all children as well
35125      * @param {Boolean} anim (optional) false to cancel the default animation
35126      */
35127     collapse : function(deep, anim){
35128         if(this.expanded && !this.isHiddenRoot()){
35129             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35130                 return;
35131             }
35132             this.expanded = false;
35133             if((this.getOwnerTree().animate && anim !== false) || anim){
35134                 this.ui.animCollapse(function(){
35135                     this.fireEvent("collapse", this);
35136                     if(deep === true){
35137                         this.collapseChildNodes(true);
35138                     }
35139                 }.createDelegate(this));
35140                 return;
35141             }else{
35142                 this.ui.collapse();
35143                 this.fireEvent("collapse", this);
35144             }
35145         }
35146         if(deep === true){
35147             var cs = this.childNodes;
35148             for(var i = 0, len = cs.length; i < len; i++) {
35149                 cs[i].collapse(true, false);
35150             }
35151         }
35152     },
35153
35154     // private
35155     delayedExpand : function(delay){
35156         if(!this.expandProcId){
35157             this.expandProcId = this.expand.defer(delay, this);
35158         }
35159     },
35160
35161     // private
35162     cancelExpand : function(){
35163         if(this.expandProcId){
35164             clearTimeout(this.expandProcId);
35165         }
35166         this.expandProcId = false;
35167     },
35168
35169     /**
35170      * Toggles expanded/collapsed state of the node
35171      */
35172     toggle : function(){
35173         if(this.expanded){
35174             this.collapse();
35175         }else{
35176             this.expand();
35177         }
35178     },
35179
35180     /**
35181      * Ensures all parent nodes are expanded
35182      */
35183     ensureVisible : function(callback){
35184         var tree = this.getOwnerTree();
35185         tree.expandPath(this.parentNode.getPath(), false, function(){
35186             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35187             Roo.callback(callback);
35188         }.createDelegate(this));
35189     },
35190
35191     /**
35192      * Expand all child nodes
35193      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35194      */
35195     expandChildNodes : function(deep){
35196         var cs = this.childNodes;
35197         for(var i = 0, len = cs.length; i < len; i++) {
35198                 cs[i].expand(deep);
35199         }
35200     },
35201
35202     /**
35203      * Collapse all child nodes
35204      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35205      */
35206     collapseChildNodes : function(deep){
35207         var cs = this.childNodes;
35208         for(var i = 0, len = cs.length; i < len; i++) {
35209                 cs[i].collapse(deep);
35210         }
35211     },
35212
35213     /**
35214      * Disables this node
35215      */
35216     disable : function(){
35217         this.disabled = true;
35218         this.unselect();
35219         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35220             this.ui.onDisableChange(this, true);
35221         }
35222         this.fireEvent("disabledchange", this, true);
35223     },
35224
35225     /**
35226      * Enables this node
35227      */
35228     enable : function(){
35229         this.disabled = false;
35230         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35231             this.ui.onDisableChange(this, false);
35232         }
35233         this.fireEvent("disabledchange", this, false);
35234     },
35235
35236     // private
35237     renderChildren : function(suppressEvent){
35238         if(suppressEvent !== false){
35239             this.fireEvent("beforechildrenrendered", this);
35240         }
35241         var cs = this.childNodes;
35242         for(var i = 0, len = cs.length; i < len; i++){
35243             cs[i].render(true);
35244         }
35245         this.childrenRendered = true;
35246     },
35247
35248     // private
35249     sort : function(fn, scope){
35250         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35251         if(this.childrenRendered){
35252             var cs = this.childNodes;
35253             for(var i = 0, len = cs.length; i < len; i++){
35254                 cs[i].render(true);
35255             }
35256         }
35257     },
35258
35259     // private
35260     render : function(bulkRender){
35261         this.ui.render(bulkRender);
35262         if(!this.rendered){
35263             this.rendered = true;
35264             if(this.expanded){
35265                 this.expanded = false;
35266                 this.expand(false, false);
35267             }
35268         }
35269     },
35270
35271     // private
35272     renderIndent : function(deep, refresh){
35273         if(refresh){
35274             this.ui.childIndent = null;
35275         }
35276         this.ui.renderIndent();
35277         if(deep === true && this.childrenRendered){
35278             var cs = this.childNodes;
35279             for(var i = 0, len = cs.length; i < len; i++){
35280                 cs[i].renderIndent(true, refresh);
35281             }
35282         }
35283     }
35284 });/*
35285  * Based on:
35286  * Ext JS Library 1.1.1
35287  * Copyright(c) 2006-2007, Ext JS, LLC.
35288  *
35289  * Originally Released Under LGPL - original licence link has changed is not relivant.
35290  *
35291  * Fork - LGPL
35292  * <script type="text/javascript">
35293  */
35294  
35295 /**
35296  * @class Roo.tree.AsyncTreeNode
35297  * @extends Roo.tree.TreeNode
35298  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35299  * @constructor
35300  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35301  */
35302  Roo.tree.AsyncTreeNode = function(config){
35303     this.loaded = false;
35304     this.loading = false;
35305     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35306     /**
35307     * @event beforeload
35308     * Fires before this node is loaded, return false to cancel
35309     * @param {Node} this This node
35310     */
35311     this.addEvents({'beforeload':true, 'load': true});
35312     /**
35313     * @event load
35314     * Fires when this node is loaded
35315     * @param {Node} this This node
35316     */
35317     /**
35318      * The loader used by this node (defaults to using the tree's defined loader)
35319      * @type TreeLoader
35320      * @property loader
35321      */
35322 };
35323 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35324     expand : function(deep, anim, callback){
35325         if(this.loading){ // if an async load is already running, waiting til it's done
35326             var timer;
35327             var f = function(){
35328                 if(!this.loading){ // done loading
35329                     clearInterval(timer);
35330                     this.expand(deep, anim, callback);
35331                 }
35332             }.createDelegate(this);
35333             timer = setInterval(f, 200);
35334             return;
35335         }
35336         if(!this.loaded){
35337             if(this.fireEvent("beforeload", this) === false){
35338                 return;
35339             }
35340             this.loading = true;
35341             this.ui.beforeLoad(this);
35342             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35343             if(loader){
35344                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35345                 return;
35346             }
35347         }
35348         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35349     },
35350     
35351     /**
35352      * Returns true if this node is currently loading
35353      * @return {Boolean}
35354      */
35355     isLoading : function(){
35356         return this.loading;  
35357     },
35358     
35359     loadComplete : function(deep, anim, callback){
35360         this.loading = false;
35361         this.loaded = true;
35362         this.ui.afterLoad(this);
35363         this.fireEvent("load", this);
35364         this.expand(deep, anim, callback);
35365     },
35366     
35367     /**
35368      * Returns true if this node has been loaded
35369      * @return {Boolean}
35370      */
35371     isLoaded : function(){
35372         return this.loaded;
35373     },
35374     
35375     hasChildNodes : function(){
35376         if(!this.isLeaf() && !this.loaded){
35377             return true;
35378         }else{
35379             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35380         }
35381     },
35382
35383     /**
35384      * Trigger a reload for this node
35385      * @param {Function} callback
35386      */
35387     reload : function(callback){
35388         this.collapse(false, false);
35389         while(this.firstChild){
35390             this.removeChild(this.firstChild);
35391         }
35392         this.childrenRendered = false;
35393         this.loaded = false;
35394         if(this.isHiddenRoot()){
35395             this.expanded = false;
35396         }
35397         this.expand(false, false, callback);
35398     }
35399 });/*
35400  * Based on:
35401  * Ext JS Library 1.1.1
35402  * Copyright(c) 2006-2007, Ext JS, LLC.
35403  *
35404  * Originally Released Under LGPL - original licence link has changed is not relivant.
35405  *
35406  * Fork - LGPL
35407  * <script type="text/javascript">
35408  */
35409  
35410 /**
35411  * @class Roo.tree.TreeNodeUI
35412  * @constructor
35413  * @param {Object} node The node to render
35414  * The TreeNode UI implementation is separate from the
35415  * tree implementation. Unless you are customizing the tree UI,
35416  * you should never have to use this directly.
35417  */
35418 Roo.tree.TreeNodeUI = function(node){
35419     this.node = node;
35420     this.rendered = false;
35421     this.animating = false;
35422     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35423 };
35424
35425 Roo.tree.TreeNodeUI.prototype = {
35426     removeChild : function(node){
35427         if(this.rendered){
35428             this.ctNode.removeChild(node.ui.getEl());
35429         }
35430     },
35431
35432     beforeLoad : function(){
35433          this.addClass("x-tree-node-loading");
35434     },
35435
35436     afterLoad : function(){
35437          this.removeClass("x-tree-node-loading");
35438     },
35439
35440     onTextChange : function(node, text, oldText){
35441         if(this.rendered){
35442             this.textNode.innerHTML = text;
35443         }
35444     },
35445
35446     onDisableChange : function(node, state){
35447         this.disabled = state;
35448         if(state){
35449             this.addClass("x-tree-node-disabled");
35450         }else{
35451             this.removeClass("x-tree-node-disabled");
35452         }
35453     },
35454
35455     onSelectedChange : function(state){
35456         if(state){
35457             this.focus();
35458             this.addClass("x-tree-selected");
35459         }else{
35460             //this.blur();
35461             this.removeClass("x-tree-selected");
35462         }
35463     },
35464
35465     onMove : function(tree, node, oldParent, newParent, index, refNode){
35466         this.childIndent = null;
35467         if(this.rendered){
35468             var targetNode = newParent.ui.getContainer();
35469             if(!targetNode){//target not rendered
35470                 this.holder = document.createElement("div");
35471                 this.holder.appendChild(this.wrap);
35472                 return;
35473             }
35474             var insertBefore = refNode ? refNode.ui.getEl() : null;
35475             if(insertBefore){
35476                 targetNode.insertBefore(this.wrap, insertBefore);
35477             }else{
35478                 targetNode.appendChild(this.wrap);
35479             }
35480             this.node.renderIndent(true);
35481         }
35482     },
35483
35484     addClass : function(cls){
35485         if(this.elNode){
35486             Roo.fly(this.elNode).addClass(cls);
35487         }
35488     },
35489
35490     removeClass : function(cls){
35491         if(this.elNode){
35492             Roo.fly(this.elNode).removeClass(cls);
35493         }
35494     },
35495
35496     remove : function(){
35497         if(this.rendered){
35498             this.holder = document.createElement("div");
35499             this.holder.appendChild(this.wrap);
35500         }
35501     },
35502
35503     fireEvent : function(){
35504         return this.node.fireEvent.apply(this.node, arguments);
35505     },
35506
35507     initEvents : function(){
35508         this.node.on("move", this.onMove, this);
35509         var E = Roo.EventManager;
35510         var a = this.anchor;
35511
35512         var el = Roo.fly(a, '_treeui');
35513
35514         if(Roo.isOpera){ // opera render bug ignores the CSS
35515             el.setStyle("text-decoration", "none");
35516         }
35517
35518         el.on("click", this.onClick, this);
35519         el.on("dblclick", this.onDblClick, this);
35520
35521         if(this.checkbox){
35522             Roo.EventManager.on(this.checkbox,
35523                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35524         }
35525
35526         el.on("contextmenu", this.onContextMenu, this);
35527
35528         var icon = Roo.fly(this.iconNode);
35529         icon.on("click", this.onClick, this);
35530         icon.on("dblclick", this.onDblClick, this);
35531         icon.on("contextmenu", this.onContextMenu, this);
35532         E.on(this.ecNode, "click", this.ecClick, this, true);
35533
35534         if(this.node.disabled){
35535             this.addClass("x-tree-node-disabled");
35536         }
35537         if(this.node.hidden){
35538             this.addClass("x-tree-node-disabled");
35539         }
35540         var ot = this.node.getOwnerTree();
35541         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35542         if(dd && (!this.node.isRoot || ot.rootVisible)){
35543             Roo.dd.Registry.register(this.elNode, {
35544                 node: this.node,
35545                 handles: this.getDDHandles(),
35546                 isHandle: false
35547             });
35548         }
35549     },
35550
35551     getDDHandles : function(){
35552         return [this.iconNode, this.textNode];
35553     },
35554
35555     hide : function(){
35556         if(this.rendered){
35557             this.wrap.style.display = "none";
35558         }
35559     },
35560
35561     show : function(){
35562         if(this.rendered){
35563             this.wrap.style.display = "";
35564         }
35565     },
35566
35567     onContextMenu : function(e){
35568         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35569             e.preventDefault();
35570             this.focus();
35571             this.fireEvent("contextmenu", this.node, e);
35572         }
35573     },
35574
35575     onClick : function(e){
35576         if(this.dropping){
35577             e.stopEvent();
35578             return;
35579         }
35580         if(this.fireEvent("beforeclick", this.node, e) !== false){
35581             if(!this.disabled && this.node.attributes.href){
35582                 this.fireEvent("click", this.node, e);
35583                 return;
35584             }
35585             e.preventDefault();
35586             if(this.disabled){
35587                 return;
35588             }
35589
35590             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35591                 this.node.toggle();
35592             }
35593
35594             this.fireEvent("click", this.node, e);
35595         }else{
35596             e.stopEvent();
35597         }
35598     },
35599
35600     onDblClick : function(e){
35601         e.preventDefault();
35602         if(this.disabled){
35603             return;
35604         }
35605         if(this.checkbox){
35606             this.toggleCheck();
35607         }
35608         if(!this.animating && this.node.hasChildNodes()){
35609             this.node.toggle();
35610         }
35611         this.fireEvent("dblclick", this.node, e);
35612     },
35613
35614     onCheckChange : function(){
35615         var checked = this.checkbox.checked;
35616         this.node.attributes.checked = checked;
35617         this.fireEvent('checkchange', this.node, checked);
35618     },
35619
35620     ecClick : function(e){
35621         if(!this.animating && this.node.hasChildNodes()){
35622             this.node.toggle();
35623         }
35624     },
35625
35626     startDrop : function(){
35627         this.dropping = true;
35628     },
35629
35630     // delayed drop so the click event doesn't get fired on a drop
35631     endDrop : function(){
35632        setTimeout(function(){
35633            this.dropping = false;
35634        }.createDelegate(this), 50);
35635     },
35636
35637     expand : function(){
35638         this.updateExpandIcon();
35639         this.ctNode.style.display = "";
35640     },
35641
35642     focus : function(){
35643         if(!this.node.preventHScroll){
35644             try{this.anchor.focus();
35645             }catch(e){}
35646         }else if(!Roo.isIE){
35647             try{
35648                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35649                 var l = noscroll.scrollLeft;
35650                 this.anchor.focus();
35651                 noscroll.scrollLeft = l;
35652             }catch(e){}
35653         }
35654     },
35655
35656     toggleCheck : function(value){
35657         var cb = this.checkbox;
35658         if(cb){
35659             cb.checked = (value === undefined ? !cb.checked : value);
35660         }
35661     },
35662
35663     blur : function(){
35664         try{
35665             this.anchor.blur();
35666         }catch(e){}
35667     },
35668
35669     animExpand : function(callback){
35670         var ct = Roo.get(this.ctNode);
35671         ct.stopFx();
35672         if(!this.node.hasChildNodes()){
35673             this.updateExpandIcon();
35674             this.ctNode.style.display = "";
35675             Roo.callback(callback);
35676             return;
35677         }
35678         this.animating = true;
35679         this.updateExpandIcon();
35680
35681         ct.slideIn('t', {
35682            callback : function(){
35683                this.animating = false;
35684                Roo.callback(callback);
35685             },
35686             scope: this,
35687             duration: this.node.ownerTree.duration || .25
35688         });
35689     },
35690
35691     highlight : function(){
35692         var tree = this.node.getOwnerTree();
35693         Roo.fly(this.wrap).highlight(
35694             tree.hlColor || "C3DAF9",
35695             {endColor: tree.hlBaseColor}
35696         );
35697     },
35698
35699     collapse : function(){
35700         this.updateExpandIcon();
35701         this.ctNode.style.display = "none";
35702     },
35703
35704     animCollapse : function(callback){
35705         var ct = Roo.get(this.ctNode);
35706         ct.enableDisplayMode('block');
35707         ct.stopFx();
35708
35709         this.animating = true;
35710         this.updateExpandIcon();
35711
35712         ct.slideOut('t', {
35713             callback : function(){
35714                this.animating = false;
35715                Roo.callback(callback);
35716             },
35717             scope: this,
35718             duration: this.node.ownerTree.duration || .25
35719         });
35720     },
35721
35722     getContainer : function(){
35723         return this.ctNode;
35724     },
35725
35726     getEl : function(){
35727         return this.wrap;
35728     },
35729
35730     appendDDGhost : function(ghostNode){
35731         ghostNode.appendChild(this.elNode.cloneNode(true));
35732     },
35733
35734     getDDRepairXY : function(){
35735         return Roo.lib.Dom.getXY(this.iconNode);
35736     },
35737
35738     onRender : function(){
35739         this.render();
35740     },
35741
35742     render : function(bulkRender){
35743         var n = this.node, a = n.attributes;
35744         var targetNode = n.parentNode ?
35745               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35746
35747         if(!this.rendered){
35748             this.rendered = true;
35749
35750             this.renderElements(n, a, targetNode, bulkRender);
35751
35752             if(a.qtip){
35753                if(this.textNode.setAttributeNS){
35754                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35755                    if(a.qtipTitle){
35756                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35757                    }
35758                }else{
35759                    this.textNode.setAttribute("ext:qtip", a.qtip);
35760                    if(a.qtipTitle){
35761                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35762                    }
35763                }
35764             }else if(a.qtipCfg){
35765                 a.qtipCfg.target = Roo.id(this.textNode);
35766                 Roo.QuickTips.register(a.qtipCfg);
35767             }
35768             this.initEvents();
35769             if(!this.node.expanded){
35770                 this.updateExpandIcon();
35771             }
35772         }else{
35773             if(bulkRender === true) {
35774                 targetNode.appendChild(this.wrap);
35775             }
35776         }
35777     },
35778
35779     renderElements : function(n, a, targetNode, bulkRender)
35780     {
35781         // add some indent caching, this helps performance when rendering a large tree
35782         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35783         var t = n.getOwnerTree();
35784         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35785         if (typeof(n.attributes.html) != 'undefined') {
35786             txt = n.attributes.html;
35787         }
35788         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35789         var cb = typeof a.checked == 'boolean';
35790         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35791         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35792             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35793             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35794             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35795             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35796             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35797              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35798                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35799             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35800             "</li>"];
35801
35802         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35803             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35804                                 n.nextSibling.ui.getEl(), buf.join(""));
35805         }else{
35806             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35807         }
35808
35809         this.elNode = this.wrap.childNodes[0];
35810         this.ctNode = this.wrap.childNodes[1];
35811         var cs = this.elNode.childNodes;
35812         this.indentNode = cs[0];
35813         this.ecNode = cs[1];
35814         this.iconNode = cs[2];
35815         var index = 3;
35816         if(cb){
35817             this.checkbox = cs[3];
35818             index++;
35819         }
35820         this.anchor = cs[index];
35821         this.textNode = cs[index].firstChild;
35822     },
35823
35824     getAnchor : function(){
35825         return this.anchor;
35826     },
35827
35828     getTextEl : function(){
35829         return this.textNode;
35830     },
35831
35832     getIconEl : function(){
35833         return this.iconNode;
35834     },
35835
35836     isChecked : function(){
35837         return this.checkbox ? this.checkbox.checked : false;
35838     },
35839
35840     updateExpandIcon : function(){
35841         if(this.rendered){
35842             var n = this.node, c1, c2;
35843             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35844             var hasChild = n.hasChildNodes();
35845             if(hasChild){
35846                 if(n.expanded){
35847                     cls += "-minus";
35848                     c1 = "x-tree-node-collapsed";
35849                     c2 = "x-tree-node-expanded";
35850                 }else{
35851                     cls += "-plus";
35852                     c1 = "x-tree-node-expanded";
35853                     c2 = "x-tree-node-collapsed";
35854                 }
35855                 if(this.wasLeaf){
35856                     this.removeClass("x-tree-node-leaf");
35857                     this.wasLeaf = false;
35858                 }
35859                 if(this.c1 != c1 || this.c2 != c2){
35860                     Roo.fly(this.elNode).replaceClass(c1, c2);
35861                     this.c1 = c1; this.c2 = c2;
35862                 }
35863             }else{
35864                 // this changes non-leafs into leafs if they have no children.
35865                 // it's not very rational behaviour..
35866                 
35867                 if(!this.wasLeaf && this.node.leaf){
35868                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35869                     delete this.c1;
35870                     delete this.c2;
35871                     this.wasLeaf = true;
35872                 }
35873             }
35874             var ecc = "x-tree-ec-icon "+cls;
35875             if(this.ecc != ecc){
35876                 this.ecNode.className = ecc;
35877                 this.ecc = ecc;
35878             }
35879         }
35880     },
35881
35882     getChildIndent : function(){
35883         if(!this.childIndent){
35884             var buf = [];
35885             var p = this.node;
35886             while(p){
35887                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35888                     if(!p.isLast()) {
35889                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35890                     } else {
35891                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35892                     }
35893                 }
35894                 p = p.parentNode;
35895             }
35896             this.childIndent = buf.join("");
35897         }
35898         return this.childIndent;
35899     },
35900
35901     renderIndent : function(){
35902         if(this.rendered){
35903             var indent = "";
35904             var p = this.node.parentNode;
35905             if(p){
35906                 indent = p.ui.getChildIndent();
35907             }
35908             if(this.indentMarkup != indent){ // don't rerender if not required
35909                 this.indentNode.innerHTML = indent;
35910                 this.indentMarkup = indent;
35911             }
35912             this.updateExpandIcon();
35913         }
35914     }
35915 };
35916
35917 Roo.tree.RootTreeNodeUI = function(){
35918     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35919 };
35920 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35921     render : function(){
35922         if(!this.rendered){
35923             var targetNode = this.node.ownerTree.innerCt.dom;
35924             this.node.expanded = true;
35925             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35926             this.wrap = this.ctNode = targetNode.firstChild;
35927         }
35928     },
35929     collapse : function(){
35930     },
35931     expand : function(){
35932     }
35933 });/*
35934  * Based on:
35935  * Ext JS Library 1.1.1
35936  * Copyright(c) 2006-2007, Ext JS, LLC.
35937  *
35938  * Originally Released Under LGPL - original licence link has changed is not relivant.
35939  *
35940  * Fork - LGPL
35941  * <script type="text/javascript">
35942  */
35943 /**
35944  * @class Roo.tree.TreeLoader
35945  * @extends Roo.util.Observable
35946  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35947  * nodes from a specified URL. The response must be a javascript Array definition
35948  * who's elements are node definition objects. eg:
35949  * <pre><code>
35950 {  success : true,
35951    data :      [
35952    
35953     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35954     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35955     ]
35956 }
35957
35958
35959 </code></pre>
35960  * <br><br>
35961  * The old style respose with just an array is still supported, but not recommended.
35962  * <br><br>
35963  *
35964  * A server request is sent, and child nodes are loaded only when a node is expanded.
35965  * The loading node's id is passed to the server under the parameter name "node" to
35966  * enable the server to produce the correct child nodes.
35967  * <br><br>
35968  * To pass extra parameters, an event handler may be attached to the "beforeload"
35969  * event, and the parameters specified in the TreeLoader's baseParams property:
35970  * <pre><code>
35971     myTreeLoader.on("beforeload", function(treeLoader, node) {
35972         this.baseParams.category = node.attributes.category;
35973     }, this);
35974 </code></pre><
35975  * This would pass an HTTP parameter called "category" to the server containing
35976  * the value of the Node's "category" attribute.
35977  * @constructor
35978  * Creates a new Treeloader.
35979  * @param {Object} config A config object containing config properties.
35980  */
35981 Roo.tree.TreeLoader = function(config){
35982     this.baseParams = {};
35983     this.requestMethod = "POST";
35984     Roo.apply(this, config);
35985
35986     this.addEvents({
35987     
35988         /**
35989          * @event beforeload
35990          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35991          * @param {Object} This TreeLoader object.
35992          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35993          * @param {Object} callback The callback function specified in the {@link #load} call.
35994          */
35995         beforeload : true,
35996         /**
35997          * @event load
35998          * Fires when the node has been successfuly loaded.
35999          * @param {Object} This TreeLoader object.
36000          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36001          * @param {Object} response The response object containing the data from the server.
36002          */
36003         load : true,
36004         /**
36005          * @event loadexception
36006          * Fires if the network request failed.
36007          * @param {Object} This TreeLoader object.
36008          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36009          * @param {Object} response The response object containing the data from the server.
36010          */
36011         loadexception : true,
36012         /**
36013          * @event create
36014          * Fires before a node is created, enabling you to return custom Node types 
36015          * @param {Object} This TreeLoader object.
36016          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36017          */
36018         create : true
36019     });
36020
36021     Roo.tree.TreeLoader.superclass.constructor.call(this);
36022 };
36023
36024 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36025     /**
36026     * @cfg {String} dataUrl The URL from which to request a Json string which
36027     * specifies an array of node definition object representing the child nodes
36028     * to be loaded.
36029     */
36030     /**
36031     * @cfg {String} requestMethod either GET or POST
36032     * defaults to POST (due to BC)
36033     * to be loaded.
36034     */
36035     /**
36036     * @cfg {Object} baseParams (optional) An object containing properties which
36037     * specify HTTP parameters to be passed to each request for child nodes.
36038     */
36039     /**
36040     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36041     * created by this loader. If the attributes sent by the server have an attribute in this object,
36042     * they take priority.
36043     */
36044     /**
36045     * @cfg {Object} uiProviders (optional) An object containing properties which
36046     * 
36047     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36048     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36049     * <i>uiProvider</i> attribute of a returned child node is a string rather
36050     * than a reference to a TreeNodeUI implementation, this that string value
36051     * is used as a property name in the uiProviders object. You can define the provider named
36052     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36053     */
36054     uiProviders : {},
36055
36056     /**
36057     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36058     * child nodes before loading.
36059     */
36060     clearOnLoad : true,
36061
36062     /**
36063     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36064     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36065     * Grid query { data : [ .....] }
36066     */
36067     
36068     root : false,
36069      /**
36070     * @cfg {String} queryParam (optional) 
36071     * Name of the query as it will be passed on the querystring (defaults to 'node')
36072     * eg. the request will be ?node=[id]
36073     */
36074     
36075     
36076     queryParam: false,
36077     
36078     /**
36079      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36080      * This is called automatically when a node is expanded, but may be used to reload
36081      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36082      * @param {Roo.tree.TreeNode} node
36083      * @param {Function} callback
36084      */
36085     load : function(node, callback){
36086         if(this.clearOnLoad){
36087             while(node.firstChild){
36088                 node.removeChild(node.firstChild);
36089             }
36090         }
36091         if(node.attributes.children){ // preloaded json children
36092             var cs = node.attributes.children;
36093             for(var i = 0, len = cs.length; i < len; i++){
36094                 node.appendChild(this.createNode(cs[i]));
36095             }
36096             if(typeof callback == "function"){
36097                 callback();
36098             }
36099         }else if(this.dataUrl){
36100             this.requestData(node, callback);
36101         }
36102     },
36103
36104     getParams: function(node){
36105         var buf = [], bp = this.baseParams;
36106         for(var key in bp){
36107             if(typeof bp[key] != "function"){
36108                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36109             }
36110         }
36111         var n = this.queryParam === false ? 'node' : this.queryParam;
36112         buf.push(n + "=", encodeURIComponent(node.id));
36113         return buf.join("");
36114     },
36115
36116     requestData : function(node, callback){
36117         if(this.fireEvent("beforeload", this, node, callback) !== false){
36118             this.transId = Roo.Ajax.request({
36119                 method:this.requestMethod,
36120                 url: this.dataUrl||this.url,
36121                 success: this.handleResponse,
36122                 failure: this.handleFailure,
36123                 scope: this,
36124                 argument: {callback: callback, node: node},
36125                 params: this.getParams(node)
36126             });
36127         }else{
36128             // if the load is cancelled, make sure we notify
36129             // the node that we are done
36130             if(typeof callback == "function"){
36131                 callback();
36132             }
36133         }
36134     },
36135
36136     isLoading : function(){
36137         return this.transId ? true : false;
36138     },
36139
36140     abort : function(){
36141         if(this.isLoading()){
36142             Roo.Ajax.abort(this.transId);
36143         }
36144     },
36145
36146     // private
36147     createNode : function(attr)
36148     {
36149         // apply baseAttrs, nice idea Corey!
36150         if(this.baseAttrs){
36151             Roo.applyIf(attr, this.baseAttrs);
36152         }
36153         if(this.applyLoader !== false){
36154             attr.loader = this;
36155         }
36156         // uiProvider = depreciated..
36157         
36158         if(typeof(attr.uiProvider) == 'string'){
36159            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36160                 /**  eval:var:attr */ eval(attr.uiProvider);
36161         }
36162         if(typeof(this.uiProviders['default']) != 'undefined') {
36163             attr.uiProvider = this.uiProviders['default'];
36164         }
36165         
36166         this.fireEvent('create', this, attr);
36167         
36168         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36169         return(attr.leaf ?
36170                         new Roo.tree.TreeNode(attr) :
36171                         new Roo.tree.AsyncTreeNode(attr));
36172     },
36173
36174     processResponse : function(response, node, callback)
36175     {
36176         var json = response.responseText;
36177         try {
36178             
36179             var o = Roo.decode(json);
36180             
36181             if (this.root === false && typeof(o.success) != undefined) {
36182                 this.root = 'data'; // the default behaviour for list like data..
36183                 }
36184                 
36185             if (this.root !== false &&  !o.success) {
36186                 // it's a failure condition.
36187                 var a = response.argument;
36188                 this.fireEvent("loadexception", this, a.node, response);
36189                 Roo.log("Load failed - should have a handler really");
36190                 return;
36191             }
36192             
36193             
36194             
36195             if (this.root !== false) {
36196                  o = o[this.root];
36197             }
36198             
36199             for(var i = 0, len = o.length; i < len; i++){
36200                 var n = this.createNode(o[i]);
36201                 if(n){
36202                     node.appendChild(n);
36203                 }
36204             }
36205             if(typeof callback == "function"){
36206                 callback(this, node);
36207             }
36208         }catch(e){
36209             this.handleFailure(response);
36210         }
36211     },
36212
36213     handleResponse : function(response){
36214         this.transId = false;
36215         var a = response.argument;
36216         this.processResponse(response, a.node, a.callback);
36217         this.fireEvent("load", this, a.node, response);
36218     },
36219
36220     handleFailure : function(response)
36221     {
36222         // should handle failure better..
36223         this.transId = false;
36224         var a = response.argument;
36225         this.fireEvent("loadexception", this, a.node, response);
36226         if(typeof a.callback == "function"){
36227             a.callback(this, a.node);
36228         }
36229     }
36230 });/*
36231  * Based on:
36232  * Ext JS Library 1.1.1
36233  * Copyright(c) 2006-2007, Ext JS, LLC.
36234  *
36235  * Originally Released Under LGPL - original licence link has changed is not relivant.
36236  *
36237  * Fork - LGPL
36238  * <script type="text/javascript">
36239  */
36240
36241 /**
36242 * @class Roo.tree.TreeFilter
36243 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36244 * @param {TreePanel} tree
36245 * @param {Object} config (optional)
36246  */
36247 Roo.tree.TreeFilter = function(tree, config){
36248     this.tree = tree;
36249     this.filtered = {};
36250     Roo.apply(this, config);
36251 };
36252
36253 Roo.tree.TreeFilter.prototype = {
36254     clearBlank:false,
36255     reverse:false,
36256     autoClear:false,
36257     remove:false,
36258
36259      /**
36260      * Filter the data by a specific attribute.
36261      * @param {String/RegExp} value Either string that the attribute value
36262      * should start with or a RegExp to test against the attribute
36263      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36264      * @param {TreeNode} startNode (optional) The node to start the filter at.
36265      */
36266     filter : function(value, attr, startNode){
36267         attr = attr || "text";
36268         var f;
36269         if(typeof value == "string"){
36270             var vlen = value.length;
36271             // auto clear empty filter
36272             if(vlen == 0 && this.clearBlank){
36273                 this.clear();
36274                 return;
36275             }
36276             value = value.toLowerCase();
36277             f = function(n){
36278                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36279             };
36280         }else if(value.exec){ // regex?
36281             f = function(n){
36282                 return value.test(n.attributes[attr]);
36283             };
36284         }else{
36285             throw 'Illegal filter type, must be string or regex';
36286         }
36287         this.filterBy(f, null, startNode);
36288         },
36289
36290     /**
36291      * Filter by a function. The passed function will be called with each
36292      * node in the tree (or from the startNode). If the function returns true, the node is kept
36293      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36294      * @param {Function} fn The filter function
36295      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36296      */
36297     filterBy : function(fn, scope, startNode){
36298         startNode = startNode || this.tree.root;
36299         if(this.autoClear){
36300             this.clear();
36301         }
36302         var af = this.filtered, rv = this.reverse;
36303         var f = function(n){
36304             if(n == startNode){
36305                 return true;
36306             }
36307             if(af[n.id]){
36308                 return false;
36309             }
36310             var m = fn.call(scope || n, n);
36311             if(!m || rv){
36312                 af[n.id] = n;
36313                 n.ui.hide();
36314                 return false;
36315             }
36316             return true;
36317         };
36318         startNode.cascade(f);
36319         if(this.remove){
36320            for(var id in af){
36321                if(typeof id != "function"){
36322                    var n = af[id];
36323                    if(n && n.parentNode){
36324                        n.parentNode.removeChild(n);
36325                    }
36326                }
36327            }
36328         }
36329     },
36330
36331     /**
36332      * Clears the current filter. Note: with the "remove" option
36333      * set a filter cannot be cleared.
36334      */
36335     clear : function(){
36336         var t = this.tree;
36337         var af = this.filtered;
36338         for(var id in af){
36339             if(typeof id != "function"){
36340                 var n = af[id];
36341                 if(n){
36342                     n.ui.show();
36343                 }
36344             }
36345         }
36346         this.filtered = {};
36347     }
36348 };
36349 /*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359  
36360
36361 /**
36362  * @class Roo.tree.TreeSorter
36363  * Provides sorting of nodes in a TreePanel
36364  * 
36365  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36366  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36367  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36368  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36369  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36370  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36371  * @constructor
36372  * @param {TreePanel} tree
36373  * @param {Object} config
36374  */
36375 Roo.tree.TreeSorter = function(tree, config){
36376     Roo.apply(this, config);
36377     tree.on("beforechildrenrendered", this.doSort, this);
36378     tree.on("append", this.updateSort, this);
36379     tree.on("insert", this.updateSort, this);
36380     
36381     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36382     var p = this.property || "text";
36383     var sortType = this.sortType;
36384     var fs = this.folderSort;
36385     var cs = this.caseSensitive === true;
36386     var leafAttr = this.leafAttr || 'leaf';
36387
36388     this.sortFn = function(n1, n2){
36389         if(fs){
36390             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36391                 return 1;
36392             }
36393             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36394                 return -1;
36395             }
36396         }
36397         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36398         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36399         if(v1 < v2){
36400                         return dsc ? +1 : -1;
36401                 }else if(v1 > v2){
36402                         return dsc ? -1 : +1;
36403         }else{
36404                 return 0;
36405         }
36406     };
36407 };
36408
36409 Roo.tree.TreeSorter.prototype = {
36410     doSort : function(node){
36411         node.sort(this.sortFn);
36412     },
36413     
36414     compareNodes : function(n1, n2){
36415         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36416     },
36417     
36418     updateSort : function(tree, node){
36419         if(node.childrenRendered){
36420             this.doSort.defer(1, this, [node]);
36421         }
36422     }
36423 };/*
36424  * Based on:
36425  * Ext JS Library 1.1.1
36426  * Copyright(c) 2006-2007, Ext JS, LLC.
36427  *
36428  * Originally Released Under LGPL - original licence link has changed is not relivant.
36429  *
36430  * Fork - LGPL
36431  * <script type="text/javascript">
36432  */
36433
36434 if(Roo.dd.DropZone){
36435     
36436 Roo.tree.TreeDropZone = function(tree, config){
36437     this.allowParentInsert = false;
36438     this.allowContainerDrop = false;
36439     this.appendOnly = false;
36440     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36441     this.tree = tree;
36442     this.lastInsertClass = "x-tree-no-status";
36443     this.dragOverData = {};
36444 };
36445
36446 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36447     ddGroup : "TreeDD",
36448     scroll:  true,
36449     
36450     expandDelay : 1000,
36451     
36452     expandNode : function(node){
36453         if(node.hasChildNodes() && !node.isExpanded()){
36454             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36455         }
36456     },
36457     
36458     queueExpand : function(node){
36459         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36460     },
36461     
36462     cancelExpand : function(){
36463         if(this.expandProcId){
36464             clearTimeout(this.expandProcId);
36465             this.expandProcId = false;
36466         }
36467     },
36468     
36469     isValidDropPoint : function(n, pt, dd, e, data){
36470         if(!n || !data){ return false; }
36471         var targetNode = n.node;
36472         var dropNode = data.node;
36473         // default drop rules
36474         if(!(targetNode && targetNode.isTarget && pt)){
36475             return false;
36476         }
36477         if(pt == "append" && targetNode.allowChildren === false){
36478             return false;
36479         }
36480         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36481             return false;
36482         }
36483         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36484             return false;
36485         }
36486         // reuse the object
36487         var overEvent = this.dragOverData;
36488         overEvent.tree = this.tree;
36489         overEvent.target = targetNode;
36490         overEvent.data = data;
36491         overEvent.point = pt;
36492         overEvent.source = dd;
36493         overEvent.rawEvent = e;
36494         overEvent.dropNode = dropNode;
36495         overEvent.cancel = false;  
36496         var result = this.tree.fireEvent("nodedragover", overEvent);
36497         return overEvent.cancel === false && result !== false;
36498     },
36499     
36500     getDropPoint : function(e, n, dd)
36501     {
36502         var tn = n.node;
36503         if(tn.isRoot){
36504             return tn.allowChildren !== false ? "append" : false; // always append for root
36505         }
36506         var dragEl = n.ddel;
36507         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36508         var y = Roo.lib.Event.getPageY(e);
36509         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36510         
36511         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36512         var noAppend = tn.allowChildren === false;
36513         if(this.appendOnly || tn.parentNode.allowChildren === false){
36514             return noAppend ? false : "append";
36515         }
36516         var noBelow = false;
36517         if(!this.allowParentInsert){
36518             noBelow = tn.hasChildNodes() && tn.isExpanded();
36519         }
36520         var q = (b - t) / (noAppend ? 2 : 3);
36521         if(y >= t && y < (t + q)){
36522             return "above";
36523         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36524             return "below";
36525         }else{
36526             return "append";
36527         }
36528     },
36529     
36530     onNodeEnter : function(n, dd, e, data)
36531     {
36532         this.cancelExpand();
36533     },
36534     
36535     onNodeOver : function(n, dd, e, data)
36536     {
36537        
36538         var pt = this.getDropPoint(e, n, dd);
36539         var node = n.node;
36540         
36541         // auto node expand check
36542         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36543             this.queueExpand(node);
36544         }else if(pt != "append"){
36545             this.cancelExpand();
36546         }
36547         
36548         // set the insert point style on the target node
36549         var returnCls = this.dropNotAllowed;
36550         if(this.isValidDropPoint(n, pt, dd, e, data)){
36551            if(pt){
36552                var el = n.ddel;
36553                var cls;
36554                if(pt == "above"){
36555                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36556                    cls = "x-tree-drag-insert-above";
36557                }else if(pt == "below"){
36558                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36559                    cls = "x-tree-drag-insert-below";
36560                }else{
36561                    returnCls = "x-tree-drop-ok-append";
36562                    cls = "x-tree-drag-append";
36563                }
36564                if(this.lastInsertClass != cls){
36565                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36566                    this.lastInsertClass = cls;
36567                }
36568            }
36569        }
36570        return returnCls;
36571     },
36572     
36573     onNodeOut : function(n, dd, e, data){
36574         
36575         this.cancelExpand();
36576         this.removeDropIndicators(n);
36577     },
36578     
36579     onNodeDrop : function(n, dd, e, data){
36580         var point = this.getDropPoint(e, n, dd);
36581         var targetNode = n.node;
36582         targetNode.ui.startDrop();
36583         if(!this.isValidDropPoint(n, point, dd, e, data)){
36584             targetNode.ui.endDrop();
36585             return false;
36586         }
36587         // first try to find the drop node
36588         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36589         var dropEvent = {
36590             tree : this.tree,
36591             target: targetNode,
36592             data: data,
36593             point: point,
36594             source: dd,
36595             rawEvent: e,
36596             dropNode: dropNode,
36597             cancel: !dropNode   
36598         };
36599         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36600         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36601             targetNode.ui.endDrop();
36602             return false;
36603         }
36604         // allow target changing
36605         targetNode = dropEvent.target;
36606         if(point == "append" && !targetNode.isExpanded()){
36607             targetNode.expand(false, null, function(){
36608                 this.completeDrop(dropEvent);
36609             }.createDelegate(this));
36610         }else{
36611             this.completeDrop(dropEvent);
36612         }
36613         return true;
36614     },
36615     
36616     completeDrop : function(de){
36617         var ns = de.dropNode, p = de.point, t = de.target;
36618         if(!(ns instanceof Array)){
36619             ns = [ns];
36620         }
36621         var n;
36622         for(var i = 0, len = ns.length; i < len; i++){
36623             n = ns[i];
36624             if(p == "above"){
36625                 t.parentNode.insertBefore(n, t);
36626             }else if(p == "below"){
36627                 t.parentNode.insertBefore(n, t.nextSibling);
36628             }else{
36629                 t.appendChild(n);
36630             }
36631         }
36632         n.ui.focus();
36633         if(this.tree.hlDrop){
36634             n.ui.highlight();
36635         }
36636         t.ui.endDrop();
36637         this.tree.fireEvent("nodedrop", de);
36638     },
36639     
36640     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36641         if(this.tree.hlDrop){
36642             dropNode.ui.focus();
36643             dropNode.ui.highlight();
36644         }
36645         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36646     },
36647     
36648     getTree : function(){
36649         return this.tree;
36650     },
36651     
36652     removeDropIndicators : function(n){
36653         if(n && n.ddel){
36654             var el = n.ddel;
36655             Roo.fly(el).removeClass([
36656                     "x-tree-drag-insert-above",
36657                     "x-tree-drag-insert-below",
36658                     "x-tree-drag-append"]);
36659             this.lastInsertClass = "_noclass";
36660         }
36661     },
36662     
36663     beforeDragDrop : function(target, e, id){
36664         this.cancelExpand();
36665         return true;
36666     },
36667     
36668     afterRepair : function(data){
36669         if(data && Roo.enableFx){
36670             data.node.ui.highlight();
36671         }
36672         this.hideProxy();
36673     } 
36674     
36675 });
36676
36677 }
36678 /*
36679  * Based on:
36680  * Ext JS Library 1.1.1
36681  * Copyright(c) 2006-2007, Ext JS, LLC.
36682  *
36683  * Originally Released Under LGPL - original licence link has changed is not relivant.
36684  *
36685  * Fork - LGPL
36686  * <script type="text/javascript">
36687  */
36688  
36689
36690 if(Roo.dd.DragZone){
36691 Roo.tree.TreeDragZone = function(tree, config){
36692     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36693     this.tree = tree;
36694 };
36695
36696 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36697     ddGroup : "TreeDD",
36698    
36699     onBeforeDrag : function(data, e){
36700         var n = data.node;
36701         return n && n.draggable && !n.disabled;
36702     },
36703      
36704     
36705     onInitDrag : function(e){
36706         var data = this.dragData;
36707         this.tree.getSelectionModel().select(data.node);
36708         this.proxy.update("");
36709         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36710         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36711     },
36712     
36713     getRepairXY : function(e, data){
36714         return data.node.ui.getDDRepairXY();
36715     },
36716     
36717     onEndDrag : function(data, e){
36718         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36719         
36720         
36721     },
36722     
36723     onValidDrop : function(dd, e, id){
36724         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36725         this.hideProxy();
36726     },
36727     
36728     beforeInvalidDrop : function(e, id){
36729         // this scrolls the original position back into view
36730         var sm = this.tree.getSelectionModel();
36731         sm.clearSelections();
36732         sm.select(this.dragData.node);
36733     }
36734 });
36735 }/*
36736  * Based on:
36737  * Ext JS Library 1.1.1
36738  * Copyright(c) 2006-2007, Ext JS, LLC.
36739  *
36740  * Originally Released Under LGPL - original licence link has changed is not relivant.
36741  *
36742  * Fork - LGPL
36743  * <script type="text/javascript">
36744  */
36745 /**
36746  * @class Roo.tree.TreeEditor
36747  * @extends Roo.Editor
36748  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36749  * as the editor field.
36750  * @constructor
36751  * @param {Object} config (used to be the tree panel.)
36752  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36753  * 
36754  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36755  * @cfg {Roo.form.TextField|Object} field The field configuration
36756  *
36757  * 
36758  */
36759 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36760     var tree = config;
36761     var field;
36762     if (oldconfig) { // old style..
36763         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36764     } else {
36765         // new style..
36766         tree = config.tree;
36767         config.field = config.field  || {};
36768         config.field.xtype = 'TextField';
36769         field = Roo.factory(config.field, Roo.form);
36770     }
36771     config = config || {};
36772     
36773     
36774     this.addEvents({
36775         /**
36776          * @event beforenodeedit
36777          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36778          * false from the handler of this event.
36779          * @param {Editor} this
36780          * @param {Roo.tree.Node} node 
36781          */
36782         "beforenodeedit" : true
36783     });
36784     
36785     //Roo.log(config);
36786     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36787
36788     this.tree = tree;
36789
36790     tree.on('beforeclick', this.beforeNodeClick, this);
36791     tree.getTreeEl().on('mousedown', this.hide, this);
36792     this.on('complete', this.updateNode, this);
36793     this.on('beforestartedit', this.fitToTree, this);
36794     this.on('startedit', this.bindScroll, this, {delay:10});
36795     this.on('specialkey', this.onSpecialKey, this);
36796 };
36797
36798 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36799     /**
36800      * @cfg {String} alignment
36801      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36802      */
36803     alignment: "l-l",
36804     // inherit
36805     autoSize: false,
36806     /**
36807      * @cfg {Boolean} hideEl
36808      * True to hide the bound element while the editor is displayed (defaults to false)
36809      */
36810     hideEl : false,
36811     /**
36812      * @cfg {String} cls
36813      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36814      */
36815     cls: "x-small-editor x-tree-editor",
36816     /**
36817      * @cfg {Boolean} shim
36818      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36819      */
36820     shim:false,
36821     // inherit
36822     shadow:"frame",
36823     /**
36824      * @cfg {Number} maxWidth
36825      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36826      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36827      * scroll and client offsets into account prior to each edit.
36828      */
36829     maxWidth: 250,
36830
36831     editDelay : 350,
36832
36833     // private
36834     fitToTree : function(ed, el){
36835         var td = this.tree.getTreeEl().dom, nd = el.dom;
36836         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36837             td.scrollLeft = nd.offsetLeft;
36838         }
36839         var w = Math.min(
36840                 this.maxWidth,
36841                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36842         this.setSize(w, '');
36843         
36844         return this.fireEvent('beforenodeedit', this, this.editNode);
36845         
36846     },
36847
36848     // private
36849     triggerEdit : function(node){
36850         this.completeEdit();
36851         this.editNode = node;
36852         this.startEdit(node.ui.textNode, node.text);
36853     },
36854
36855     // private
36856     bindScroll : function(){
36857         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36858     },
36859
36860     // private
36861     beforeNodeClick : function(node, e){
36862         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36863         this.lastClick = new Date();
36864         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36865             e.stopEvent();
36866             this.triggerEdit(node);
36867             return false;
36868         }
36869         return true;
36870     },
36871
36872     // private
36873     updateNode : function(ed, value){
36874         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36875         this.editNode.setText(value);
36876     },
36877
36878     // private
36879     onHide : function(){
36880         Roo.tree.TreeEditor.superclass.onHide.call(this);
36881         if(this.editNode){
36882             this.editNode.ui.focus();
36883         }
36884     },
36885
36886     // private
36887     onSpecialKey : function(field, e){
36888         var k = e.getKey();
36889         if(k == e.ESC){
36890             e.stopEvent();
36891             this.cancelEdit();
36892         }else if(k == e.ENTER && !e.hasModifier()){
36893             e.stopEvent();
36894             this.completeEdit();
36895         }
36896     }
36897 });//<Script type="text/javascript">
36898 /*
36899  * Based on:
36900  * Ext JS Library 1.1.1
36901  * Copyright(c) 2006-2007, Ext JS, LLC.
36902  *
36903  * Originally Released Under LGPL - original licence link has changed is not relivant.
36904  *
36905  * Fork - LGPL
36906  * <script type="text/javascript">
36907  */
36908  
36909 /**
36910  * Not documented??? - probably should be...
36911  */
36912
36913 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36914     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36915     
36916     renderElements : function(n, a, targetNode, bulkRender){
36917         //consel.log("renderElements?");
36918         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36919
36920         var t = n.getOwnerTree();
36921         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36922         
36923         var cols = t.columns;
36924         var bw = t.borderWidth;
36925         var c = cols[0];
36926         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36927          var cb = typeof a.checked == "boolean";
36928         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36929         var colcls = 'x-t-' + tid + '-c0';
36930         var buf = [
36931             '<li class="x-tree-node">',
36932             
36933                 
36934                 '<div class="x-tree-node-el ', a.cls,'">',
36935                     // extran...
36936                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36937                 
36938                 
36939                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36940                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36941                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36942                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36943                            (a.iconCls ? ' '+a.iconCls : ''),
36944                            '" unselectable="on" />',
36945                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36946                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36947                              
36948                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36949                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36950                             '<span unselectable="on" qtip="' + tx + '">',
36951                              tx,
36952                              '</span></a>' ,
36953                     '</div>',
36954                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36955                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36956                  ];
36957         for(var i = 1, len = cols.length; i < len; i++){
36958             c = cols[i];
36959             colcls = 'x-t-' + tid + '-c' +i;
36960             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36961             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36962                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36963                       "</div>");
36964          }
36965          
36966          buf.push(
36967             '</a>',
36968             '<div class="x-clear"></div></div>',
36969             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36970             "</li>");
36971         
36972         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36973             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36974                                 n.nextSibling.ui.getEl(), buf.join(""));
36975         }else{
36976             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36977         }
36978         var el = this.wrap.firstChild;
36979         this.elRow = el;
36980         this.elNode = el.firstChild;
36981         this.ranchor = el.childNodes[1];
36982         this.ctNode = this.wrap.childNodes[1];
36983         var cs = el.firstChild.childNodes;
36984         this.indentNode = cs[0];
36985         this.ecNode = cs[1];
36986         this.iconNode = cs[2];
36987         var index = 3;
36988         if(cb){
36989             this.checkbox = cs[3];
36990             index++;
36991         }
36992         this.anchor = cs[index];
36993         
36994         this.textNode = cs[index].firstChild;
36995         
36996         //el.on("click", this.onClick, this);
36997         //el.on("dblclick", this.onDblClick, this);
36998         
36999         
37000        // console.log(this);
37001     },
37002     initEvents : function(){
37003         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37004         
37005             
37006         var a = this.ranchor;
37007
37008         var el = Roo.get(a);
37009
37010         if(Roo.isOpera){ // opera render bug ignores the CSS
37011             el.setStyle("text-decoration", "none");
37012         }
37013
37014         el.on("click", this.onClick, this);
37015         el.on("dblclick", this.onDblClick, this);
37016         el.on("contextmenu", this.onContextMenu, this);
37017         
37018     },
37019     
37020     /*onSelectedChange : function(state){
37021         if(state){
37022             this.focus();
37023             this.addClass("x-tree-selected");
37024         }else{
37025             //this.blur();
37026             this.removeClass("x-tree-selected");
37027         }
37028     },*/
37029     addClass : function(cls){
37030         if(this.elRow){
37031             Roo.fly(this.elRow).addClass(cls);
37032         }
37033         
37034     },
37035     
37036     
37037     removeClass : function(cls){
37038         if(this.elRow){
37039             Roo.fly(this.elRow).removeClass(cls);
37040         }
37041     }
37042
37043     
37044     
37045 });//<Script type="text/javascript">
37046
37047 /*
37048  * Based on:
37049  * Ext JS Library 1.1.1
37050  * Copyright(c) 2006-2007, Ext JS, LLC.
37051  *
37052  * Originally Released Under LGPL - original licence link has changed is not relivant.
37053  *
37054  * Fork - LGPL
37055  * <script type="text/javascript">
37056  */
37057  
37058
37059 /**
37060  * @class Roo.tree.ColumnTree
37061  * @extends Roo.data.TreePanel
37062  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37063  * @cfg {int} borderWidth  compined right/left border allowance
37064  * @constructor
37065  * @param {String/HTMLElement/Element} el The container element
37066  * @param {Object} config
37067  */
37068 Roo.tree.ColumnTree =  function(el, config)
37069 {
37070    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37071    this.addEvents({
37072         /**
37073         * @event resize
37074         * Fire this event on a container when it resizes
37075         * @param {int} w Width
37076         * @param {int} h Height
37077         */
37078        "resize" : true
37079     });
37080     this.on('resize', this.onResize, this);
37081 };
37082
37083 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37084     //lines:false,
37085     
37086     
37087     borderWidth: Roo.isBorderBox ? 0 : 2, 
37088     headEls : false,
37089     
37090     render : function(){
37091         // add the header.....
37092        
37093         Roo.tree.ColumnTree.superclass.render.apply(this);
37094         
37095         this.el.addClass('x-column-tree');
37096         
37097         this.headers = this.el.createChild(
37098             {cls:'x-tree-headers'},this.innerCt.dom);
37099    
37100         var cols = this.columns, c;
37101         var totalWidth = 0;
37102         this.headEls = [];
37103         var  len = cols.length;
37104         for(var i = 0; i < len; i++){
37105              c = cols[i];
37106              totalWidth += c.width;
37107             this.headEls.push(this.headers.createChild({
37108                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37109                  cn: {
37110                      cls:'x-tree-hd-text',
37111                      html: c.header
37112                  },
37113                  style:'width:'+(c.width-this.borderWidth)+'px;'
37114              }));
37115         }
37116         this.headers.createChild({cls:'x-clear'});
37117         // prevent floats from wrapping when clipped
37118         this.headers.setWidth(totalWidth);
37119         //this.innerCt.setWidth(totalWidth);
37120         this.innerCt.setStyle({ overflow: 'auto' });
37121         this.onResize(this.width, this.height);
37122              
37123         
37124     },
37125     onResize : function(w,h)
37126     {
37127         this.height = h;
37128         this.width = w;
37129         // resize cols..
37130         this.innerCt.setWidth(this.width);
37131         this.innerCt.setHeight(this.height-20);
37132         
37133         // headers...
37134         var cols = this.columns, c;
37135         var totalWidth = 0;
37136         var expEl = false;
37137         var len = cols.length;
37138         for(var i = 0; i < len; i++){
37139             c = cols[i];
37140             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37141                 // it's the expander..
37142                 expEl  = this.headEls[i];
37143                 continue;
37144             }
37145             totalWidth += c.width;
37146             
37147         }
37148         if (expEl) {
37149             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37150         }
37151         this.headers.setWidth(w-20);
37152
37153         
37154         
37155         
37156     }
37157 });
37158 /*
37159  * Based on:
37160  * Ext JS Library 1.1.1
37161  * Copyright(c) 2006-2007, Ext JS, LLC.
37162  *
37163  * Originally Released Under LGPL - original licence link has changed is not relivant.
37164  *
37165  * Fork - LGPL
37166  * <script type="text/javascript">
37167  */
37168  
37169 /**
37170  * @class Roo.menu.Menu
37171  * @extends Roo.util.Observable
37172  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37173  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37174  * @constructor
37175  * Creates a new Menu
37176  * @param {Object} config Configuration options
37177  */
37178 Roo.menu.Menu = function(config){
37179     Roo.apply(this, config);
37180     this.id = this.id || Roo.id();
37181     this.addEvents({
37182         /**
37183          * @event beforeshow
37184          * Fires before this menu is displayed
37185          * @param {Roo.menu.Menu} this
37186          */
37187         beforeshow : true,
37188         /**
37189          * @event beforehide
37190          * Fires before this menu is hidden
37191          * @param {Roo.menu.Menu} this
37192          */
37193         beforehide : true,
37194         /**
37195          * @event show
37196          * Fires after this menu is displayed
37197          * @param {Roo.menu.Menu} this
37198          */
37199         show : true,
37200         /**
37201          * @event hide
37202          * Fires after this menu is hidden
37203          * @param {Roo.menu.Menu} this
37204          */
37205         hide : true,
37206         /**
37207          * @event click
37208          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37209          * @param {Roo.menu.Menu} this
37210          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37211          * @param {Roo.EventObject} e
37212          */
37213         click : true,
37214         /**
37215          * @event mouseover
37216          * Fires when the mouse is hovering over this menu
37217          * @param {Roo.menu.Menu} this
37218          * @param {Roo.EventObject} e
37219          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37220          */
37221         mouseover : true,
37222         /**
37223          * @event mouseout
37224          * Fires when the mouse exits this menu
37225          * @param {Roo.menu.Menu} this
37226          * @param {Roo.EventObject} e
37227          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37228          */
37229         mouseout : true,
37230         /**
37231          * @event itemclick
37232          * Fires when a menu item contained in this menu is clicked
37233          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37234          * @param {Roo.EventObject} e
37235          */
37236         itemclick: true
37237     });
37238     if (this.registerMenu) {
37239         Roo.menu.MenuMgr.register(this);
37240     }
37241     
37242     var mis = this.items;
37243     this.items = new Roo.util.MixedCollection();
37244     if(mis){
37245         this.add.apply(this, mis);
37246     }
37247 };
37248
37249 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37250     /**
37251      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37252      */
37253     minWidth : 120,
37254     /**
37255      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37256      * for bottom-right shadow (defaults to "sides")
37257      */
37258     shadow : "sides",
37259     /**
37260      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37261      * this menu (defaults to "tl-tr?")
37262      */
37263     subMenuAlign : "tl-tr?",
37264     /**
37265      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37266      * relative to its element of origin (defaults to "tl-bl?")
37267      */
37268     defaultAlign : "tl-bl?",
37269     /**
37270      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37271      */
37272     allowOtherMenus : false,
37273     /**
37274      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37275      */
37276     registerMenu : true,
37277
37278     hidden:true,
37279
37280     // private
37281     render : function(){
37282         if(this.el){
37283             return;
37284         }
37285         var el = this.el = new Roo.Layer({
37286             cls: "x-menu",
37287             shadow:this.shadow,
37288             constrain: false,
37289             parentEl: this.parentEl || document.body,
37290             zindex:15000
37291         });
37292
37293         this.keyNav = new Roo.menu.MenuNav(this);
37294
37295         if(this.plain){
37296             el.addClass("x-menu-plain");
37297         }
37298         if(this.cls){
37299             el.addClass(this.cls);
37300         }
37301         // generic focus element
37302         this.focusEl = el.createChild({
37303             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37304         });
37305         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37306         //disabling touch- as it's causing issues ..
37307         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37308         ul.on('click'   , this.onClick, this);
37309         
37310         
37311         ul.on("mouseover", this.onMouseOver, this);
37312         ul.on("mouseout", this.onMouseOut, this);
37313         this.items.each(function(item){
37314             if (item.hidden) {
37315                 return;
37316             }
37317             
37318             var li = document.createElement("li");
37319             li.className = "x-menu-list-item";
37320             ul.dom.appendChild(li);
37321             item.render(li, this);
37322         }, this);
37323         this.ul = ul;
37324         this.autoWidth();
37325     },
37326
37327     // private
37328     autoWidth : function(){
37329         var el = this.el, ul = this.ul;
37330         if(!el){
37331             return;
37332         }
37333         var w = this.width;
37334         if(w){
37335             el.setWidth(w);
37336         }else if(Roo.isIE){
37337             el.setWidth(this.minWidth);
37338             var t = el.dom.offsetWidth; // force recalc
37339             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37340         }
37341     },
37342
37343     // private
37344     delayAutoWidth : function(){
37345         if(this.rendered){
37346             if(!this.awTask){
37347                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37348             }
37349             this.awTask.delay(20);
37350         }
37351     },
37352
37353     // private
37354     findTargetItem : function(e){
37355         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37356         if(t && t.menuItemId){
37357             return this.items.get(t.menuItemId);
37358         }
37359     },
37360
37361     // private
37362     onClick : function(e){
37363         Roo.log("menu.onClick");
37364         var t = this.findTargetItem(e);
37365         if(!t){
37366             return;
37367         }
37368         Roo.log(e);
37369         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37370             if(t == this.activeItem && t.shouldDeactivate(e)){
37371                 this.activeItem.deactivate();
37372                 delete this.activeItem;
37373                 return;
37374             }
37375             if(t.canActivate){
37376                 this.setActiveItem(t, true);
37377             }
37378             return;
37379             
37380             
37381         }
37382         
37383         t.onClick(e);
37384         this.fireEvent("click", this, t, e);
37385     },
37386
37387     // private
37388     setActiveItem : function(item, autoExpand){
37389         if(item != this.activeItem){
37390             if(this.activeItem){
37391                 this.activeItem.deactivate();
37392             }
37393             this.activeItem = item;
37394             item.activate(autoExpand);
37395         }else if(autoExpand){
37396             item.expandMenu();
37397         }
37398     },
37399
37400     // private
37401     tryActivate : function(start, step){
37402         var items = this.items;
37403         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37404             var item = items.get(i);
37405             if(!item.disabled && item.canActivate){
37406                 this.setActiveItem(item, false);
37407                 return item;
37408             }
37409         }
37410         return false;
37411     },
37412
37413     // private
37414     onMouseOver : function(e){
37415         var t;
37416         if(t = this.findTargetItem(e)){
37417             if(t.canActivate && !t.disabled){
37418                 this.setActiveItem(t, true);
37419             }
37420         }
37421         this.fireEvent("mouseover", this, e, t);
37422     },
37423
37424     // private
37425     onMouseOut : function(e){
37426         var t;
37427         if(t = this.findTargetItem(e)){
37428             if(t == this.activeItem && t.shouldDeactivate(e)){
37429                 this.activeItem.deactivate();
37430                 delete this.activeItem;
37431             }
37432         }
37433         this.fireEvent("mouseout", this, e, t);
37434     },
37435
37436     /**
37437      * Read-only.  Returns true if the menu is currently displayed, else false.
37438      * @type Boolean
37439      */
37440     isVisible : function(){
37441         return this.el && !this.hidden;
37442     },
37443
37444     /**
37445      * Displays this menu relative to another element
37446      * @param {String/HTMLElement/Roo.Element} element The element to align to
37447      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37448      * the element (defaults to this.defaultAlign)
37449      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37450      */
37451     show : function(el, pos, parentMenu){
37452         this.parentMenu = parentMenu;
37453         if(!this.el){
37454             this.render();
37455         }
37456         this.fireEvent("beforeshow", this);
37457         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37458     },
37459
37460     /**
37461      * Displays this menu at a specific xy position
37462      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37463      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37464      */
37465     showAt : function(xy, parentMenu, /* private: */_e){
37466         this.parentMenu = parentMenu;
37467         if(!this.el){
37468             this.render();
37469         }
37470         if(_e !== false){
37471             this.fireEvent("beforeshow", this);
37472             xy = this.el.adjustForConstraints(xy);
37473         }
37474         this.el.setXY(xy);
37475         this.el.show();
37476         this.hidden = false;
37477         this.focus();
37478         this.fireEvent("show", this);
37479     },
37480
37481     focus : function(){
37482         if(!this.hidden){
37483             this.doFocus.defer(50, this);
37484         }
37485     },
37486
37487     doFocus : function(){
37488         if(!this.hidden){
37489             this.focusEl.focus();
37490         }
37491     },
37492
37493     /**
37494      * Hides this menu and optionally all parent menus
37495      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37496      */
37497     hide : function(deep){
37498         if(this.el && this.isVisible()){
37499             this.fireEvent("beforehide", this);
37500             if(this.activeItem){
37501                 this.activeItem.deactivate();
37502                 this.activeItem = null;
37503             }
37504             this.el.hide();
37505             this.hidden = true;
37506             this.fireEvent("hide", this);
37507         }
37508         if(deep === true && this.parentMenu){
37509             this.parentMenu.hide(true);
37510         }
37511     },
37512
37513     /**
37514      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37515      * Any of the following are valid:
37516      * <ul>
37517      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37518      * <li>An HTMLElement object which will be converted to a menu item</li>
37519      * <li>A menu item config object that will be created as a new menu item</li>
37520      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37521      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37522      * </ul>
37523      * Usage:
37524      * <pre><code>
37525 // Create the menu
37526 var menu = new Roo.menu.Menu();
37527
37528 // Create a menu item to add by reference
37529 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37530
37531 // Add a bunch of items at once using different methods.
37532 // Only the last item added will be returned.
37533 var item = menu.add(
37534     menuItem,                // add existing item by ref
37535     'Dynamic Item',          // new TextItem
37536     '-',                     // new separator
37537     { text: 'Config Item' }  // new item by config
37538 );
37539 </code></pre>
37540      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37541      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37542      */
37543     add : function(){
37544         var a = arguments, l = a.length, item;
37545         for(var i = 0; i < l; i++){
37546             var el = a[i];
37547             if ((typeof(el) == "object") && el.xtype && el.xns) {
37548                 el = Roo.factory(el, Roo.menu);
37549             }
37550             
37551             if(el.render){ // some kind of Item
37552                 item = this.addItem(el);
37553             }else if(typeof el == "string"){ // string
37554                 if(el == "separator" || el == "-"){
37555                     item = this.addSeparator();
37556                 }else{
37557                     item = this.addText(el);
37558                 }
37559             }else if(el.tagName || el.el){ // element
37560                 item = this.addElement(el);
37561             }else if(typeof el == "object"){ // must be menu item config?
37562                 item = this.addMenuItem(el);
37563             }
37564         }
37565         return item;
37566     },
37567
37568     /**
37569      * Returns this menu's underlying {@link Roo.Element} object
37570      * @return {Roo.Element} The element
37571      */
37572     getEl : function(){
37573         if(!this.el){
37574             this.render();
37575         }
37576         return this.el;
37577     },
37578
37579     /**
37580      * Adds a separator bar to the menu
37581      * @return {Roo.menu.Item} The menu item that was added
37582      */
37583     addSeparator : function(){
37584         return this.addItem(new Roo.menu.Separator());
37585     },
37586
37587     /**
37588      * Adds an {@link Roo.Element} object to the menu
37589      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37590      * @return {Roo.menu.Item} The menu item that was added
37591      */
37592     addElement : function(el){
37593         return this.addItem(new Roo.menu.BaseItem(el));
37594     },
37595
37596     /**
37597      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37598      * @param {Roo.menu.Item} item The menu item to add
37599      * @return {Roo.menu.Item} The menu item that was added
37600      */
37601     addItem : function(item){
37602         this.items.add(item);
37603         if(this.ul){
37604             var li = document.createElement("li");
37605             li.className = "x-menu-list-item";
37606             this.ul.dom.appendChild(li);
37607             item.render(li, this);
37608             this.delayAutoWidth();
37609         }
37610         return item;
37611     },
37612
37613     /**
37614      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37615      * @param {Object} config A MenuItem config object
37616      * @return {Roo.menu.Item} The menu item that was added
37617      */
37618     addMenuItem : function(config){
37619         if(!(config instanceof Roo.menu.Item)){
37620             if(typeof config.checked == "boolean"){ // must be check menu item config?
37621                 config = new Roo.menu.CheckItem(config);
37622             }else{
37623                 config = new Roo.menu.Item(config);
37624             }
37625         }
37626         return this.addItem(config);
37627     },
37628
37629     /**
37630      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37631      * @param {String} text The text to display in the menu item
37632      * @return {Roo.menu.Item} The menu item that was added
37633      */
37634     addText : function(text){
37635         return this.addItem(new Roo.menu.TextItem({ text : text }));
37636     },
37637
37638     /**
37639      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37640      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37641      * @param {Roo.menu.Item} item The menu item to add
37642      * @return {Roo.menu.Item} The menu item that was added
37643      */
37644     insert : function(index, item){
37645         this.items.insert(index, item);
37646         if(this.ul){
37647             var li = document.createElement("li");
37648             li.className = "x-menu-list-item";
37649             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37650             item.render(li, this);
37651             this.delayAutoWidth();
37652         }
37653         return item;
37654     },
37655
37656     /**
37657      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37658      * @param {Roo.menu.Item} item The menu item to remove
37659      */
37660     remove : function(item){
37661         this.items.removeKey(item.id);
37662         item.destroy();
37663     },
37664
37665     /**
37666      * Removes and destroys all items in the menu
37667      */
37668     removeAll : function(){
37669         var f;
37670         while(f = this.items.first()){
37671             this.remove(f);
37672         }
37673     }
37674 });
37675
37676 // MenuNav is a private utility class used internally by the Menu
37677 Roo.menu.MenuNav = function(menu){
37678     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37679     this.scope = this.menu = menu;
37680 };
37681
37682 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37683     doRelay : function(e, h){
37684         var k = e.getKey();
37685         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37686             this.menu.tryActivate(0, 1);
37687             return false;
37688         }
37689         return h.call(this.scope || this, e, this.menu);
37690     },
37691
37692     up : function(e, m){
37693         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37694             m.tryActivate(m.items.length-1, -1);
37695         }
37696     },
37697
37698     down : function(e, m){
37699         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37700             m.tryActivate(0, 1);
37701         }
37702     },
37703
37704     right : function(e, m){
37705         if(m.activeItem){
37706             m.activeItem.expandMenu(true);
37707         }
37708     },
37709
37710     left : function(e, m){
37711         m.hide();
37712         if(m.parentMenu && m.parentMenu.activeItem){
37713             m.parentMenu.activeItem.activate();
37714         }
37715     },
37716
37717     enter : function(e, m){
37718         if(m.activeItem){
37719             e.stopPropagation();
37720             m.activeItem.onClick(e);
37721             m.fireEvent("click", this, m.activeItem);
37722             return true;
37723         }
37724     }
37725 });/*
37726  * Based on:
37727  * Ext JS Library 1.1.1
37728  * Copyright(c) 2006-2007, Ext JS, LLC.
37729  *
37730  * Originally Released Under LGPL - original licence link has changed is not relivant.
37731  *
37732  * Fork - LGPL
37733  * <script type="text/javascript">
37734  */
37735  
37736 /**
37737  * @class Roo.menu.MenuMgr
37738  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37739  * @singleton
37740  */
37741 Roo.menu.MenuMgr = function(){
37742    var menus, active, groups = {}, attached = false, lastShow = new Date();
37743
37744    // private - called when first menu is created
37745    function init(){
37746        menus = {};
37747        active = new Roo.util.MixedCollection();
37748        Roo.get(document).addKeyListener(27, function(){
37749            if(active.length > 0){
37750                hideAll();
37751            }
37752        });
37753    }
37754
37755    // private
37756    function hideAll(){
37757        if(active && active.length > 0){
37758            var c = active.clone();
37759            c.each(function(m){
37760                m.hide();
37761            });
37762        }
37763    }
37764
37765    // private
37766    function onHide(m){
37767        active.remove(m);
37768        if(active.length < 1){
37769            Roo.get(document).un("mousedown", onMouseDown);
37770            attached = false;
37771        }
37772    }
37773
37774    // private
37775    function onShow(m){
37776        var last = active.last();
37777        lastShow = new Date();
37778        active.add(m);
37779        if(!attached){
37780            Roo.get(document).on("mousedown", onMouseDown);
37781            attached = true;
37782        }
37783        if(m.parentMenu){
37784           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37785           m.parentMenu.activeChild = m;
37786        }else if(last && last.isVisible()){
37787           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37788        }
37789    }
37790
37791    // private
37792    function onBeforeHide(m){
37793        if(m.activeChild){
37794            m.activeChild.hide();
37795        }
37796        if(m.autoHideTimer){
37797            clearTimeout(m.autoHideTimer);
37798            delete m.autoHideTimer;
37799        }
37800    }
37801
37802    // private
37803    function onBeforeShow(m){
37804        var pm = m.parentMenu;
37805        if(!pm && !m.allowOtherMenus){
37806            hideAll();
37807        }else if(pm && pm.activeChild && active != m){
37808            pm.activeChild.hide();
37809        }
37810    }
37811
37812    // private
37813    function onMouseDown(e){
37814        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37815            hideAll();
37816        }
37817    }
37818
37819    // private
37820    function onBeforeCheck(mi, state){
37821        if(state){
37822            var g = groups[mi.group];
37823            for(var i = 0, l = g.length; i < l; i++){
37824                if(g[i] != mi){
37825                    g[i].setChecked(false);
37826                }
37827            }
37828        }
37829    }
37830
37831    return {
37832
37833        /**
37834         * Hides all menus that are currently visible
37835         */
37836        hideAll : function(){
37837             hideAll();  
37838        },
37839
37840        // private
37841        register : function(menu){
37842            if(!menus){
37843                init();
37844            }
37845            menus[menu.id] = menu;
37846            menu.on("beforehide", onBeforeHide);
37847            menu.on("hide", onHide);
37848            menu.on("beforeshow", onBeforeShow);
37849            menu.on("show", onShow);
37850            var g = menu.group;
37851            if(g && menu.events["checkchange"]){
37852                if(!groups[g]){
37853                    groups[g] = [];
37854                }
37855                groups[g].push(menu);
37856                menu.on("checkchange", onCheck);
37857            }
37858        },
37859
37860         /**
37861          * Returns a {@link Roo.menu.Menu} object
37862          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37863          * be used to generate and return a new Menu instance.
37864          */
37865        get : function(menu){
37866            if(typeof menu == "string"){ // menu id
37867                return menus[menu];
37868            }else if(menu.events){  // menu instance
37869                return menu;
37870            }else if(typeof menu.length == 'number'){ // array of menu items?
37871                return new Roo.menu.Menu({items:menu});
37872            }else{ // otherwise, must be a config
37873                return new Roo.menu.Menu(menu);
37874            }
37875        },
37876
37877        // private
37878        unregister : function(menu){
37879            delete menus[menu.id];
37880            menu.un("beforehide", onBeforeHide);
37881            menu.un("hide", onHide);
37882            menu.un("beforeshow", onBeforeShow);
37883            menu.un("show", onShow);
37884            var g = menu.group;
37885            if(g && menu.events["checkchange"]){
37886                groups[g].remove(menu);
37887                menu.un("checkchange", onCheck);
37888            }
37889        },
37890
37891        // private
37892        registerCheckable : function(menuItem){
37893            var g = menuItem.group;
37894            if(g){
37895                if(!groups[g]){
37896                    groups[g] = [];
37897                }
37898                groups[g].push(menuItem);
37899                menuItem.on("beforecheckchange", onBeforeCheck);
37900            }
37901        },
37902
37903        // private
37904        unregisterCheckable : function(menuItem){
37905            var g = menuItem.group;
37906            if(g){
37907                groups[g].remove(menuItem);
37908                menuItem.un("beforecheckchange", onBeforeCheck);
37909            }
37910        }
37911    };
37912 }();/*
37913  * Based on:
37914  * Ext JS Library 1.1.1
37915  * Copyright(c) 2006-2007, Ext JS, LLC.
37916  *
37917  * Originally Released Under LGPL - original licence link has changed is not relivant.
37918  *
37919  * Fork - LGPL
37920  * <script type="text/javascript">
37921  */
37922  
37923
37924 /**
37925  * @class Roo.menu.BaseItem
37926  * @extends Roo.Component
37927  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37928  * management and base configuration options shared by all menu components.
37929  * @constructor
37930  * Creates a new BaseItem
37931  * @param {Object} config Configuration options
37932  */
37933 Roo.menu.BaseItem = function(config){
37934     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37935
37936     this.addEvents({
37937         /**
37938          * @event click
37939          * Fires when this item is clicked
37940          * @param {Roo.menu.BaseItem} this
37941          * @param {Roo.EventObject} e
37942          */
37943         click: true,
37944         /**
37945          * @event activate
37946          * Fires when this item is activated
37947          * @param {Roo.menu.BaseItem} this
37948          */
37949         activate : true,
37950         /**
37951          * @event deactivate
37952          * Fires when this item is deactivated
37953          * @param {Roo.menu.BaseItem} this
37954          */
37955         deactivate : true
37956     });
37957
37958     if(this.handler){
37959         this.on("click", this.handler, this.scope, true);
37960     }
37961 };
37962
37963 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37964     /**
37965      * @cfg {Function} handler
37966      * A function that will handle the click event of this menu item (defaults to undefined)
37967      */
37968     /**
37969      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37970      */
37971     canActivate : false,
37972     
37973      /**
37974      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37975      */
37976     hidden: false,
37977     
37978     /**
37979      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37980      */
37981     activeClass : "x-menu-item-active",
37982     /**
37983      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37984      */
37985     hideOnClick : true,
37986     /**
37987      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37988      */
37989     hideDelay : 100,
37990
37991     // private
37992     ctype: "Roo.menu.BaseItem",
37993
37994     // private
37995     actionMode : "container",
37996
37997     // private
37998     render : function(container, parentMenu){
37999         this.parentMenu = parentMenu;
38000         Roo.menu.BaseItem.superclass.render.call(this, container);
38001         this.container.menuItemId = this.id;
38002     },
38003
38004     // private
38005     onRender : function(container, position){
38006         this.el = Roo.get(this.el);
38007         container.dom.appendChild(this.el.dom);
38008     },
38009
38010     // private
38011     onClick : function(e){
38012         if(!this.disabled && this.fireEvent("click", this, e) !== false
38013                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38014             this.handleClick(e);
38015         }else{
38016             e.stopEvent();
38017         }
38018     },
38019
38020     // private
38021     activate : function(){
38022         if(this.disabled){
38023             return false;
38024         }
38025         var li = this.container;
38026         li.addClass(this.activeClass);
38027         this.region = li.getRegion().adjust(2, 2, -2, -2);
38028         this.fireEvent("activate", this);
38029         return true;
38030     },
38031
38032     // private
38033     deactivate : function(){
38034         this.container.removeClass(this.activeClass);
38035         this.fireEvent("deactivate", this);
38036     },
38037
38038     // private
38039     shouldDeactivate : function(e){
38040         return !this.region || !this.region.contains(e.getPoint());
38041     },
38042
38043     // private
38044     handleClick : function(e){
38045         if(this.hideOnClick){
38046             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38047         }
38048     },
38049
38050     // private
38051     expandMenu : function(autoActivate){
38052         // do nothing
38053     },
38054
38055     // private
38056     hideMenu : function(){
38057         // do nothing
38058     }
38059 });/*
38060  * Based on:
38061  * Ext JS Library 1.1.1
38062  * Copyright(c) 2006-2007, Ext JS, LLC.
38063  *
38064  * Originally Released Under LGPL - original licence link has changed is not relivant.
38065  *
38066  * Fork - LGPL
38067  * <script type="text/javascript">
38068  */
38069  
38070 /**
38071  * @class Roo.menu.Adapter
38072  * @extends Roo.menu.BaseItem
38073  * 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.
38074  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38075  * @constructor
38076  * Creates a new Adapter
38077  * @param {Object} config Configuration options
38078  */
38079 Roo.menu.Adapter = function(component, config){
38080     Roo.menu.Adapter.superclass.constructor.call(this, config);
38081     this.component = component;
38082 };
38083 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38084     // private
38085     canActivate : true,
38086
38087     // private
38088     onRender : function(container, position){
38089         this.component.render(container);
38090         this.el = this.component.getEl();
38091     },
38092
38093     // private
38094     activate : function(){
38095         if(this.disabled){
38096             return false;
38097         }
38098         this.component.focus();
38099         this.fireEvent("activate", this);
38100         return true;
38101     },
38102
38103     // private
38104     deactivate : function(){
38105         this.fireEvent("deactivate", this);
38106     },
38107
38108     // private
38109     disable : function(){
38110         this.component.disable();
38111         Roo.menu.Adapter.superclass.disable.call(this);
38112     },
38113
38114     // private
38115     enable : function(){
38116         this.component.enable();
38117         Roo.menu.Adapter.superclass.enable.call(this);
38118     }
38119 });/*
38120  * Based on:
38121  * Ext JS Library 1.1.1
38122  * Copyright(c) 2006-2007, Ext JS, LLC.
38123  *
38124  * Originally Released Under LGPL - original licence link has changed is not relivant.
38125  *
38126  * Fork - LGPL
38127  * <script type="text/javascript">
38128  */
38129
38130 /**
38131  * @class Roo.menu.TextItem
38132  * @extends Roo.menu.BaseItem
38133  * Adds a static text string to a menu, usually used as either a heading or group separator.
38134  * Note: old style constructor with text is still supported.
38135  * 
38136  * @constructor
38137  * Creates a new TextItem
38138  * @param {Object} cfg Configuration
38139  */
38140 Roo.menu.TextItem = function(cfg){
38141     if (typeof(cfg) == 'string') {
38142         this.text = cfg;
38143     } else {
38144         Roo.apply(this,cfg);
38145     }
38146     
38147     Roo.menu.TextItem.superclass.constructor.call(this);
38148 };
38149
38150 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38151     /**
38152      * @cfg {Boolean} text Text to show on item.
38153      */
38154     text : '',
38155     
38156     /**
38157      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38158      */
38159     hideOnClick : false,
38160     /**
38161      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38162      */
38163     itemCls : "x-menu-text",
38164
38165     // private
38166     onRender : function(){
38167         var s = document.createElement("span");
38168         s.className = this.itemCls;
38169         s.innerHTML = this.text;
38170         this.el = s;
38171         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38172     }
38173 });/*
38174  * Based on:
38175  * Ext JS Library 1.1.1
38176  * Copyright(c) 2006-2007, Ext JS, LLC.
38177  *
38178  * Originally Released Under LGPL - original licence link has changed is not relivant.
38179  *
38180  * Fork - LGPL
38181  * <script type="text/javascript">
38182  */
38183
38184 /**
38185  * @class Roo.menu.Separator
38186  * @extends Roo.menu.BaseItem
38187  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38188  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38189  * @constructor
38190  * @param {Object} config Configuration options
38191  */
38192 Roo.menu.Separator = function(config){
38193     Roo.menu.Separator.superclass.constructor.call(this, config);
38194 };
38195
38196 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38197     /**
38198      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38199      */
38200     itemCls : "x-menu-sep",
38201     /**
38202      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38203      */
38204     hideOnClick : false,
38205
38206     // private
38207     onRender : function(li){
38208         var s = document.createElement("span");
38209         s.className = this.itemCls;
38210         s.innerHTML = "&#160;";
38211         this.el = s;
38212         li.addClass("x-menu-sep-li");
38213         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38214     }
38215 });/*
38216  * Based on:
38217  * Ext JS Library 1.1.1
38218  * Copyright(c) 2006-2007, Ext JS, LLC.
38219  *
38220  * Originally Released Under LGPL - original licence link has changed is not relivant.
38221  *
38222  * Fork - LGPL
38223  * <script type="text/javascript">
38224  */
38225 /**
38226  * @class Roo.menu.Item
38227  * @extends Roo.menu.BaseItem
38228  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38229  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38230  * activation and click handling.
38231  * @constructor
38232  * Creates a new Item
38233  * @param {Object} config Configuration options
38234  */
38235 Roo.menu.Item = function(config){
38236     Roo.menu.Item.superclass.constructor.call(this, config);
38237     if(this.menu){
38238         this.menu = Roo.menu.MenuMgr.get(this.menu);
38239     }
38240 };
38241 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38242     
38243     /**
38244      * @cfg {String} text
38245      * The text to show on the menu item.
38246      */
38247     text: '',
38248      /**
38249      * @cfg {String} HTML to render in menu
38250      * The text to show on the menu item (HTML version).
38251      */
38252     html: '',
38253     /**
38254      * @cfg {String} icon
38255      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38256      */
38257     icon: undefined,
38258     /**
38259      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38260      */
38261     itemCls : "x-menu-item",
38262     /**
38263      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38264      */
38265     canActivate : true,
38266     /**
38267      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38268      */
38269     showDelay: 200,
38270     // doc'd in BaseItem
38271     hideDelay: 200,
38272
38273     // private
38274     ctype: "Roo.menu.Item",
38275     
38276     // private
38277     onRender : function(container, position){
38278         var el = document.createElement("a");
38279         el.hideFocus = true;
38280         el.unselectable = "on";
38281         el.href = this.href || "#";
38282         if(this.hrefTarget){
38283             el.target = this.hrefTarget;
38284         }
38285         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38286         
38287         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38288         
38289         el.innerHTML = String.format(
38290                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38291                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38292         this.el = el;
38293         Roo.menu.Item.superclass.onRender.call(this, container, position);
38294     },
38295
38296     /**
38297      * Sets the text to display in this menu item
38298      * @param {String} text The text to display
38299      * @param {Boolean} isHTML true to indicate text is pure html.
38300      */
38301     setText : function(text, isHTML){
38302         if (isHTML) {
38303             this.html = text;
38304         } else {
38305             this.text = text;
38306             this.html = '';
38307         }
38308         if(this.rendered){
38309             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38310      
38311             this.el.update(String.format(
38312                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38313                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38314             this.parentMenu.autoWidth();
38315         }
38316     },
38317
38318     // private
38319     handleClick : function(e){
38320         if(!this.href){ // if no link defined, stop the event automatically
38321             e.stopEvent();
38322         }
38323         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38324     },
38325
38326     // private
38327     activate : function(autoExpand){
38328         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38329             this.focus();
38330             if(autoExpand){
38331                 this.expandMenu();
38332             }
38333         }
38334         return true;
38335     },
38336
38337     // private
38338     shouldDeactivate : function(e){
38339         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38340             if(this.menu && this.menu.isVisible()){
38341                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38342             }
38343             return true;
38344         }
38345         return false;
38346     },
38347
38348     // private
38349     deactivate : function(){
38350         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38351         this.hideMenu();
38352     },
38353
38354     // private
38355     expandMenu : function(autoActivate){
38356         if(!this.disabled && this.menu){
38357             clearTimeout(this.hideTimer);
38358             delete this.hideTimer;
38359             if(!this.menu.isVisible() && !this.showTimer){
38360                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38361             }else if (this.menu.isVisible() && autoActivate){
38362                 this.menu.tryActivate(0, 1);
38363             }
38364         }
38365     },
38366
38367     // private
38368     deferExpand : function(autoActivate){
38369         delete this.showTimer;
38370         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38371         if(autoActivate){
38372             this.menu.tryActivate(0, 1);
38373         }
38374     },
38375
38376     // private
38377     hideMenu : function(){
38378         clearTimeout(this.showTimer);
38379         delete this.showTimer;
38380         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38381             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38382         }
38383     },
38384
38385     // private
38386     deferHide : function(){
38387         delete this.hideTimer;
38388         this.menu.hide();
38389     }
38390 });/*
38391  * Based on:
38392  * Ext JS Library 1.1.1
38393  * Copyright(c) 2006-2007, Ext JS, LLC.
38394  *
38395  * Originally Released Under LGPL - original licence link has changed is not relivant.
38396  *
38397  * Fork - LGPL
38398  * <script type="text/javascript">
38399  */
38400  
38401 /**
38402  * @class Roo.menu.CheckItem
38403  * @extends Roo.menu.Item
38404  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38405  * @constructor
38406  * Creates a new CheckItem
38407  * @param {Object} config Configuration options
38408  */
38409 Roo.menu.CheckItem = function(config){
38410     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38411     this.addEvents({
38412         /**
38413          * @event beforecheckchange
38414          * Fires before the checked value is set, providing an opportunity to cancel if needed
38415          * @param {Roo.menu.CheckItem} this
38416          * @param {Boolean} checked The new checked value that will be set
38417          */
38418         "beforecheckchange" : true,
38419         /**
38420          * @event checkchange
38421          * Fires after the checked value has been set
38422          * @param {Roo.menu.CheckItem} this
38423          * @param {Boolean} checked The checked value that was set
38424          */
38425         "checkchange" : true
38426     });
38427     if(this.checkHandler){
38428         this.on('checkchange', this.checkHandler, this.scope);
38429     }
38430 };
38431 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38432     /**
38433      * @cfg {String} group
38434      * All check items with the same group name will automatically be grouped into a single-select
38435      * radio button group (defaults to '')
38436      */
38437     /**
38438      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38439      */
38440     itemCls : "x-menu-item x-menu-check-item",
38441     /**
38442      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38443      */
38444     groupClass : "x-menu-group-item",
38445
38446     /**
38447      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38448      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38449      * initialized with checked = true will be rendered as checked.
38450      */
38451     checked: false,
38452
38453     // private
38454     ctype: "Roo.menu.CheckItem",
38455
38456     // private
38457     onRender : function(c){
38458         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38459         if(this.group){
38460             this.el.addClass(this.groupClass);
38461         }
38462         Roo.menu.MenuMgr.registerCheckable(this);
38463         if(this.checked){
38464             this.checked = false;
38465             this.setChecked(true, true);
38466         }
38467     },
38468
38469     // private
38470     destroy : function(){
38471         if(this.rendered){
38472             Roo.menu.MenuMgr.unregisterCheckable(this);
38473         }
38474         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38475     },
38476
38477     /**
38478      * Set the checked state of this item
38479      * @param {Boolean} checked The new checked value
38480      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38481      */
38482     setChecked : function(state, suppressEvent){
38483         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38484             if(this.container){
38485                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38486             }
38487             this.checked = state;
38488             if(suppressEvent !== true){
38489                 this.fireEvent("checkchange", this, state);
38490             }
38491         }
38492     },
38493
38494     // private
38495     handleClick : function(e){
38496        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38497            this.setChecked(!this.checked);
38498        }
38499        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38500     }
38501 });/*
38502  * Based on:
38503  * Ext JS Library 1.1.1
38504  * Copyright(c) 2006-2007, Ext JS, LLC.
38505  *
38506  * Originally Released Under LGPL - original licence link has changed is not relivant.
38507  *
38508  * Fork - LGPL
38509  * <script type="text/javascript">
38510  */
38511  
38512 /**
38513  * @class Roo.menu.DateItem
38514  * @extends Roo.menu.Adapter
38515  * A menu item that wraps the {@link Roo.DatPicker} component.
38516  * @constructor
38517  * Creates a new DateItem
38518  * @param {Object} config Configuration options
38519  */
38520 Roo.menu.DateItem = function(config){
38521     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38522     /** The Roo.DatePicker object @type Roo.DatePicker */
38523     this.picker = this.component;
38524     this.addEvents({select: true});
38525     
38526     this.picker.on("render", function(picker){
38527         picker.getEl().swallowEvent("click");
38528         picker.container.addClass("x-menu-date-item");
38529     });
38530
38531     this.picker.on("select", this.onSelect, this);
38532 };
38533
38534 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38535     // private
38536     onSelect : function(picker, date){
38537         this.fireEvent("select", this, date, picker);
38538         Roo.menu.DateItem.superclass.handleClick.call(this);
38539     }
38540 });/*
38541  * Based on:
38542  * Ext JS Library 1.1.1
38543  * Copyright(c) 2006-2007, Ext JS, LLC.
38544  *
38545  * Originally Released Under LGPL - original licence link has changed is not relivant.
38546  *
38547  * Fork - LGPL
38548  * <script type="text/javascript">
38549  */
38550  
38551 /**
38552  * @class Roo.menu.ColorItem
38553  * @extends Roo.menu.Adapter
38554  * A menu item that wraps the {@link Roo.ColorPalette} component.
38555  * @constructor
38556  * Creates a new ColorItem
38557  * @param {Object} config Configuration options
38558  */
38559 Roo.menu.ColorItem = function(config){
38560     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38561     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38562     this.palette = this.component;
38563     this.relayEvents(this.palette, ["select"]);
38564     if(this.selectHandler){
38565         this.on('select', this.selectHandler, this.scope);
38566     }
38567 };
38568 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38569  * Based on:
38570  * Ext JS Library 1.1.1
38571  * Copyright(c) 2006-2007, Ext JS, LLC.
38572  *
38573  * Originally Released Under LGPL - original licence link has changed is not relivant.
38574  *
38575  * Fork - LGPL
38576  * <script type="text/javascript">
38577  */
38578  
38579
38580 /**
38581  * @class Roo.menu.DateMenu
38582  * @extends Roo.menu.Menu
38583  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38584  * @constructor
38585  * Creates a new DateMenu
38586  * @param {Object} config Configuration options
38587  */
38588 Roo.menu.DateMenu = function(config){
38589     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38590     this.plain = true;
38591     var di = new Roo.menu.DateItem(config);
38592     this.add(di);
38593     /**
38594      * The {@link Roo.DatePicker} instance for this DateMenu
38595      * @type DatePicker
38596      */
38597     this.picker = di.picker;
38598     /**
38599      * @event select
38600      * @param {DatePicker} picker
38601      * @param {Date} date
38602      */
38603     this.relayEvents(di, ["select"]);
38604     this.on('beforeshow', function(){
38605         if(this.picker){
38606             this.picker.hideMonthPicker(false);
38607         }
38608     }, this);
38609 };
38610 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38611     cls:'x-date-menu'
38612 });/*
38613  * Based on:
38614  * Ext JS Library 1.1.1
38615  * Copyright(c) 2006-2007, Ext JS, LLC.
38616  *
38617  * Originally Released Under LGPL - original licence link has changed is not relivant.
38618  *
38619  * Fork - LGPL
38620  * <script type="text/javascript">
38621  */
38622  
38623
38624 /**
38625  * @class Roo.menu.ColorMenu
38626  * @extends Roo.menu.Menu
38627  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38628  * @constructor
38629  * Creates a new ColorMenu
38630  * @param {Object} config Configuration options
38631  */
38632 Roo.menu.ColorMenu = function(config){
38633     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38634     this.plain = true;
38635     var ci = new Roo.menu.ColorItem(config);
38636     this.add(ci);
38637     /**
38638      * The {@link Roo.ColorPalette} instance for this ColorMenu
38639      * @type ColorPalette
38640      */
38641     this.palette = ci.palette;
38642     /**
38643      * @event select
38644      * @param {ColorPalette} palette
38645      * @param {String} color
38646      */
38647     this.relayEvents(ci, ["select"]);
38648 };
38649 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38650  * Based on:
38651  * Ext JS Library 1.1.1
38652  * Copyright(c) 2006-2007, Ext JS, LLC.
38653  *
38654  * Originally Released Under LGPL - original licence link has changed is not relivant.
38655  *
38656  * Fork - LGPL
38657  * <script type="text/javascript">
38658  */
38659  
38660 /**
38661  * @class Roo.form.Field
38662  * @extends Roo.BoxComponent
38663  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38664  * @constructor
38665  * Creates a new Field
38666  * @param {Object} config Configuration options
38667  */
38668 Roo.form.Field = function(config){
38669     Roo.form.Field.superclass.constructor.call(this, config);
38670 };
38671
38672 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38673     /**
38674      * @cfg {String} fieldLabel Label to use when rendering a form.
38675      */
38676        /**
38677      * @cfg {String} qtip Mouse over tip
38678      */
38679      
38680     /**
38681      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38682      */
38683     invalidClass : "x-form-invalid",
38684     /**
38685      * @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")
38686      */
38687     invalidText : "The value in this field is invalid",
38688     /**
38689      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38690      */
38691     focusClass : "x-form-focus",
38692     /**
38693      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38694       automatic validation (defaults to "keyup").
38695      */
38696     validationEvent : "keyup",
38697     /**
38698      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38699      */
38700     validateOnBlur : true,
38701     /**
38702      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38703      */
38704     validationDelay : 250,
38705     /**
38706      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38707      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38708      */
38709     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38710     /**
38711      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38712      */
38713     fieldClass : "x-form-field",
38714     /**
38715      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38716      *<pre>
38717 Value         Description
38718 -----------   ----------------------------------------------------------------------
38719 qtip          Display a quick tip when the user hovers over the field
38720 title         Display a default browser title attribute popup
38721 under         Add a block div beneath the field containing the error text
38722 side          Add an error icon to the right of the field with a popup on hover
38723 [element id]  Add the error text directly to the innerHTML of the specified element
38724 </pre>
38725      */
38726     msgTarget : 'qtip',
38727     /**
38728      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38729      */
38730     msgFx : 'normal',
38731
38732     /**
38733      * @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.
38734      */
38735     readOnly : false,
38736
38737     /**
38738      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38739      */
38740     disabled : false,
38741
38742     /**
38743      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38744      */
38745     inputType : undefined,
38746     
38747     /**
38748      * @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).
38749          */
38750         tabIndex : undefined,
38751         
38752     // private
38753     isFormField : true,
38754
38755     // private
38756     hasFocus : false,
38757     /**
38758      * @property {Roo.Element} fieldEl
38759      * Element Containing the rendered Field (with label etc.)
38760      */
38761     /**
38762      * @cfg {Mixed} value A value to initialize this field with.
38763      */
38764     value : undefined,
38765
38766     /**
38767      * @cfg {String} name The field's HTML name attribute.
38768      */
38769     /**
38770      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38771      */
38772     // private
38773     loadedValue : false,
38774      
38775      
38776         // private ??
38777         initComponent : function(){
38778         Roo.form.Field.superclass.initComponent.call(this);
38779         this.addEvents({
38780             /**
38781              * @event focus
38782              * Fires when this field receives input focus.
38783              * @param {Roo.form.Field} this
38784              */
38785             focus : true,
38786             /**
38787              * @event blur
38788              * Fires when this field loses input focus.
38789              * @param {Roo.form.Field} this
38790              */
38791             blur : true,
38792             /**
38793              * @event specialkey
38794              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38795              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38796              * @param {Roo.form.Field} this
38797              * @param {Roo.EventObject} e The event object
38798              */
38799             specialkey : true,
38800             /**
38801              * @event change
38802              * Fires just before the field blurs if the field value has changed.
38803              * @param {Roo.form.Field} this
38804              * @param {Mixed} newValue The new value
38805              * @param {Mixed} oldValue The original value
38806              */
38807             change : true,
38808             /**
38809              * @event invalid
38810              * Fires after the field has been marked as invalid.
38811              * @param {Roo.form.Field} this
38812              * @param {String} msg The validation message
38813              */
38814             invalid : true,
38815             /**
38816              * @event valid
38817              * Fires after the field has been validated with no errors.
38818              * @param {Roo.form.Field} this
38819              */
38820             valid : true,
38821              /**
38822              * @event keyup
38823              * Fires after the key up
38824              * @param {Roo.form.Field} this
38825              * @param {Roo.EventObject}  e The event Object
38826              */
38827             keyup : true
38828         });
38829     },
38830
38831     /**
38832      * Returns the name attribute of the field if available
38833      * @return {String} name The field name
38834      */
38835     getName: function(){
38836          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38837     },
38838
38839     // private
38840     onRender : function(ct, position){
38841         Roo.form.Field.superclass.onRender.call(this, ct, position);
38842         if(!this.el){
38843             var cfg = this.getAutoCreate();
38844             if(!cfg.name){
38845                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38846             }
38847             if (!cfg.name.length) {
38848                 delete cfg.name;
38849             }
38850             if(this.inputType){
38851                 cfg.type = this.inputType;
38852             }
38853             this.el = ct.createChild(cfg, position);
38854         }
38855         var type = this.el.dom.type;
38856         if(type){
38857             if(type == 'password'){
38858                 type = 'text';
38859             }
38860             this.el.addClass('x-form-'+type);
38861         }
38862         if(this.readOnly){
38863             this.el.dom.readOnly = true;
38864         }
38865         if(this.tabIndex !== undefined){
38866             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38867         }
38868
38869         this.el.addClass([this.fieldClass, this.cls]);
38870         this.initValue();
38871     },
38872
38873     /**
38874      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38875      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38876      * @return {Roo.form.Field} this
38877      */
38878     applyTo : function(target){
38879         this.allowDomMove = false;
38880         this.el = Roo.get(target);
38881         this.render(this.el.dom.parentNode);
38882         return this;
38883     },
38884
38885     // private
38886     initValue : function(){
38887         if(this.value !== undefined){
38888             this.setValue(this.value);
38889         }else if(this.el.dom.value.length > 0){
38890             this.setValue(this.el.dom.value);
38891         }
38892     },
38893
38894     /**
38895      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38896      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38897      */
38898     isDirty : function() {
38899         if(this.disabled) {
38900             return false;
38901         }
38902         return String(this.getValue()) !== String(this.originalValue);
38903     },
38904
38905     /**
38906      * stores the current value in loadedValue
38907      */
38908     resetHasChanged : function()
38909     {
38910         this.loadedValue = String(this.getValue());
38911     },
38912     /**
38913      * checks the current value against the 'loaded' value.
38914      * Note - will return false if 'resetHasChanged' has not been called first.
38915      */
38916     hasChanged : function()
38917     {
38918         if(this.disabled || this.readOnly) {
38919             return false;
38920         }
38921         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38922     },
38923     
38924     
38925     
38926     // private
38927     afterRender : function(){
38928         Roo.form.Field.superclass.afterRender.call(this);
38929         this.initEvents();
38930     },
38931
38932     // private
38933     fireKey : function(e){
38934         //Roo.log('field ' + e.getKey());
38935         if(e.isNavKeyPress()){
38936             this.fireEvent("specialkey", this, e);
38937         }
38938     },
38939
38940     /**
38941      * Resets the current field value to the originally loaded value and clears any validation messages
38942      */
38943     reset : function(){
38944         this.setValue(this.resetValue);
38945         this.originalValue = this.getValue();
38946         this.clearInvalid();
38947     },
38948
38949     // private
38950     initEvents : function(){
38951         // safari killled keypress - so keydown is now used..
38952         this.el.on("keydown" , this.fireKey,  this);
38953         this.el.on("focus", this.onFocus,  this);
38954         this.el.on("blur", this.onBlur,  this);
38955         this.el.relayEvent('keyup', this);
38956
38957         // reference to original value for reset
38958         this.originalValue = this.getValue();
38959         this.resetValue =  this.getValue();
38960     },
38961
38962     // private
38963     onFocus : function(){
38964         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38965             this.el.addClass(this.focusClass);
38966         }
38967         if(!this.hasFocus){
38968             this.hasFocus = true;
38969             this.startValue = this.getValue();
38970             this.fireEvent("focus", this);
38971         }
38972     },
38973
38974     beforeBlur : Roo.emptyFn,
38975
38976     // private
38977     onBlur : function(){
38978         this.beforeBlur();
38979         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38980             this.el.removeClass(this.focusClass);
38981         }
38982         this.hasFocus = false;
38983         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38984             this.validate();
38985         }
38986         var v = this.getValue();
38987         if(String(v) !== String(this.startValue)){
38988             this.fireEvent('change', this, v, this.startValue);
38989         }
38990         this.fireEvent("blur", this);
38991     },
38992
38993     /**
38994      * Returns whether or not the field value is currently valid
38995      * @param {Boolean} preventMark True to disable marking the field invalid
38996      * @return {Boolean} True if the value is valid, else false
38997      */
38998     isValid : function(preventMark){
38999         if(this.disabled){
39000             return true;
39001         }
39002         var restore = this.preventMark;
39003         this.preventMark = preventMark === true;
39004         var v = this.validateValue(this.processValue(this.getRawValue()));
39005         this.preventMark = restore;
39006         return v;
39007     },
39008
39009     /**
39010      * Validates the field value
39011      * @return {Boolean} True if the value is valid, else false
39012      */
39013     validate : function(){
39014         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39015             this.clearInvalid();
39016             return true;
39017         }
39018         return false;
39019     },
39020
39021     processValue : function(value){
39022         return value;
39023     },
39024
39025     // private
39026     // Subclasses should provide the validation implementation by overriding this
39027     validateValue : function(value){
39028         return true;
39029     },
39030
39031     /**
39032      * Mark this field as invalid
39033      * @param {String} msg The validation message
39034      */
39035     markInvalid : function(msg){
39036         if(!this.rendered || this.preventMark){ // not rendered
39037             return;
39038         }
39039         
39040         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39041         
39042         obj.el.addClass(this.invalidClass);
39043         msg = msg || this.invalidText;
39044         switch(this.msgTarget){
39045             case 'qtip':
39046                 obj.el.dom.qtip = msg;
39047                 obj.el.dom.qclass = 'x-form-invalid-tip';
39048                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39049                     Roo.QuickTips.enable();
39050                 }
39051                 break;
39052             case 'title':
39053                 this.el.dom.title = msg;
39054                 break;
39055             case 'under':
39056                 if(!this.errorEl){
39057                     var elp = this.el.findParent('.x-form-element', 5, true);
39058                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39059                     this.errorEl.setWidth(elp.getWidth(true)-20);
39060                 }
39061                 this.errorEl.update(msg);
39062                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39063                 break;
39064             case 'side':
39065                 if(!this.errorIcon){
39066                     var elp = this.el.findParent('.x-form-element', 5, true);
39067                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39068                 }
39069                 this.alignErrorIcon();
39070                 this.errorIcon.dom.qtip = msg;
39071                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39072                 this.errorIcon.show();
39073                 this.on('resize', this.alignErrorIcon, this);
39074                 break;
39075             default:
39076                 var t = Roo.getDom(this.msgTarget);
39077                 t.innerHTML = msg;
39078                 t.style.display = this.msgDisplay;
39079                 break;
39080         }
39081         this.fireEvent('invalid', this, msg);
39082     },
39083
39084     // private
39085     alignErrorIcon : function(){
39086         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39087     },
39088
39089     /**
39090      * Clear any invalid styles/messages for this field
39091      */
39092     clearInvalid : function(){
39093         if(!this.rendered || this.preventMark){ // not rendered
39094             return;
39095         }
39096         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39097         
39098         obj.el.removeClass(this.invalidClass);
39099         switch(this.msgTarget){
39100             case 'qtip':
39101                 obj.el.dom.qtip = '';
39102                 break;
39103             case 'title':
39104                 this.el.dom.title = '';
39105                 break;
39106             case 'under':
39107                 if(this.errorEl){
39108                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39109                 }
39110                 break;
39111             case 'side':
39112                 if(this.errorIcon){
39113                     this.errorIcon.dom.qtip = '';
39114                     this.errorIcon.hide();
39115                     this.un('resize', this.alignErrorIcon, this);
39116                 }
39117                 break;
39118             default:
39119                 var t = Roo.getDom(this.msgTarget);
39120                 t.innerHTML = '';
39121                 t.style.display = 'none';
39122                 break;
39123         }
39124         this.fireEvent('valid', this);
39125     },
39126
39127     /**
39128      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39129      * @return {Mixed} value The field value
39130      */
39131     getRawValue : function(){
39132         var v = this.el.getValue();
39133         
39134         return v;
39135     },
39136
39137     /**
39138      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39139      * @return {Mixed} value The field value
39140      */
39141     getValue : function(){
39142         var v = this.el.getValue();
39143          
39144         return v;
39145     },
39146
39147     /**
39148      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39149      * @param {Mixed} value The value to set
39150      */
39151     setRawValue : function(v){
39152         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39153     },
39154
39155     /**
39156      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39157      * @param {Mixed} value The value to set
39158      */
39159     setValue : function(v){
39160         this.value = v;
39161         if(this.rendered){
39162             this.el.dom.value = (v === null || v === undefined ? '' : v);
39163              this.validate();
39164         }
39165     },
39166
39167     adjustSize : function(w, h){
39168         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39169         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39170         return s;
39171     },
39172
39173     adjustWidth : function(tag, w){
39174         tag = tag.toLowerCase();
39175         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39176             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39177                 if(tag == 'input'){
39178                     return w + 2;
39179                 }
39180                 if(tag == 'textarea'){
39181                     return w-2;
39182                 }
39183             }else if(Roo.isOpera){
39184                 if(tag == 'input'){
39185                     return w + 2;
39186                 }
39187                 if(tag == 'textarea'){
39188                     return w-2;
39189                 }
39190             }
39191         }
39192         return w;
39193     }
39194 });
39195
39196
39197 // anything other than normal should be considered experimental
39198 Roo.form.Field.msgFx = {
39199     normal : {
39200         show: function(msgEl, f){
39201             msgEl.setDisplayed('block');
39202         },
39203
39204         hide : function(msgEl, f){
39205             msgEl.setDisplayed(false).update('');
39206         }
39207     },
39208
39209     slide : {
39210         show: function(msgEl, f){
39211             msgEl.slideIn('t', {stopFx:true});
39212         },
39213
39214         hide : function(msgEl, f){
39215             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39216         }
39217     },
39218
39219     slideRight : {
39220         show: function(msgEl, f){
39221             msgEl.fixDisplay();
39222             msgEl.alignTo(f.el, 'tl-tr');
39223             msgEl.slideIn('l', {stopFx:true});
39224         },
39225
39226         hide : function(msgEl, f){
39227             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39228         }
39229     }
39230 };/*
39231  * Based on:
39232  * Ext JS Library 1.1.1
39233  * Copyright(c) 2006-2007, Ext JS, LLC.
39234  *
39235  * Originally Released Under LGPL - original licence link has changed is not relivant.
39236  *
39237  * Fork - LGPL
39238  * <script type="text/javascript">
39239  */
39240  
39241
39242 /**
39243  * @class Roo.form.TextField
39244  * @extends Roo.form.Field
39245  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39246  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39247  * @constructor
39248  * Creates a new TextField
39249  * @param {Object} config Configuration options
39250  */
39251 Roo.form.TextField = function(config){
39252     Roo.form.TextField.superclass.constructor.call(this, config);
39253     this.addEvents({
39254         /**
39255          * @event autosize
39256          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39257          * according to the default logic, but this event provides a hook for the developer to apply additional
39258          * logic at runtime to resize the field if needed.
39259              * @param {Roo.form.Field} this This text field
39260              * @param {Number} width The new field width
39261              */
39262         autosize : true
39263     });
39264 };
39265
39266 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39267     /**
39268      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39269      */
39270     grow : false,
39271     /**
39272      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39273      */
39274     growMin : 30,
39275     /**
39276      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39277      */
39278     growMax : 800,
39279     /**
39280      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39281      */
39282     vtype : null,
39283     /**
39284      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39285      */
39286     maskRe : null,
39287     /**
39288      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39289      */
39290     disableKeyFilter : false,
39291     /**
39292      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39293      */
39294     allowBlank : true,
39295     /**
39296      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39297      */
39298     minLength : 0,
39299     /**
39300      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39301      */
39302     maxLength : Number.MAX_VALUE,
39303     /**
39304      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39305      */
39306     minLengthText : "The minimum length for this field is {0}",
39307     /**
39308      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39309      */
39310     maxLengthText : "The maximum length for this field is {0}",
39311     /**
39312      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39313      */
39314     selectOnFocus : false,
39315     /**
39316      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39317      */
39318     blankText : "This field is required",
39319     /**
39320      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39321      * If available, this function will be called only after the basic validators all return true, and will be passed the
39322      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39323      */
39324     validator : null,
39325     /**
39326      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39327      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39328      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39329      */
39330     regex : null,
39331     /**
39332      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39333      */
39334     regexText : "",
39335     /**
39336      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39337      */
39338     emptyText : null,
39339    
39340
39341     // private
39342     initEvents : function()
39343     {
39344         if (this.emptyText) {
39345             this.el.attr('placeholder', this.emptyText);
39346         }
39347         
39348         Roo.form.TextField.superclass.initEvents.call(this);
39349         if(this.validationEvent == 'keyup'){
39350             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39351             this.el.on('keyup', this.filterValidation, this);
39352         }
39353         else if(this.validationEvent !== false){
39354             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39355         }
39356         
39357         if(this.selectOnFocus){
39358             this.on("focus", this.preFocus, this);
39359             
39360         }
39361         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39362             this.el.on("keypress", this.filterKeys, this);
39363         }
39364         if(this.grow){
39365             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39366             this.el.on("click", this.autoSize,  this);
39367         }
39368         if(this.el.is('input[type=password]') && Roo.isSafari){
39369             this.el.on('keydown', this.SafariOnKeyDown, this);
39370         }
39371     },
39372
39373     processValue : function(value){
39374         if(this.stripCharsRe){
39375             var newValue = value.replace(this.stripCharsRe, '');
39376             if(newValue !== value){
39377                 this.setRawValue(newValue);
39378                 return newValue;
39379             }
39380         }
39381         return value;
39382     },
39383
39384     filterValidation : function(e){
39385         if(!e.isNavKeyPress()){
39386             this.validationTask.delay(this.validationDelay);
39387         }
39388     },
39389
39390     // private
39391     onKeyUp : function(e){
39392         if(!e.isNavKeyPress()){
39393             this.autoSize();
39394         }
39395     },
39396
39397     /**
39398      * Resets the current field value to the originally-loaded value and clears any validation messages.
39399      *  
39400      */
39401     reset : function(){
39402         Roo.form.TextField.superclass.reset.call(this);
39403        
39404     },
39405
39406     
39407     // private
39408     preFocus : function(){
39409         
39410         if(this.selectOnFocus){
39411             this.el.dom.select();
39412         }
39413     },
39414
39415     
39416     // private
39417     filterKeys : function(e){
39418         var k = e.getKey();
39419         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39420             return;
39421         }
39422         var c = e.getCharCode(), cc = String.fromCharCode(c);
39423         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39424             return;
39425         }
39426         if(!this.maskRe.test(cc)){
39427             e.stopEvent();
39428         }
39429     },
39430
39431     setValue : function(v){
39432         
39433         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39434         
39435         this.autoSize();
39436     },
39437
39438     /**
39439      * Validates a value according to the field's validation rules and marks the field as invalid
39440      * if the validation fails
39441      * @param {Mixed} value The value to validate
39442      * @return {Boolean} True if the value is valid, else false
39443      */
39444     validateValue : function(value){
39445         if(value.length < 1)  { // if it's blank
39446              if(this.allowBlank){
39447                 this.clearInvalid();
39448                 return true;
39449              }else{
39450                 this.markInvalid(this.blankText);
39451                 return false;
39452              }
39453         }
39454         if(value.length < this.minLength){
39455             this.markInvalid(String.format(this.minLengthText, this.minLength));
39456             return false;
39457         }
39458         if(value.length > this.maxLength){
39459             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39460             return false;
39461         }
39462         if(this.vtype){
39463             var vt = Roo.form.VTypes;
39464             if(!vt[this.vtype](value, this)){
39465                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39466                 return false;
39467             }
39468         }
39469         if(typeof this.validator == "function"){
39470             var msg = this.validator(value);
39471             if(msg !== true){
39472                 this.markInvalid(msg);
39473                 return false;
39474             }
39475         }
39476         if(this.regex && !this.regex.test(value)){
39477             this.markInvalid(this.regexText);
39478             return false;
39479         }
39480         return true;
39481     },
39482
39483     /**
39484      * Selects text in this field
39485      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39486      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39487      */
39488     selectText : function(start, end){
39489         var v = this.getRawValue();
39490         if(v.length > 0){
39491             start = start === undefined ? 0 : start;
39492             end = end === undefined ? v.length : end;
39493             var d = this.el.dom;
39494             if(d.setSelectionRange){
39495                 d.setSelectionRange(start, end);
39496             }else if(d.createTextRange){
39497                 var range = d.createTextRange();
39498                 range.moveStart("character", start);
39499                 range.moveEnd("character", v.length-end);
39500                 range.select();
39501             }
39502         }
39503     },
39504
39505     /**
39506      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39507      * This only takes effect if grow = true, and fires the autosize event.
39508      */
39509     autoSize : function(){
39510         if(!this.grow || !this.rendered){
39511             return;
39512         }
39513         if(!this.metrics){
39514             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39515         }
39516         var el = this.el;
39517         var v = el.dom.value;
39518         var d = document.createElement('div');
39519         d.appendChild(document.createTextNode(v));
39520         v = d.innerHTML;
39521         d = null;
39522         v += "&#160;";
39523         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39524         this.el.setWidth(w);
39525         this.fireEvent("autosize", this, w);
39526     },
39527     
39528     // private
39529     SafariOnKeyDown : function(event)
39530     {
39531         // this is a workaround for a password hang bug on chrome/ webkit.
39532         
39533         var isSelectAll = false;
39534         
39535         if(this.el.dom.selectionEnd > 0){
39536             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39537         }
39538         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39539             event.preventDefault();
39540             this.setValue('');
39541             return;
39542         }
39543         
39544         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39545             
39546             event.preventDefault();
39547             // this is very hacky as keydown always get's upper case.
39548             
39549             var cc = String.fromCharCode(event.getCharCode());
39550             
39551             
39552             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39553             
39554         }
39555         
39556         
39557     }
39558 });/*
39559  * Based on:
39560  * Ext JS Library 1.1.1
39561  * Copyright(c) 2006-2007, Ext JS, LLC.
39562  *
39563  * Originally Released Under LGPL - original licence link has changed is not relivant.
39564  *
39565  * Fork - LGPL
39566  * <script type="text/javascript">
39567  */
39568  
39569 /**
39570  * @class Roo.form.Hidden
39571  * @extends Roo.form.TextField
39572  * Simple Hidden element used on forms 
39573  * 
39574  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39575  * 
39576  * @constructor
39577  * Creates a new Hidden form element.
39578  * @param {Object} config Configuration options
39579  */
39580
39581
39582
39583 // easy hidden field...
39584 Roo.form.Hidden = function(config){
39585     Roo.form.Hidden.superclass.constructor.call(this, config);
39586 };
39587   
39588 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39589     fieldLabel:      '',
39590     inputType:      'hidden',
39591     width:          50,
39592     allowBlank:     true,
39593     labelSeparator: '',
39594     hidden:         true,
39595     itemCls :       'x-form-item-display-none'
39596
39597
39598 });
39599
39600
39601 /*
39602  * Based on:
39603  * Ext JS Library 1.1.1
39604  * Copyright(c) 2006-2007, Ext JS, LLC.
39605  *
39606  * Originally Released Under LGPL - original licence link has changed is not relivant.
39607  *
39608  * Fork - LGPL
39609  * <script type="text/javascript">
39610  */
39611  
39612 /**
39613  * @class Roo.form.TriggerField
39614  * @extends Roo.form.TextField
39615  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39616  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39617  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39618  * for which you can provide a custom implementation.  For example:
39619  * <pre><code>
39620 var trigger = new Roo.form.TriggerField();
39621 trigger.onTriggerClick = myTriggerFn;
39622 trigger.applyTo('my-field');
39623 </code></pre>
39624  *
39625  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39626  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39627  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39628  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39629  * @constructor
39630  * Create a new TriggerField.
39631  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39632  * to the base TextField)
39633  */
39634 Roo.form.TriggerField = function(config){
39635     this.mimicing = false;
39636     Roo.form.TriggerField.superclass.constructor.call(this, config);
39637 };
39638
39639 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39640     /**
39641      * @cfg {String} triggerClass A CSS class to apply to the trigger
39642      */
39643     /**
39644      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39645      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39646      */
39647     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39648     /**
39649      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39650      */
39651     hideTrigger:false,
39652
39653     /** @cfg {Boolean} grow @hide */
39654     /** @cfg {Number} growMin @hide */
39655     /** @cfg {Number} growMax @hide */
39656
39657     /**
39658      * @hide 
39659      * @method
39660      */
39661     autoSize: Roo.emptyFn,
39662     // private
39663     monitorTab : true,
39664     // private
39665     deferHeight : true,
39666
39667     
39668     actionMode : 'wrap',
39669     // private
39670     onResize : function(w, h){
39671         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39672         if(typeof w == 'number'){
39673             var x = w - this.trigger.getWidth();
39674             this.el.setWidth(this.adjustWidth('input', x));
39675             this.trigger.setStyle('left', x+'px');
39676         }
39677     },
39678
39679     // private
39680     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39681
39682     // private
39683     getResizeEl : function(){
39684         return this.wrap;
39685     },
39686
39687     // private
39688     getPositionEl : function(){
39689         return this.wrap;
39690     },
39691
39692     // private
39693     alignErrorIcon : function(){
39694         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39695     },
39696
39697     // private
39698     onRender : function(ct, position){
39699         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39700         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39701         this.trigger = this.wrap.createChild(this.triggerConfig ||
39702                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39703         if(this.hideTrigger){
39704             this.trigger.setDisplayed(false);
39705         }
39706         this.initTrigger();
39707         if(!this.width){
39708             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39709         }
39710     },
39711
39712     // private
39713     initTrigger : function(){
39714         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39715         this.trigger.addClassOnOver('x-form-trigger-over');
39716         this.trigger.addClassOnClick('x-form-trigger-click');
39717     },
39718
39719     // private
39720     onDestroy : function(){
39721         if(this.trigger){
39722             this.trigger.removeAllListeners();
39723             this.trigger.remove();
39724         }
39725         if(this.wrap){
39726             this.wrap.remove();
39727         }
39728         Roo.form.TriggerField.superclass.onDestroy.call(this);
39729     },
39730
39731     // private
39732     onFocus : function(){
39733         Roo.form.TriggerField.superclass.onFocus.call(this);
39734         if(!this.mimicing){
39735             this.wrap.addClass('x-trigger-wrap-focus');
39736             this.mimicing = true;
39737             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39738             if(this.monitorTab){
39739                 this.el.on("keydown", this.checkTab, this);
39740             }
39741         }
39742     },
39743
39744     // private
39745     checkTab : function(e){
39746         if(e.getKey() == e.TAB){
39747             this.triggerBlur();
39748         }
39749     },
39750
39751     // private
39752     onBlur : function(){
39753         // do nothing
39754     },
39755
39756     // private
39757     mimicBlur : function(e, t){
39758         if(!this.wrap.contains(t) && this.validateBlur()){
39759             this.triggerBlur();
39760         }
39761     },
39762
39763     // private
39764     triggerBlur : function(){
39765         this.mimicing = false;
39766         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39767         if(this.monitorTab){
39768             this.el.un("keydown", this.checkTab, this);
39769         }
39770         this.wrap.removeClass('x-trigger-wrap-focus');
39771         Roo.form.TriggerField.superclass.onBlur.call(this);
39772     },
39773
39774     // private
39775     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39776     validateBlur : function(e, t){
39777         return true;
39778     },
39779
39780     // private
39781     onDisable : function(){
39782         Roo.form.TriggerField.superclass.onDisable.call(this);
39783         if(this.wrap){
39784             this.wrap.addClass('x-item-disabled');
39785         }
39786     },
39787
39788     // private
39789     onEnable : function(){
39790         Roo.form.TriggerField.superclass.onEnable.call(this);
39791         if(this.wrap){
39792             this.wrap.removeClass('x-item-disabled');
39793         }
39794     },
39795
39796     // private
39797     onShow : function(){
39798         var ae = this.getActionEl();
39799         
39800         if(ae){
39801             ae.dom.style.display = '';
39802             ae.dom.style.visibility = 'visible';
39803         }
39804     },
39805
39806     // private
39807     
39808     onHide : function(){
39809         var ae = this.getActionEl();
39810         ae.dom.style.display = 'none';
39811     },
39812
39813     /**
39814      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39815      * by an implementing function.
39816      * @method
39817      * @param {EventObject} e
39818      */
39819     onTriggerClick : Roo.emptyFn
39820 });
39821
39822 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39823 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39824 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39825 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39826     initComponent : function(){
39827         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39828
39829         this.triggerConfig = {
39830             tag:'span', cls:'x-form-twin-triggers', cn:[
39831             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39832             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39833         ]};
39834     },
39835
39836     getTrigger : function(index){
39837         return this.triggers[index];
39838     },
39839
39840     initTrigger : function(){
39841         var ts = this.trigger.select('.x-form-trigger', true);
39842         this.wrap.setStyle('overflow', 'hidden');
39843         var triggerField = this;
39844         ts.each(function(t, all, index){
39845             t.hide = function(){
39846                 var w = triggerField.wrap.getWidth();
39847                 this.dom.style.display = 'none';
39848                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39849             };
39850             t.show = function(){
39851                 var w = triggerField.wrap.getWidth();
39852                 this.dom.style.display = '';
39853                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39854             };
39855             var triggerIndex = 'Trigger'+(index+1);
39856
39857             if(this['hide'+triggerIndex]){
39858                 t.dom.style.display = 'none';
39859             }
39860             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39861             t.addClassOnOver('x-form-trigger-over');
39862             t.addClassOnClick('x-form-trigger-click');
39863         }, this);
39864         this.triggers = ts.elements;
39865     },
39866
39867     onTrigger1Click : Roo.emptyFn,
39868     onTrigger2Click : Roo.emptyFn
39869 });/*
39870  * Based on:
39871  * Ext JS Library 1.1.1
39872  * Copyright(c) 2006-2007, Ext JS, LLC.
39873  *
39874  * Originally Released Under LGPL - original licence link has changed is not relivant.
39875  *
39876  * Fork - LGPL
39877  * <script type="text/javascript">
39878  */
39879  
39880 /**
39881  * @class Roo.form.TextArea
39882  * @extends Roo.form.TextField
39883  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39884  * support for auto-sizing.
39885  * @constructor
39886  * Creates a new TextArea
39887  * @param {Object} config Configuration options
39888  */
39889 Roo.form.TextArea = function(config){
39890     Roo.form.TextArea.superclass.constructor.call(this, config);
39891     // these are provided exchanges for backwards compat
39892     // minHeight/maxHeight were replaced by growMin/growMax to be
39893     // compatible with TextField growing config values
39894     if(this.minHeight !== undefined){
39895         this.growMin = this.minHeight;
39896     }
39897     if(this.maxHeight !== undefined){
39898         this.growMax = this.maxHeight;
39899     }
39900 };
39901
39902 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39903     /**
39904      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39905      */
39906     growMin : 60,
39907     /**
39908      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39909      */
39910     growMax: 1000,
39911     /**
39912      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39913      * in the field (equivalent to setting overflow: hidden, defaults to false)
39914      */
39915     preventScrollbars: false,
39916     /**
39917      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39918      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39919      */
39920
39921     // private
39922     onRender : function(ct, position){
39923         if(!this.el){
39924             this.defaultAutoCreate = {
39925                 tag: "textarea",
39926                 style:"width:300px;height:60px;",
39927                 autocomplete: "new-password"
39928             };
39929         }
39930         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39931         if(this.grow){
39932             this.textSizeEl = Roo.DomHelper.append(document.body, {
39933                 tag: "pre", cls: "x-form-grow-sizer"
39934             });
39935             if(this.preventScrollbars){
39936                 this.el.setStyle("overflow", "hidden");
39937             }
39938             this.el.setHeight(this.growMin);
39939         }
39940     },
39941
39942     onDestroy : function(){
39943         if(this.textSizeEl){
39944             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39945         }
39946         Roo.form.TextArea.superclass.onDestroy.call(this);
39947     },
39948
39949     // private
39950     onKeyUp : function(e){
39951         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39952             this.autoSize();
39953         }
39954     },
39955
39956     /**
39957      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39958      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39959      */
39960     autoSize : function(){
39961         if(!this.grow || !this.textSizeEl){
39962             return;
39963         }
39964         var el = this.el;
39965         var v = el.dom.value;
39966         var ts = this.textSizeEl;
39967
39968         ts.innerHTML = '';
39969         ts.appendChild(document.createTextNode(v));
39970         v = ts.innerHTML;
39971
39972         Roo.fly(ts).setWidth(this.el.getWidth());
39973         if(v.length < 1){
39974             v = "&#160;&#160;";
39975         }else{
39976             if(Roo.isIE){
39977                 v = v.replace(/\n/g, '<p>&#160;</p>');
39978             }
39979             v += "&#160;\n&#160;";
39980         }
39981         ts.innerHTML = v;
39982         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39983         if(h != this.lastHeight){
39984             this.lastHeight = h;
39985             this.el.setHeight(h);
39986             this.fireEvent("autosize", this, h);
39987         }
39988     }
39989 });/*
39990  * Based on:
39991  * Ext JS Library 1.1.1
39992  * Copyright(c) 2006-2007, Ext JS, LLC.
39993  *
39994  * Originally Released Under LGPL - original licence link has changed is not relivant.
39995  *
39996  * Fork - LGPL
39997  * <script type="text/javascript">
39998  */
39999  
40000
40001 /**
40002  * @class Roo.form.NumberField
40003  * @extends Roo.form.TextField
40004  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40005  * @constructor
40006  * Creates a new NumberField
40007  * @param {Object} config Configuration options
40008  */
40009 Roo.form.NumberField = function(config){
40010     Roo.form.NumberField.superclass.constructor.call(this, config);
40011 };
40012
40013 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40014     /**
40015      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40016      */
40017     fieldClass: "x-form-field x-form-num-field",
40018     /**
40019      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40020      */
40021     allowDecimals : true,
40022     /**
40023      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40024      */
40025     decimalSeparator : ".",
40026     /**
40027      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40028      */
40029     decimalPrecision : 2,
40030     /**
40031      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40032      */
40033     allowNegative : true,
40034     /**
40035      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40036      */
40037     minValue : Number.NEGATIVE_INFINITY,
40038     /**
40039      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40040      */
40041     maxValue : Number.MAX_VALUE,
40042     /**
40043      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40044      */
40045     minText : "The minimum value for this field is {0}",
40046     /**
40047      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40048      */
40049     maxText : "The maximum value for this field is {0}",
40050     /**
40051      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40052      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40053      */
40054     nanText : "{0} is not a valid number",
40055
40056     // private
40057     initEvents : function(){
40058         Roo.form.NumberField.superclass.initEvents.call(this);
40059         var allowed = "0123456789";
40060         if(this.allowDecimals){
40061             allowed += this.decimalSeparator;
40062         }
40063         if(this.allowNegative){
40064             allowed += "-";
40065         }
40066         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40067         var keyPress = function(e){
40068             var k = e.getKey();
40069             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40070                 return;
40071             }
40072             var c = e.getCharCode();
40073             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40074                 e.stopEvent();
40075             }
40076         };
40077         this.el.on("keypress", keyPress, this);
40078     },
40079
40080     // private
40081     validateValue : function(value){
40082         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40083             return false;
40084         }
40085         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40086              return true;
40087         }
40088         var num = this.parseValue(value);
40089         if(isNaN(num)){
40090             this.markInvalid(String.format(this.nanText, value));
40091             return false;
40092         }
40093         if(num < this.minValue){
40094             this.markInvalid(String.format(this.minText, this.minValue));
40095             return false;
40096         }
40097         if(num > this.maxValue){
40098             this.markInvalid(String.format(this.maxText, this.maxValue));
40099             return false;
40100         }
40101         return true;
40102     },
40103
40104     getValue : function(){
40105         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40106     },
40107
40108     // private
40109     parseValue : function(value){
40110         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40111         return isNaN(value) ? '' : value;
40112     },
40113
40114     // private
40115     fixPrecision : function(value){
40116         var nan = isNaN(value);
40117         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40118             return nan ? '' : value;
40119         }
40120         return parseFloat(value).toFixed(this.decimalPrecision);
40121     },
40122
40123     setValue : function(v){
40124         v = this.fixPrecision(v);
40125         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40126     },
40127
40128     // private
40129     decimalPrecisionFcn : function(v){
40130         return Math.floor(v);
40131     },
40132
40133     beforeBlur : function(){
40134         var v = this.parseValue(this.getRawValue());
40135         if(v){
40136             this.setValue(v);
40137         }
40138     }
40139 });/*
40140  * Based on:
40141  * Ext JS Library 1.1.1
40142  * Copyright(c) 2006-2007, Ext JS, LLC.
40143  *
40144  * Originally Released Under LGPL - original licence link has changed is not relivant.
40145  *
40146  * Fork - LGPL
40147  * <script type="text/javascript">
40148  */
40149  
40150 /**
40151  * @class Roo.form.DateField
40152  * @extends Roo.form.TriggerField
40153  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40154 * @constructor
40155 * Create a new DateField
40156 * @param {Object} config
40157  */
40158 Roo.form.DateField = function(config){
40159     Roo.form.DateField.superclass.constructor.call(this, config);
40160     
40161       this.addEvents({
40162          
40163         /**
40164          * @event select
40165          * Fires when a date is selected
40166              * @param {Roo.form.DateField} combo This combo box
40167              * @param {Date} date The date selected
40168              */
40169         'select' : true
40170          
40171     });
40172     
40173     
40174     if(typeof this.minValue == "string") {
40175         this.minValue = this.parseDate(this.minValue);
40176     }
40177     if(typeof this.maxValue == "string") {
40178         this.maxValue = this.parseDate(this.maxValue);
40179     }
40180     this.ddMatch = null;
40181     if(this.disabledDates){
40182         var dd = this.disabledDates;
40183         var re = "(?:";
40184         for(var i = 0; i < dd.length; i++){
40185             re += dd[i];
40186             if(i != dd.length-1) {
40187                 re += "|";
40188             }
40189         }
40190         this.ddMatch = new RegExp(re + ")");
40191     }
40192 };
40193
40194 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40195     /**
40196      * @cfg {String} format
40197      * The default date format string which can be overriden for localization support.  The format must be
40198      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40199      */
40200     format : "m/d/y",
40201     /**
40202      * @cfg {String} altFormats
40203      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40204      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40205      */
40206     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40207     /**
40208      * @cfg {Array} disabledDays
40209      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40210      */
40211     disabledDays : null,
40212     /**
40213      * @cfg {String} disabledDaysText
40214      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40215      */
40216     disabledDaysText : "Disabled",
40217     /**
40218      * @cfg {Array} disabledDates
40219      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40220      * expression so they are very powerful. Some examples:
40221      * <ul>
40222      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40223      * <li>["03/08", "09/16"] would disable those days for every year</li>
40224      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40225      * <li>["03/../2006"] would disable every day in March 2006</li>
40226      * <li>["^03"] would disable every day in every March</li>
40227      * </ul>
40228      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40229      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40230      */
40231     disabledDates : null,
40232     /**
40233      * @cfg {String} disabledDatesText
40234      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40235      */
40236     disabledDatesText : "Disabled",
40237     /**
40238      * @cfg {Date/String} minValue
40239      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40240      * valid format (defaults to null).
40241      */
40242     minValue : null,
40243     /**
40244      * @cfg {Date/String} maxValue
40245      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40246      * valid format (defaults to null).
40247      */
40248     maxValue : null,
40249     /**
40250      * @cfg {String} minText
40251      * The error text to display when the date in the cell is before minValue (defaults to
40252      * 'The date in this field must be after {minValue}').
40253      */
40254     minText : "The date in this field must be equal to or after {0}",
40255     /**
40256      * @cfg {String} maxText
40257      * The error text to display when the date in the cell is after maxValue (defaults to
40258      * 'The date in this field must be before {maxValue}').
40259      */
40260     maxText : "The date in this field must be equal to or before {0}",
40261     /**
40262      * @cfg {String} invalidText
40263      * The error text to display when the date in the field is invalid (defaults to
40264      * '{value} is not a valid date - it must be in the format {format}').
40265      */
40266     invalidText : "{0} is not a valid date - it must be in the format {1}",
40267     /**
40268      * @cfg {String} triggerClass
40269      * An additional CSS class used to style the trigger button.  The trigger will always get the
40270      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40271      * which displays a calendar icon).
40272      */
40273     triggerClass : 'x-form-date-trigger',
40274     
40275
40276     /**
40277      * @cfg {Boolean} useIso
40278      * if enabled, then the date field will use a hidden field to store the 
40279      * real value as iso formated date. default (false)
40280      */ 
40281     useIso : false,
40282     /**
40283      * @cfg {String/Object} autoCreate
40284      * A DomHelper element spec, or true for a default element spec (defaults to
40285      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40286      */ 
40287     // private
40288     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40289     
40290     // private
40291     hiddenField: false,
40292     
40293     onRender : function(ct, position)
40294     {
40295         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40296         if (this.useIso) {
40297             //this.el.dom.removeAttribute('name'); 
40298             Roo.log("Changing name?");
40299             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40300             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40301                     'before', true);
40302             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40303             // prevent input submission
40304             this.hiddenName = this.name;
40305         }
40306             
40307             
40308     },
40309     
40310     // private
40311     validateValue : function(value)
40312     {
40313         value = this.formatDate(value);
40314         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40315             Roo.log('super failed');
40316             return false;
40317         }
40318         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40319              return true;
40320         }
40321         var svalue = value;
40322         value = this.parseDate(value);
40323         if(!value){
40324             Roo.log('parse date failed' + svalue);
40325             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40326             return false;
40327         }
40328         var time = value.getTime();
40329         if(this.minValue && time < this.minValue.getTime()){
40330             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40331             return false;
40332         }
40333         if(this.maxValue && time > this.maxValue.getTime()){
40334             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40335             return false;
40336         }
40337         if(this.disabledDays){
40338             var day = value.getDay();
40339             for(var i = 0; i < this.disabledDays.length; i++) {
40340                 if(day === this.disabledDays[i]){
40341                     this.markInvalid(this.disabledDaysText);
40342                     return false;
40343                 }
40344             }
40345         }
40346         var fvalue = this.formatDate(value);
40347         if(this.ddMatch && this.ddMatch.test(fvalue)){
40348             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40349             return false;
40350         }
40351         return true;
40352     },
40353
40354     // private
40355     // Provides logic to override the default TriggerField.validateBlur which just returns true
40356     validateBlur : function(){
40357         return !this.menu || !this.menu.isVisible();
40358     },
40359     
40360     getName: function()
40361     {
40362         // returns hidden if it's set..
40363         if (!this.rendered) {return ''};
40364         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40365         
40366     },
40367
40368     /**
40369      * Returns the current date value of the date field.
40370      * @return {Date} The date value
40371      */
40372     getValue : function(){
40373         
40374         return  this.hiddenField ?
40375                 this.hiddenField.value :
40376                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40377     },
40378
40379     /**
40380      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40381      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40382      * (the default format used is "m/d/y").
40383      * <br />Usage:
40384      * <pre><code>
40385 //All of these calls set the same date value (May 4, 2006)
40386
40387 //Pass a date object:
40388 var dt = new Date('5/4/06');
40389 dateField.setValue(dt);
40390
40391 //Pass a date string (default format):
40392 dateField.setValue('5/4/06');
40393
40394 //Pass a date string (custom format):
40395 dateField.format = 'Y-m-d';
40396 dateField.setValue('2006-5-4');
40397 </code></pre>
40398      * @param {String/Date} date The date or valid date string
40399      */
40400     setValue : function(date){
40401         if (this.hiddenField) {
40402             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40403         }
40404         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40405         // make sure the value field is always stored as a date..
40406         this.value = this.parseDate(date);
40407         
40408         
40409     },
40410
40411     // private
40412     parseDate : function(value){
40413         if(!value || value instanceof Date){
40414             return value;
40415         }
40416         var v = Date.parseDate(value, this.format);
40417          if (!v && this.useIso) {
40418             v = Date.parseDate(value, 'Y-m-d');
40419         }
40420         if(!v && this.altFormats){
40421             if(!this.altFormatsArray){
40422                 this.altFormatsArray = this.altFormats.split("|");
40423             }
40424             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40425                 v = Date.parseDate(value, this.altFormatsArray[i]);
40426             }
40427         }
40428         return v;
40429     },
40430
40431     // private
40432     formatDate : function(date, fmt){
40433         return (!date || !(date instanceof Date)) ?
40434                date : date.dateFormat(fmt || this.format);
40435     },
40436
40437     // private
40438     menuListeners : {
40439         select: function(m, d){
40440             
40441             this.setValue(d);
40442             this.fireEvent('select', this, d);
40443         },
40444         show : function(){ // retain focus styling
40445             this.onFocus();
40446         },
40447         hide : function(){
40448             this.focus.defer(10, this);
40449             var ml = this.menuListeners;
40450             this.menu.un("select", ml.select,  this);
40451             this.menu.un("show", ml.show,  this);
40452             this.menu.un("hide", ml.hide,  this);
40453         }
40454     },
40455
40456     // private
40457     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40458     onTriggerClick : function(){
40459         if(this.disabled){
40460             return;
40461         }
40462         if(this.menu == null){
40463             this.menu = new Roo.menu.DateMenu();
40464         }
40465         Roo.apply(this.menu.picker,  {
40466             showClear: this.allowBlank,
40467             minDate : this.minValue,
40468             maxDate : this.maxValue,
40469             disabledDatesRE : this.ddMatch,
40470             disabledDatesText : this.disabledDatesText,
40471             disabledDays : this.disabledDays,
40472             disabledDaysText : this.disabledDaysText,
40473             format : this.useIso ? 'Y-m-d' : this.format,
40474             minText : String.format(this.minText, this.formatDate(this.minValue)),
40475             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40476         });
40477         this.menu.on(Roo.apply({}, this.menuListeners, {
40478             scope:this
40479         }));
40480         this.menu.picker.setValue(this.getValue() || new Date());
40481         this.menu.show(this.el, "tl-bl?");
40482     },
40483
40484     beforeBlur : function(){
40485         var v = this.parseDate(this.getRawValue());
40486         if(v){
40487             this.setValue(v);
40488         }
40489     },
40490
40491     /*@
40492      * overide
40493      * 
40494      */
40495     isDirty : function() {
40496         if(this.disabled) {
40497             return false;
40498         }
40499         
40500         if(typeof(this.startValue) === 'undefined'){
40501             return false;
40502         }
40503         
40504         return String(this.getValue()) !== String(this.startValue);
40505         
40506     }
40507 });/*
40508  * Based on:
40509  * Ext JS Library 1.1.1
40510  * Copyright(c) 2006-2007, Ext JS, LLC.
40511  *
40512  * Originally Released Under LGPL - original licence link has changed is not relivant.
40513  *
40514  * Fork - LGPL
40515  * <script type="text/javascript">
40516  */
40517  
40518 /**
40519  * @class Roo.form.MonthField
40520  * @extends Roo.form.TriggerField
40521  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40522 * @constructor
40523 * Create a new MonthField
40524 * @param {Object} config
40525  */
40526 Roo.form.MonthField = function(config){
40527     
40528     Roo.form.MonthField.superclass.constructor.call(this, config);
40529     
40530       this.addEvents({
40531          
40532         /**
40533          * @event select
40534          * Fires when a date is selected
40535              * @param {Roo.form.MonthFieeld} combo This combo box
40536              * @param {Date} date The date selected
40537              */
40538         'select' : true
40539          
40540     });
40541     
40542     
40543     if(typeof this.minValue == "string") {
40544         this.minValue = this.parseDate(this.minValue);
40545     }
40546     if(typeof this.maxValue == "string") {
40547         this.maxValue = this.parseDate(this.maxValue);
40548     }
40549     this.ddMatch = null;
40550     if(this.disabledDates){
40551         var dd = this.disabledDates;
40552         var re = "(?:";
40553         for(var i = 0; i < dd.length; i++){
40554             re += dd[i];
40555             if(i != dd.length-1) {
40556                 re += "|";
40557             }
40558         }
40559         this.ddMatch = new RegExp(re + ")");
40560     }
40561 };
40562
40563 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40564     /**
40565      * @cfg {String} format
40566      * The default date format string which can be overriden for localization support.  The format must be
40567      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40568      */
40569     format : "M Y",
40570     /**
40571      * @cfg {String} altFormats
40572      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40573      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40574      */
40575     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40576     /**
40577      * @cfg {Array} disabledDays
40578      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40579      */
40580     disabledDays : [0,1,2,3,4,5,6],
40581     /**
40582      * @cfg {String} disabledDaysText
40583      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40584      */
40585     disabledDaysText : "Disabled",
40586     /**
40587      * @cfg {Array} disabledDates
40588      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40589      * expression so they are very powerful. Some examples:
40590      * <ul>
40591      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40592      * <li>["03/08", "09/16"] would disable those days for every year</li>
40593      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40594      * <li>["03/../2006"] would disable every day in March 2006</li>
40595      * <li>["^03"] would disable every day in every March</li>
40596      * </ul>
40597      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40598      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40599      */
40600     disabledDates : null,
40601     /**
40602      * @cfg {String} disabledDatesText
40603      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40604      */
40605     disabledDatesText : "Disabled",
40606     /**
40607      * @cfg {Date/String} minValue
40608      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40609      * valid format (defaults to null).
40610      */
40611     minValue : null,
40612     /**
40613      * @cfg {Date/String} maxValue
40614      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40615      * valid format (defaults to null).
40616      */
40617     maxValue : null,
40618     /**
40619      * @cfg {String} minText
40620      * The error text to display when the date in the cell is before minValue (defaults to
40621      * 'The date in this field must be after {minValue}').
40622      */
40623     minText : "The date in this field must be equal to or after {0}",
40624     /**
40625      * @cfg {String} maxTextf
40626      * The error text to display when the date in the cell is after maxValue (defaults to
40627      * 'The date in this field must be before {maxValue}').
40628      */
40629     maxText : "The date in this field must be equal to or before {0}",
40630     /**
40631      * @cfg {String} invalidText
40632      * The error text to display when the date in the field is invalid (defaults to
40633      * '{value} is not a valid date - it must be in the format {format}').
40634      */
40635     invalidText : "{0} is not a valid date - it must be in the format {1}",
40636     /**
40637      * @cfg {String} triggerClass
40638      * An additional CSS class used to style the trigger button.  The trigger will always get the
40639      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40640      * which displays a calendar icon).
40641      */
40642     triggerClass : 'x-form-date-trigger',
40643     
40644
40645     /**
40646      * @cfg {Boolean} useIso
40647      * if enabled, then the date field will use a hidden field to store the 
40648      * real value as iso formated date. default (true)
40649      */ 
40650     useIso : true,
40651     /**
40652      * @cfg {String/Object} autoCreate
40653      * A DomHelper element spec, or true for a default element spec (defaults to
40654      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40655      */ 
40656     // private
40657     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40658     
40659     // private
40660     hiddenField: false,
40661     
40662     hideMonthPicker : false,
40663     
40664     onRender : function(ct, position)
40665     {
40666         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40667         if (this.useIso) {
40668             this.el.dom.removeAttribute('name'); 
40669             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40670                     'before', true);
40671             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40672             // prevent input submission
40673             this.hiddenName = this.name;
40674         }
40675             
40676             
40677     },
40678     
40679     // private
40680     validateValue : function(value)
40681     {
40682         value = this.formatDate(value);
40683         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40684             return false;
40685         }
40686         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40687              return true;
40688         }
40689         var svalue = value;
40690         value = this.parseDate(value);
40691         if(!value){
40692             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40693             return false;
40694         }
40695         var time = value.getTime();
40696         if(this.minValue && time < this.minValue.getTime()){
40697             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40698             return false;
40699         }
40700         if(this.maxValue && time > this.maxValue.getTime()){
40701             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40702             return false;
40703         }
40704         /*if(this.disabledDays){
40705             var day = value.getDay();
40706             for(var i = 0; i < this.disabledDays.length; i++) {
40707                 if(day === this.disabledDays[i]){
40708                     this.markInvalid(this.disabledDaysText);
40709                     return false;
40710                 }
40711             }
40712         }
40713         */
40714         var fvalue = this.formatDate(value);
40715         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40716             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40717             return false;
40718         }
40719         */
40720         return true;
40721     },
40722
40723     // private
40724     // Provides logic to override the default TriggerField.validateBlur which just returns true
40725     validateBlur : function(){
40726         return !this.menu || !this.menu.isVisible();
40727     },
40728
40729     /**
40730      * Returns the current date value of the date field.
40731      * @return {Date} The date value
40732      */
40733     getValue : function(){
40734         
40735         
40736         
40737         return  this.hiddenField ?
40738                 this.hiddenField.value :
40739                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40740     },
40741
40742     /**
40743      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40744      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40745      * (the default format used is "m/d/y").
40746      * <br />Usage:
40747      * <pre><code>
40748 //All of these calls set the same date value (May 4, 2006)
40749
40750 //Pass a date object:
40751 var dt = new Date('5/4/06');
40752 monthField.setValue(dt);
40753
40754 //Pass a date string (default format):
40755 monthField.setValue('5/4/06');
40756
40757 //Pass a date string (custom format):
40758 monthField.format = 'Y-m-d';
40759 monthField.setValue('2006-5-4');
40760 </code></pre>
40761      * @param {String/Date} date The date or valid date string
40762      */
40763     setValue : function(date){
40764         Roo.log('month setValue' + date);
40765         // can only be first of month..
40766         
40767         var val = this.parseDate(date);
40768         
40769         if (this.hiddenField) {
40770             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40771         }
40772         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40773         this.value = this.parseDate(date);
40774     },
40775
40776     // private
40777     parseDate : function(value){
40778         if(!value || value instanceof Date){
40779             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40780             return value;
40781         }
40782         var v = Date.parseDate(value, this.format);
40783         if (!v && this.useIso) {
40784             v = Date.parseDate(value, 'Y-m-d');
40785         }
40786         if (v) {
40787             // 
40788             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40789         }
40790         
40791         
40792         if(!v && this.altFormats){
40793             if(!this.altFormatsArray){
40794                 this.altFormatsArray = this.altFormats.split("|");
40795             }
40796             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40797                 v = Date.parseDate(value, this.altFormatsArray[i]);
40798             }
40799         }
40800         return v;
40801     },
40802
40803     // private
40804     formatDate : function(date, fmt){
40805         return (!date || !(date instanceof Date)) ?
40806                date : date.dateFormat(fmt || this.format);
40807     },
40808
40809     // private
40810     menuListeners : {
40811         select: function(m, d){
40812             this.setValue(d);
40813             this.fireEvent('select', this, d);
40814         },
40815         show : function(){ // retain focus styling
40816             this.onFocus();
40817         },
40818         hide : function(){
40819             this.focus.defer(10, this);
40820             var ml = this.menuListeners;
40821             this.menu.un("select", ml.select,  this);
40822             this.menu.un("show", ml.show,  this);
40823             this.menu.un("hide", ml.hide,  this);
40824         }
40825     },
40826     // private
40827     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40828     onTriggerClick : function(){
40829         if(this.disabled){
40830             return;
40831         }
40832         if(this.menu == null){
40833             this.menu = new Roo.menu.DateMenu();
40834            
40835         }
40836         
40837         Roo.apply(this.menu.picker,  {
40838             
40839             showClear: this.allowBlank,
40840             minDate : this.minValue,
40841             maxDate : this.maxValue,
40842             disabledDatesRE : this.ddMatch,
40843             disabledDatesText : this.disabledDatesText,
40844             
40845             format : this.useIso ? 'Y-m-d' : this.format,
40846             minText : String.format(this.minText, this.formatDate(this.minValue)),
40847             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40848             
40849         });
40850          this.menu.on(Roo.apply({}, this.menuListeners, {
40851             scope:this
40852         }));
40853        
40854         
40855         var m = this.menu;
40856         var p = m.picker;
40857         
40858         // hide month picker get's called when we called by 'before hide';
40859         
40860         var ignorehide = true;
40861         p.hideMonthPicker  = function(disableAnim){
40862             if (ignorehide) {
40863                 return;
40864             }
40865              if(this.monthPicker){
40866                 Roo.log("hideMonthPicker called");
40867                 if(disableAnim === true){
40868                     this.monthPicker.hide();
40869                 }else{
40870                     this.monthPicker.slideOut('t', {duration:.2});
40871                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40872                     p.fireEvent("select", this, this.value);
40873                     m.hide();
40874                 }
40875             }
40876         }
40877         
40878         Roo.log('picker set value');
40879         Roo.log(this.getValue());
40880         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40881         m.show(this.el, 'tl-bl?');
40882         ignorehide  = false;
40883         // this will trigger hideMonthPicker..
40884         
40885         
40886         // hidden the day picker
40887         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40888         
40889         
40890         
40891       
40892         
40893         p.showMonthPicker.defer(100, p);
40894     
40895         
40896        
40897     },
40898
40899     beforeBlur : function(){
40900         var v = this.parseDate(this.getRawValue());
40901         if(v){
40902             this.setValue(v);
40903         }
40904     }
40905
40906     /** @cfg {Boolean} grow @hide */
40907     /** @cfg {Number} growMin @hide */
40908     /** @cfg {Number} growMax @hide */
40909     /**
40910      * @hide
40911      * @method autoSize
40912      */
40913 });/*
40914  * Based on:
40915  * Ext JS Library 1.1.1
40916  * Copyright(c) 2006-2007, Ext JS, LLC.
40917  *
40918  * Originally Released Under LGPL - original licence link has changed is not relivant.
40919  *
40920  * Fork - LGPL
40921  * <script type="text/javascript">
40922  */
40923  
40924
40925 /**
40926  * @class Roo.form.ComboBox
40927  * @extends Roo.form.TriggerField
40928  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40929  * @constructor
40930  * Create a new ComboBox.
40931  * @param {Object} config Configuration options
40932  */
40933 Roo.form.ComboBox = function(config){
40934     Roo.form.ComboBox.superclass.constructor.call(this, config);
40935     this.addEvents({
40936         /**
40937          * @event expand
40938          * Fires when the dropdown list is expanded
40939              * @param {Roo.form.ComboBox} combo This combo box
40940              */
40941         'expand' : true,
40942         /**
40943          * @event collapse
40944          * Fires when the dropdown list is collapsed
40945              * @param {Roo.form.ComboBox} combo This combo box
40946              */
40947         'collapse' : true,
40948         /**
40949          * @event beforeselect
40950          * Fires before a list item is selected. Return false to cancel the selection.
40951              * @param {Roo.form.ComboBox} combo This combo box
40952              * @param {Roo.data.Record} record The data record returned from the underlying store
40953              * @param {Number} index The index of the selected item in the dropdown list
40954              */
40955         'beforeselect' : true,
40956         /**
40957          * @event select
40958          * Fires when a list item is selected
40959              * @param {Roo.form.ComboBox} combo This combo box
40960              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40961              * @param {Number} index The index of the selected item in the dropdown list
40962              */
40963         'select' : true,
40964         /**
40965          * @event beforequery
40966          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40967          * The event object passed has these properties:
40968              * @param {Roo.form.ComboBox} combo This combo box
40969              * @param {String} query The query
40970              * @param {Boolean} forceAll true to force "all" query
40971              * @param {Boolean} cancel true to cancel the query
40972              * @param {Object} e The query event object
40973              */
40974         'beforequery': true,
40975          /**
40976          * @event add
40977          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40978              * @param {Roo.form.ComboBox} combo This combo box
40979              */
40980         'add' : true,
40981         /**
40982          * @event edit
40983          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40984              * @param {Roo.form.ComboBox} combo This combo box
40985              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40986              */
40987         'edit' : true
40988         
40989         
40990     });
40991     if(this.transform){
40992         this.allowDomMove = false;
40993         var s = Roo.getDom(this.transform);
40994         if(!this.hiddenName){
40995             this.hiddenName = s.name;
40996         }
40997         if(!this.store){
40998             this.mode = 'local';
40999             var d = [], opts = s.options;
41000             for(var i = 0, len = opts.length;i < len; i++){
41001                 var o = opts[i];
41002                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41003                 if(o.selected) {
41004                     this.value = value;
41005                 }
41006                 d.push([value, o.text]);
41007             }
41008             this.store = new Roo.data.SimpleStore({
41009                 'id': 0,
41010                 fields: ['value', 'text'],
41011                 data : d
41012             });
41013             this.valueField = 'value';
41014             this.displayField = 'text';
41015         }
41016         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41017         if(!this.lazyRender){
41018             this.target = true;
41019             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41020             s.parentNode.removeChild(s); // remove it
41021             this.render(this.el.parentNode);
41022         }else{
41023             s.parentNode.removeChild(s); // remove it
41024         }
41025
41026     }
41027     if (this.store) {
41028         this.store = Roo.factory(this.store, Roo.data);
41029     }
41030     
41031     this.selectedIndex = -1;
41032     if(this.mode == 'local'){
41033         if(config.queryDelay === undefined){
41034             this.queryDelay = 10;
41035         }
41036         if(config.minChars === undefined){
41037             this.minChars = 0;
41038         }
41039     }
41040 };
41041
41042 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41043     /**
41044      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41045      */
41046     /**
41047      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41048      * rendering into an Roo.Editor, defaults to false)
41049      */
41050     /**
41051      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41052      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41053      */
41054     /**
41055      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41056      */
41057     /**
41058      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41059      * the dropdown list (defaults to undefined, with no header element)
41060      */
41061
41062      /**
41063      * @cfg {String/Roo.Template} tpl The template to use to render the output
41064      */
41065      
41066     // private
41067     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41068     /**
41069      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41070      */
41071     listWidth: undefined,
41072     /**
41073      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41074      * mode = 'remote' or 'text' if mode = 'local')
41075      */
41076     displayField: undefined,
41077     /**
41078      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41079      * mode = 'remote' or 'value' if mode = 'local'). 
41080      * Note: use of a valueField requires the user make a selection
41081      * in order for a value to be mapped.
41082      */
41083     valueField: undefined,
41084     
41085     
41086     /**
41087      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41088      * field's data value (defaults to the underlying DOM element's name)
41089      */
41090     hiddenName: undefined,
41091     /**
41092      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41093      */
41094     listClass: '',
41095     /**
41096      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41097      */
41098     selectedClass: 'x-combo-selected',
41099     /**
41100      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41101      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41102      * which displays a downward arrow icon).
41103      */
41104     triggerClass : 'x-form-arrow-trigger',
41105     /**
41106      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41107      */
41108     shadow:'sides',
41109     /**
41110      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41111      * anchor positions (defaults to 'tl-bl')
41112      */
41113     listAlign: 'tl-bl?',
41114     /**
41115      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41116      */
41117     maxHeight: 300,
41118     /**
41119      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41120      * query specified by the allQuery config option (defaults to 'query')
41121      */
41122     triggerAction: 'query',
41123     /**
41124      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41125      * (defaults to 4, does not apply if editable = false)
41126      */
41127     minChars : 4,
41128     /**
41129      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41130      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41131      */
41132     typeAhead: false,
41133     /**
41134      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41135      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41136      */
41137     queryDelay: 500,
41138     /**
41139      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41140      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41141      */
41142     pageSize: 0,
41143     /**
41144      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41145      * when editable = true (defaults to false)
41146      */
41147     selectOnFocus:false,
41148     /**
41149      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41150      */
41151     queryParam: 'query',
41152     /**
41153      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41154      * when mode = 'remote' (defaults to 'Loading...')
41155      */
41156     loadingText: 'Loading...',
41157     /**
41158      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41159      */
41160     resizable: false,
41161     /**
41162      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41163      */
41164     handleHeight : 8,
41165     /**
41166      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41167      * traditional select (defaults to true)
41168      */
41169     editable: true,
41170     /**
41171      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41172      */
41173     allQuery: '',
41174     /**
41175      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41176      */
41177     mode: 'remote',
41178     /**
41179      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41180      * listWidth has a higher value)
41181      */
41182     minListWidth : 70,
41183     /**
41184      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41185      * allow the user to set arbitrary text into the field (defaults to false)
41186      */
41187     forceSelection:false,
41188     /**
41189      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41190      * if typeAhead = true (defaults to 250)
41191      */
41192     typeAheadDelay : 250,
41193     /**
41194      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41195      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41196      */
41197     valueNotFoundText : undefined,
41198     /**
41199      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41200      */
41201     blockFocus : false,
41202     
41203     /**
41204      * @cfg {Boolean} disableClear Disable showing of clear button.
41205      */
41206     disableClear : false,
41207     /**
41208      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41209      */
41210     alwaysQuery : false,
41211     
41212     //private
41213     addicon : false,
41214     editicon: false,
41215     
41216     // element that contains real text value.. (when hidden is used..)
41217      
41218     // private
41219     onRender : function(ct, position){
41220         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41221         if(this.hiddenName){
41222             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41223                     'before', true);
41224             this.hiddenField.value =
41225                 this.hiddenValue !== undefined ? this.hiddenValue :
41226                 this.value !== undefined ? this.value : '';
41227
41228             // prevent input submission
41229             this.el.dom.removeAttribute('name');
41230              
41231              
41232         }
41233         if(Roo.isGecko){
41234             this.el.dom.setAttribute('autocomplete', 'off');
41235         }
41236
41237         var cls = 'x-combo-list';
41238
41239         this.list = new Roo.Layer({
41240             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41241         });
41242
41243         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41244         this.list.setWidth(lw);
41245         this.list.swallowEvent('mousewheel');
41246         this.assetHeight = 0;
41247
41248         if(this.title){
41249             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41250             this.assetHeight += this.header.getHeight();
41251         }
41252
41253         this.innerList = this.list.createChild({cls:cls+'-inner'});
41254         this.innerList.on('mouseover', this.onViewOver, this);
41255         this.innerList.on('mousemove', this.onViewMove, this);
41256         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41257         
41258         if(this.allowBlank && !this.pageSize && !this.disableClear){
41259             this.footer = this.list.createChild({cls:cls+'-ft'});
41260             this.pageTb = new Roo.Toolbar(this.footer);
41261            
41262         }
41263         if(this.pageSize){
41264             this.footer = this.list.createChild({cls:cls+'-ft'});
41265             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41266                     {pageSize: this.pageSize});
41267             
41268         }
41269         
41270         if (this.pageTb && this.allowBlank && !this.disableClear) {
41271             var _this = this;
41272             this.pageTb.add(new Roo.Toolbar.Fill(), {
41273                 cls: 'x-btn-icon x-btn-clear',
41274                 text: '&#160;',
41275                 handler: function()
41276                 {
41277                     _this.collapse();
41278                     _this.clearValue();
41279                     _this.onSelect(false, -1);
41280                 }
41281             });
41282         }
41283         if (this.footer) {
41284             this.assetHeight += this.footer.getHeight();
41285         }
41286         
41287
41288         if(!this.tpl){
41289             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41290         }
41291
41292         this.view = new Roo.View(this.innerList, this.tpl, {
41293             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41294         });
41295
41296         this.view.on('click', this.onViewClick, this);
41297
41298         this.store.on('beforeload', this.onBeforeLoad, this);
41299         this.store.on('load', this.onLoad, this);
41300         this.store.on('loadexception', this.onLoadException, this);
41301
41302         if(this.resizable){
41303             this.resizer = new Roo.Resizable(this.list,  {
41304                pinned:true, handles:'se'
41305             });
41306             this.resizer.on('resize', function(r, w, h){
41307                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41308                 this.listWidth = w;
41309                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41310                 this.restrictHeight();
41311             }, this);
41312             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41313         }
41314         if(!this.editable){
41315             this.editable = true;
41316             this.setEditable(false);
41317         }  
41318         
41319         
41320         if (typeof(this.events.add.listeners) != 'undefined') {
41321             
41322             this.addicon = this.wrap.createChild(
41323                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41324        
41325             this.addicon.on('click', function(e) {
41326                 this.fireEvent('add', this);
41327             }, this);
41328         }
41329         if (typeof(this.events.edit.listeners) != 'undefined') {
41330             
41331             this.editicon = this.wrap.createChild(
41332                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41333             if (this.addicon) {
41334                 this.editicon.setStyle('margin-left', '40px');
41335             }
41336             this.editicon.on('click', function(e) {
41337                 
41338                 // we fire even  if inothing is selected..
41339                 this.fireEvent('edit', this, this.lastData );
41340                 
41341             }, this);
41342         }
41343         
41344         
41345         
41346     },
41347
41348     // private
41349     initEvents : function(){
41350         Roo.form.ComboBox.superclass.initEvents.call(this);
41351
41352         this.keyNav = new Roo.KeyNav(this.el, {
41353             "up" : function(e){
41354                 this.inKeyMode = true;
41355                 this.selectPrev();
41356             },
41357
41358             "down" : function(e){
41359                 if(!this.isExpanded()){
41360                     this.onTriggerClick();
41361                 }else{
41362                     this.inKeyMode = true;
41363                     this.selectNext();
41364                 }
41365             },
41366
41367             "enter" : function(e){
41368                 this.onViewClick();
41369                 //return true;
41370             },
41371
41372             "esc" : function(e){
41373                 this.collapse();
41374             },
41375
41376             "tab" : function(e){
41377                 this.onViewClick(false);
41378                 this.fireEvent("specialkey", this, e);
41379                 return true;
41380             },
41381
41382             scope : this,
41383
41384             doRelay : function(foo, bar, hname){
41385                 if(hname == 'down' || this.scope.isExpanded()){
41386                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41387                 }
41388                 return true;
41389             },
41390
41391             forceKeyDown: true
41392         });
41393         this.queryDelay = Math.max(this.queryDelay || 10,
41394                 this.mode == 'local' ? 10 : 250);
41395         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41396         if(this.typeAhead){
41397             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41398         }
41399         if(this.editable !== false){
41400             this.el.on("keyup", this.onKeyUp, this);
41401         }
41402         if(this.forceSelection){
41403             this.on('blur', this.doForce, this);
41404         }
41405     },
41406
41407     onDestroy : function(){
41408         if(this.view){
41409             this.view.setStore(null);
41410             this.view.el.removeAllListeners();
41411             this.view.el.remove();
41412             this.view.purgeListeners();
41413         }
41414         if(this.list){
41415             this.list.destroy();
41416         }
41417         if(this.store){
41418             this.store.un('beforeload', this.onBeforeLoad, this);
41419             this.store.un('load', this.onLoad, this);
41420             this.store.un('loadexception', this.onLoadException, this);
41421         }
41422         Roo.form.ComboBox.superclass.onDestroy.call(this);
41423     },
41424
41425     // private
41426     fireKey : function(e){
41427         if(e.isNavKeyPress() && !this.list.isVisible()){
41428             this.fireEvent("specialkey", this, e);
41429         }
41430     },
41431
41432     // private
41433     onResize: function(w, h){
41434         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41435         
41436         if(typeof w != 'number'){
41437             // we do not handle it!?!?
41438             return;
41439         }
41440         var tw = this.trigger.getWidth();
41441         tw += this.addicon ? this.addicon.getWidth() : 0;
41442         tw += this.editicon ? this.editicon.getWidth() : 0;
41443         var x = w - tw;
41444         this.el.setWidth( this.adjustWidth('input', x));
41445             
41446         this.trigger.setStyle('left', x+'px');
41447         
41448         if(this.list && this.listWidth === undefined){
41449             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41450             this.list.setWidth(lw);
41451             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41452         }
41453         
41454     
41455         
41456     },
41457
41458     /**
41459      * Allow or prevent the user from directly editing the field text.  If false is passed,
41460      * the user will only be able to select from the items defined in the dropdown list.  This method
41461      * is the runtime equivalent of setting the 'editable' config option at config time.
41462      * @param {Boolean} value True to allow the user to directly edit the field text
41463      */
41464     setEditable : function(value){
41465         if(value == this.editable){
41466             return;
41467         }
41468         this.editable = value;
41469         if(!value){
41470             this.el.dom.setAttribute('readOnly', true);
41471             this.el.on('mousedown', this.onTriggerClick,  this);
41472             this.el.addClass('x-combo-noedit');
41473         }else{
41474             this.el.dom.setAttribute('readOnly', false);
41475             this.el.un('mousedown', this.onTriggerClick,  this);
41476             this.el.removeClass('x-combo-noedit');
41477         }
41478     },
41479
41480     // private
41481     onBeforeLoad : function(){
41482         if(!this.hasFocus){
41483             return;
41484         }
41485         this.innerList.update(this.loadingText ?
41486                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41487         this.restrictHeight();
41488         this.selectedIndex = -1;
41489     },
41490
41491     // private
41492     onLoad : function(){
41493         if(!this.hasFocus){
41494             return;
41495         }
41496         if(this.store.getCount() > 0){
41497             this.expand();
41498             this.restrictHeight();
41499             if(this.lastQuery == this.allQuery){
41500                 if(this.editable){
41501                     this.el.dom.select();
41502                 }
41503                 if(!this.selectByValue(this.value, true)){
41504                     this.select(0, true);
41505                 }
41506             }else{
41507                 this.selectNext();
41508                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41509                     this.taTask.delay(this.typeAheadDelay);
41510                 }
41511             }
41512         }else{
41513             this.onEmptyResults();
41514         }
41515         //this.el.focus();
41516     },
41517     // private
41518     onLoadException : function()
41519     {
41520         this.collapse();
41521         Roo.log(this.store.reader.jsonData);
41522         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41523             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41524         }
41525         
41526         
41527     },
41528     // private
41529     onTypeAhead : function(){
41530         if(this.store.getCount() > 0){
41531             var r = this.store.getAt(0);
41532             var newValue = r.data[this.displayField];
41533             var len = newValue.length;
41534             var selStart = this.getRawValue().length;
41535             if(selStart != len){
41536                 this.setRawValue(newValue);
41537                 this.selectText(selStart, newValue.length);
41538             }
41539         }
41540     },
41541
41542     // private
41543     onSelect : function(record, index){
41544         if(this.fireEvent('beforeselect', this, record, index) !== false){
41545             this.setFromData(index > -1 ? record.data : false);
41546             this.collapse();
41547             this.fireEvent('select', this, record, index);
41548         }
41549     },
41550
41551     /**
41552      * Returns the currently selected field value or empty string if no value is set.
41553      * @return {String} value The selected value
41554      */
41555     getValue : function(){
41556         if(this.valueField){
41557             return typeof this.value != 'undefined' ? this.value : '';
41558         }
41559         return Roo.form.ComboBox.superclass.getValue.call(this);
41560     },
41561
41562     /**
41563      * Clears any text/value currently set in the field
41564      */
41565     clearValue : function(){
41566         if(this.hiddenField){
41567             this.hiddenField.value = '';
41568         }
41569         this.value = '';
41570         this.setRawValue('');
41571         this.lastSelectionText = '';
41572         
41573     },
41574
41575     /**
41576      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41577      * will be displayed in the field.  If the value does not match the data value of an existing item,
41578      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41579      * Otherwise the field will be blank (although the value will still be set).
41580      * @param {String} value The value to match
41581      */
41582     setValue : function(v){
41583         var text = v;
41584         if(this.valueField){
41585             var r = this.findRecord(this.valueField, v);
41586             if(r){
41587                 text = r.data[this.displayField];
41588             }else if(this.valueNotFoundText !== undefined){
41589                 text = this.valueNotFoundText;
41590             }
41591         }
41592         this.lastSelectionText = text;
41593         if(this.hiddenField){
41594             this.hiddenField.value = v;
41595         }
41596         Roo.form.ComboBox.superclass.setValue.call(this, text);
41597         this.value = v;
41598     },
41599     /**
41600      * @property {Object} the last set data for the element
41601      */
41602     
41603     lastData : false,
41604     /**
41605      * Sets the value of the field based on a object which is related to the record format for the store.
41606      * @param {Object} value the value to set as. or false on reset?
41607      */
41608     setFromData : function(o){
41609         var dv = ''; // display value
41610         var vv = ''; // value value..
41611         this.lastData = o;
41612         if (this.displayField) {
41613             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41614         } else {
41615             // this is an error condition!!!
41616             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41617         }
41618         
41619         if(this.valueField){
41620             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41621         }
41622         if(this.hiddenField){
41623             this.hiddenField.value = vv;
41624             
41625             this.lastSelectionText = dv;
41626             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41627             this.value = vv;
41628             return;
41629         }
41630         // no hidden field.. - we store the value in 'value', but still display
41631         // display field!!!!
41632         this.lastSelectionText = dv;
41633         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41634         this.value = vv;
41635         
41636         
41637     },
41638     // private
41639     reset : function(){
41640         // overridden so that last data is reset..
41641         this.setValue(this.resetValue);
41642         this.originalValue = this.getValue();
41643         this.clearInvalid();
41644         this.lastData = false;
41645         if (this.view) {
41646             this.view.clearSelections();
41647         }
41648     },
41649     // private
41650     findRecord : function(prop, value){
41651         var record;
41652         if(this.store.getCount() > 0){
41653             this.store.each(function(r){
41654                 if(r.data[prop] == value){
41655                     record = r;
41656                     return false;
41657                 }
41658                 return true;
41659             });
41660         }
41661         return record;
41662     },
41663     
41664     getName: function()
41665     {
41666         // returns hidden if it's set..
41667         if (!this.rendered) {return ''};
41668         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41669         
41670     },
41671     // private
41672     onViewMove : function(e, t){
41673         this.inKeyMode = false;
41674     },
41675
41676     // private
41677     onViewOver : function(e, t){
41678         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41679             return;
41680         }
41681         var item = this.view.findItemFromChild(t);
41682         if(item){
41683             var index = this.view.indexOf(item);
41684             this.select(index, false);
41685         }
41686     },
41687
41688     // private
41689     onViewClick : function(doFocus)
41690     {
41691         var index = this.view.getSelectedIndexes()[0];
41692         var r = this.store.getAt(index);
41693         if(r){
41694             this.onSelect(r, index);
41695         }
41696         if(doFocus !== false && !this.blockFocus){
41697             this.el.focus();
41698         }
41699     },
41700
41701     // private
41702     restrictHeight : function(){
41703         this.innerList.dom.style.height = '';
41704         var inner = this.innerList.dom;
41705         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41706         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41707         this.list.beginUpdate();
41708         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41709         this.list.alignTo(this.el, this.listAlign);
41710         this.list.endUpdate();
41711     },
41712
41713     // private
41714     onEmptyResults : function(){
41715         this.collapse();
41716     },
41717
41718     /**
41719      * Returns true if the dropdown list is expanded, else false.
41720      */
41721     isExpanded : function(){
41722         return this.list.isVisible();
41723     },
41724
41725     /**
41726      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41727      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41728      * @param {String} value The data value of the item to select
41729      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41730      * selected item if it is not currently in view (defaults to true)
41731      * @return {Boolean} True if the value matched an item in the list, else false
41732      */
41733     selectByValue : function(v, scrollIntoView){
41734         if(v !== undefined && v !== null){
41735             var r = this.findRecord(this.valueField || this.displayField, v);
41736             if(r){
41737                 this.select(this.store.indexOf(r), scrollIntoView);
41738                 return true;
41739             }
41740         }
41741         return false;
41742     },
41743
41744     /**
41745      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41746      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41747      * @param {Number} index The zero-based index of the list item to select
41748      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41749      * selected item if it is not currently in view (defaults to true)
41750      */
41751     select : function(index, scrollIntoView){
41752         this.selectedIndex = index;
41753         this.view.select(index);
41754         if(scrollIntoView !== false){
41755             var el = this.view.getNode(index);
41756             if(el){
41757                 this.innerList.scrollChildIntoView(el, false);
41758             }
41759         }
41760     },
41761
41762     // private
41763     selectNext : function(){
41764         var ct = this.store.getCount();
41765         if(ct > 0){
41766             if(this.selectedIndex == -1){
41767                 this.select(0);
41768             }else if(this.selectedIndex < ct-1){
41769                 this.select(this.selectedIndex+1);
41770             }
41771         }
41772     },
41773
41774     // private
41775     selectPrev : function(){
41776         var ct = this.store.getCount();
41777         if(ct > 0){
41778             if(this.selectedIndex == -1){
41779                 this.select(0);
41780             }else if(this.selectedIndex != 0){
41781                 this.select(this.selectedIndex-1);
41782             }
41783         }
41784     },
41785
41786     // private
41787     onKeyUp : function(e){
41788         if(this.editable !== false && !e.isSpecialKey()){
41789             this.lastKey = e.getKey();
41790             this.dqTask.delay(this.queryDelay);
41791         }
41792     },
41793
41794     // private
41795     validateBlur : function(){
41796         return !this.list || !this.list.isVisible();   
41797     },
41798
41799     // private
41800     initQuery : function(){
41801         this.doQuery(this.getRawValue());
41802     },
41803
41804     // private
41805     doForce : function(){
41806         if(this.el.dom.value.length > 0){
41807             this.el.dom.value =
41808                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41809              
41810         }
41811     },
41812
41813     /**
41814      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41815      * query allowing the query action to be canceled if needed.
41816      * @param {String} query The SQL query to execute
41817      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41818      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41819      * saved in the current store (defaults to false)
41820      */
41821     doQuery : function(q, forceAll){
41822         if(q === undefined || q === null){
41823             q = '';
41824         }
41825         var qe = {
41826             query: q,
41827             forceAll: forceAll,
41828             combo: this,
41829             cancel:false
41830         };
41831         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41832             return false;
41833         }
41834         q = qe.query;
41835         forceAll = qe.forceAll;
41836         if(forceAll === true || (q.length >= this.minChars)){
41837             if(this.lastQuery != q || this.alwaysQuery){
41838                 this.lastQuery = q;
41839                 if(this.mode == 'local'){
41840                     this.selectedIndex = -1;
41841                     if(forceAll){
41842                         this.store.clearFilter();
41843                     }else{
41844                         this.store.filter(this.displayField, q);
41845                     }
41846                     this.onLoad();
41847                 }else{
41848                     this.store.baseParams[this.queryParam] = q;
41849                     this.store.load({
41850                         params: this.getParams(q)
41851                     });
41852                     this.expand();
41853                 }
41854             }else{
41855                 this.selectedIndex = -1;
41856                 this.onLoad();   
41857             }
41858         }
41859     },
41860
41861     // private
41862     getParams : function(q){
41863         var p = {};
41864         //p[this.queryParam] = q;
41865         if(this.pageSize){
41866             p.start = 0;
41867             p.limit = this.pageSize;
41868         }
41869         return p;
41870     },
41871
41872     /**
41873      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41874      */
41875     collapse : function(){
41876         if(!this.isExpanded()){
41877             return;
41878         }
41879         this.list.hide();
41880         Roo.get(document).un('mousedown', this.collapseIf, this);
41881         Roo.get(document).un('mousewheel', this.collapseIf, this);
41882         if (!this.editable) {
41883             Roo.get(document).un('keydown', this.listKeyPress, this);
41884         }
41885         this.fireEvent('collapse', this);
41886     },
41887
41888     // private
41889     collapseIf : function(e){
41890         if(!e.within(this.wrap) && !e.within(this.list)){
41891             this.collapse();
41892         }
41893     },
41894
41895     /**
41896      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41897      */
41898     expand : function(){
41899         if(this.isExpanded() || !this.hasFocus){
41900             return;
41901         }
41902         this.list.alignTo(this.el, this.listAlign);
41903         this.list.show();
41904         Roo.get(document).on('mousedown', this.collapseIf, this);
41905         Roo.get(document).on('mousewheel', this.collapseIf, this);
41906         if (!this.editable) {
41907             Roo.get(document).on('keydown', this.listKeyPress, this);
41908         }
41909         
41910         this.fireEvent('expand', this);
41911     },
41912
41913     // private
41914     // Implements the default empty TriggerField.onTriggerClick function
41915     onTriggerClick : function(){
41916         if(this.disabled){
41917             return;
41918         }
41919         if(this.isExpanded()){
41920             this.collapse();
41921             if (!this.blockFocus) {
41922                 this.el.focus();
41923             }
41924             
41925         }else {
41926             this.hasFocus = true;
41927             if(this.triggerAction == 'all') {
41928                 this.doQuery(this.allQuery, true);
41929             } else {
41930                 this.doQuery(this.getRawValue());
41931             }
41932             if (!this.blockFocus) {
41933                 this.el.focus();
41934             }
41935         }
41936     },
41937     listKeyPress : function(e)
41938     {
41939         //Roo.log('listkeypress');
41940         // scroll to first matching element based on key pres..
41941         if (e.isSpecialKey()) {
41942             return false;
41943         }
41944         var k = String.fromCharCode(e.getKey()).toUpperCase();
41945         //Roo.log(k);
41946         var match  = false;
41947         var csel = this.view.getSelectedNodes();
41948         var cselitem = false;
41949         if (csel.length) {
41950             var ix = this.view.indexOf(csel[0]);
41951             cselitem  = this.store.getAt(ix);
41952             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41953                 cselitem = false;
41954             }
41955             
41956         }
41957         
41958         this.store.each(function(v) { 
41959             if (cselitem) {
41960                 // start at existing selection.
41961                 if (cselitem.id == v.id) {
41962                     cselitem = false;
41963                 }
41964                 return;
41965             }
41966                 
41967             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41968                 match = this.store.indexOf(v);
41969                 return false;
41970             }
41971         }, this);
41972         
41973         if (match === false) {
41974             return true; // no more action?
41975         }
41976         // scroll to?
41977         this.view.select(match);
41978         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41979         sn.scrollIntoView(sn.dom.parentNode, false);
41980     }
41981
41982     /** 
41983     * @cfg {Boolean} grow 
41984     * @hide 
41985     */
41986     /** 
41987     * @cfg {Number} growMin 
41988     * @hide 
41989     */
41990     /** 
41991     * @cfg {Number} growMax 
41992     * @hide 
41993     */
41994     /**
41995      * @hide
41996      * @method autoSize
41997      */
41998 });/*
41999  * Copyright(c) 2010-2012, Roo J Solutions Limited
42000  *
42001  * Licence LGPL
42002  *
42003  */
42004
42005 /**
42006  * @class Roo.form.ComboBoxArray
42007  * @extends Roo.form.TextField
42008  * A facebook style adder... for lists of email / people / countries  etc...
42009  * pick multiple items from a combo box, and shows each one.
42010  *
42011  *  Fred [x]  Brian [x]  [Pick another |v]
42012  *
42013  *
42014  *  For this to work: it needs various extra information
42015  *    - normal combo problay has
42016  *      name, hiddenName
42017  *    + displayField, valueField
42018  *
42019  *    For our purpose...
42020  *
42021  *
42022  *   If we change from 'extends' to wrapping...
42023  *   
42024  *  
42025  *
42026  
42027  
42028  * @constructor
42029  * Create a new ComboBoxArray.
42030  * @param {Object} config Configuration options
42031  */
42032  
42033
42034 Roo.form.ComboBoxArray = function(config)
42035 {
42036     this.addEvents({
42037         /**
42038          * @event beforeremove
42039          * Fires before remove the value from the list
42040              * @param {Roo.form.ComboBoxArray} _self This combo box array
42041              * @param {Roo.form.ComboBoxArray.Item} item removed item
42042              */
42043         'beforeremove' : true,
42044         /**
42045          * @event remove
42046          * Fires when remove the value from the list
42047              * @param {Roo.form.ComboBoxArray} _self This combo box array
42048              * @param {Roo.form.ComboBoxArray.Item} item removed item
42049              */
42050         'remove' : true
42051         
42052         
42053     });
42054     
42055     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42056     
42057     this.items = new Roo.util.MixedCollection(false);
42058     
42059     // construct the child combo...
42060     
42061     
42062     
42063     
42064    
42065     
42066 }
42067
42068  
42069 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42070
42071     /**
42072      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42073      */
42074     
42075     lastData : false,
42076     
42077     // behavies liek a hiddne field
42078     inputType:      'hidden',
42079     /**
42080      * @cfg {Number} width The width of the box that displays the selected element
42081      */ 
42082     width:          300,
42083
42084     
42085     
42086     /**
42087      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42088      */
42089     name : false,
42090     /**
42091      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42092      */
42093     hiddenName : false,
42094     
42095     
42096     // private the array of items that are displayed..
42097     items  : false,
42098     // private - the hidden field el.
42099     hiddenEl : false,
42100     // private - the filed el..
42101     el : false,
42102     
42103     //validateValue : function() { return true; }, // all values are ok!
42104     //onAddClick: function() { },
42105     
42106     onRender : function(ct, position) 
42107     {
42108         
42109         // create the standard hidden element
42110         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42111         
42112         
42113         // give fake names to child combo;
42114         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42115         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42116         
42117         this.combo = Roo.factory(this.combo, Roo.form);
42118         this.combo.onRender(ct, position);
42119         if (typeof(this.combo.width) != 'undefined') {
42120             this.combo.onResize(this.combo.width,0);
42121         }
42122         
42123         this.combo.initEvents();
42124         
42125         // assigned so form know we need to do this..
42126         this.store          = this.combo.store;
42127         this.valueField     = this.combo.valueField;
42128         this.displayField   = this.combo.displayField ;
42129         
42130         
42131         this.combo.wrap.addClass('x-cbarray-grp');
42132         
42133         var cbwrap = this.combo.wrap.createChild(
42134             {tag: 'div', cls: 'x-cbarray-cb'},
42135             this.combo.el.dom
42136         );
42137         
42138              
42139         this.hiddenEl = this.combo.wrap.createChild({
42140             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42141         });
42142         this.el = this.combo.wrap.createChild({
42143             tag: 'input',  type:'hidden' , name: this.name, value : ''
42144         });
42145          //   this.el.dom.removeAttribute("name");
42146         
42147         
42148         this.outerWrap = this.combo.wrap;
42149         this.wrap = cbwrap;
42150         
42151         this.outerWrap.setWidth(this.width);
42152         this.outerWrap.dom.removeChild(this.el.dom);
42153         
42154         this.wrap.dom.appendChild(this.el.dom);
42155         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42156         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42157         
42158         this.combo.trigger.setStyle('position','relative');
42159         this.combo.trigger.setStyle('left', '0px');
42160         this.combo.trigger.setStyle('top', '2px');
42161         
42162         this.combo.el.setStyle('vertical-align', 'text-bottom');
42163         
42164         //this.trigger.setStyle('vertical-align', 'top');
42165         
42166         // this should use the code from combo really... on('add' ....)
42167         if (this.adder) {
42168             
42169         
42170             this.adder = this.outerWrap.createChild(
42171                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42172             var _t = this;
42173             this.adder.on('click', function(e) {
42174                 _t.fireEvent('adderclick', this, e);
42175             }, _t);
42176         }
42177         //var _t = this;
42178         //this.adder.on('click', this.onAddClick, _t);
42179         
42180         
42181         this.combo.on('select', function(cb, rec, ix) {
42182             this.addItem(rec.data);
42183             
42184             cb.setValue('');
42185             cb.el.dom.value = '';
42186             //cb.lastData = rec.data;
42187             // add to list
42188             
42189         }, this);
42190         
42191         
42192     },
42193     
42194     
42195     getName: function()
42196     {
42197         // returns hidden if it's set..
42198         if (!this.rendered) {return ''};
42199         return  this.hiddenName ? this.hiddenName : this.name;
42200         
42201     },
42202     
42203     
42204     onResize: function(w, h){
42205         
42206         return;
42207         // not sure if this is needed..
42208         //this.combo.onResize(w,h);
42209         
42210         if(typeof w != 'number'){
42211             // we do not handle it!?!?
42212             return;
42213         }
42214         var tw = this.combo.trigger.getWidth();
42215         tw += this.addicon ? this.addicon.getWidth() : 0;
42216         tw += this.editicon ? this.editicon.getWidth() : 0;
42217         var x = w - tw;
42218         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42219             
42220         this.combo.trigger.setStyle('left', '0px');
42221         
42222         if(this.list && this.listWidth === undefined){
42223             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42224             this.list.setWidth(lw);
42225             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42226         }
42227         
42228     
42229         
42230     },
42231     
42232     addItem: function(rec)
42233     {
42234         var valueField = this.combo.valueField;
42235         var displayField = this.combo.displayField;
42236         if (this.items.indexOfKey(rec[valueField]) > -1) {
42237             //console.log("GOT " + rec.data.id);
42238             return;
42239         }
42240         
42241         var x = new Roo.form.ComboBoxArray.Item({
42242             //id : rec[this.idField],
42243             data : rec,
42244             displayField : displayField ,
42245             tipField : displayField ,
42246             cb : this
42247         });
42248         // use the 
42249         this.items.add(rec[valueField],x);
42250         // add it before the element..
42251         this.updateHiddenEl();
42252         x.render(this.outerWrap, this.wrap.dom);
42253         // add the image handler..
42254     },
42255     
42256     updateHiddenEl : function()
42257     {
42258         this.validate();
42259         if (!this.hiddenEl) {
42260             return;
42261         }
42262         var ar = [];
42263         var idField = this.combo.valueField;
42264         
42265         this.items.each(function(f) {
42266             ar.push(f.data[idField]);
42267            
42268         });
42269         this.hiddenEl.dom.value = ar.join(',');
42270         this.validate();
42271     },
42272     
42273     reset : function()
42274     {
42275         this.items.clear();
42276         
42277         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42278            el.remove();
42279         });
42280         
42281         this.el.dom.value = '';
42282         if (this.hiddenEl) {
42283             this.hiddenEl.dom.value = '';
42284         }
42285         
42286     },
42287     getValue: function()
42288     {
42289         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42290     },
42291     setValue: function(v) // not a valid action - must use addItems..
42292     {
42293          
42294         this.reset();
42295         
42296         
42297         
42298         if (this.store.isLocal && (typeof(v) == 'string')) {
42299             // then we can use the store to find the values..
42300             // comma seperated at present.. this needs to allow JSON based encoding..
42301             this.hiddenEl.value  = v;
42302             var v_ar = [];
42303             Roo.each(v.split(','), function(k) {
42304                 Roo.log("CHECK " + this.valueField + ',' + k);
42305                 var li = this.store.query(this.valueField, k);
42306                 if (!li.length) {
42307                     return;
42308                 }
42309                 var add = {};
42310                 add[this.valueField] = k;
42311                 add[this.displayField] = li.item(0).data[this.displayField];
42312                 
42313                 this.addItem(add);
42314             }, this) 
42315              
42316         }
42317         if (typeof(v) == 'object' ) {
42318             // then let's assume it's an array of objects..
42319             Roo.each(v, function(l) {
42320                 this.addItem(l);
42321             }, this);
42322              
42323         }
42324         
42325         
42326     },
42327     setFromData: function(v)
42328     {
42329         // this recieves an object, if setValues is called.
42330         this.reset();
42331         this.el.dom.value = v[this.displayField];
42332         this.hiddenEl.dom.value = v[this.valueField];
42333         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42334             return;
42335         }
42336         var kv = v[this.valueField];
42337         var dv = v[this.displayField];
42338         kv = typeof(kv) != 'string' ? '' : kv;
42339         dv = typeof(dv) != 'string' ? '' : dv;
42340         
42341         
42342         var keys = kv.split(',');
42343         var display = dv.split(',');
42344         for (var i = 0 ; i < keys.length; i++) {
42345             
42346             add = {};
42347             add[this.valueField] = keys[i];
42348             add[this.displayField] = display[i];
42349             this.addItem(add);
42350         }
42351       
42352         
42353     },
42354     
42355     /**
42356      * Validates the combox array value
42357      * @return {Boolean} True if the value is valid, else false
42358      */
42359     validate : function(){
42360         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42361             this.clearInvalid();
42362             return true;
42363         }
42364         return false;
42365     },
42366     
42367     validateValue : function(value){
42368         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42369         
42370     },
42371     
42372     /*@
42373      * overide
42374      * 
42375      */
42376     isDirty : function() {
42377         if(this.disabled) {
42378             return false;
42379         }
42380         
42381         try {
42382             var d = Roo.decode(String(this.originalValue));
42383         } catch (e) {
42384             return String(this.getValue()) !== String(this.originalValue);
42385         }
42386         
42387         var originalValue = [];
42388         
42389         for (var i = 0; i < d.length; i++){
42390             originalValue.push(d[i][this.valueField]);
42391         }
42392         
42393         return String(this.getValue()) !== String(originalValue.join(','));
42394         
42395     }
42396     
42397 });
42398
42399
42400
42401 /**
42402  * @class Roo.form.ComboBoxArray.Item
42403  * @extends Roo.BoxComponent
42404  * A selected item in the list
42405  *  Fred [x]  Brian [x]  [Pick another |v]
42406  * 
42407  * @constructor
42408  * Create a new item.
42409  * @param {Object} config Configuration options
42410  */
42411  
42412 Roo.form.ComboBoxArray.Item = function(config) {
42413     config.id = Roo.id();
42414     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42415 }
42416
42417 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42418     data : {},
42419     cb: false,
42420     displayField : false,
42421     tipField : false,
42422     
42423     
42424     defaultAutoCreate : {
42425         tag: 'div',
42426         cls: 'x-cbarray-item',
42427         cn : [ 
42428             { tag: 'div' },
42429             {
42430                 tag: 'img',
42431                 width:16,
42432                 height : 16,
42433                 src : Roo.BLANK_IMAGE_URL ,
42434                 align: 'center'
42435             }
42436         ]
42437         
42438     },
42439     
42440  
42441     onRender : function(ct, position)
42442     {
42443         Roo.form.Field.superclass.onRender.call(this, ct, position);
42444         
42445         if(!this.el){
42446             var cfg = this.getAutoCreate();
42447             this.el = ct.createChild(cfg, position);
42448         }
42449         
42450         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42451         
42452         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42453             this.cb.renderer(this.data) :
42454             String.format('{0}',this.data[this.displayField]);
42455         
42456             
42457         this.el.child('div').dom.setAttribute('qtip',
42458                         String.format('{0}',this.data[this.tipField])
42459         );
42460         
42461         this.el.child('img').on('click', this.remove, this);
42462         
42463     },
42464    
42465     remove : function()
42466     {
42467         if(this.cb.disabled){
42468             return;
42469         }
42470         
42471         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42472             this.cb.items.remove(this);
42473             this.el.child('img').un('click', this.remove, this);
42474             this.el.remove();
42475             this.cb.updateHiddenEl();
42476
42477             this.cb.fireEvent('remove', this.cb, this);
42478         }
42479         
42480     }
42481 });/*
42482  * Based on:
42483  * Ext JS Library 1.1.1
42484  * Copyright(c) 2006-2007, Ext JS, LLC.
42485  *
42486  * Originally Released Under LGPL - original licence link has changed is not relivant.
42487  *
42488  * Fork - LGPL
42489  * <script type="text/javascript">
42490  */
42491 /**
42492  * @class Roo.form.Checkbox
42493  * @extends Roo.form.Field
42494  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42495  * @constructor
42496  * Creates a new Checkbox
42497  * @param {Object} config Configuration options
42498  */
42499 Roo.form.Checkbox = function(config){
42500     Roo.form.Checkbox.superclass.constructor.call(this, config);
42501     this.addEvents({
42502         /**
42503          * @event check
42504          * Fires when the checkbox is checked or unchecked.
42505              * @param {Roo.form.Checkbox} this This checkbox
42506              * @param {Boolean} checked The new checked value
42507              */
42508         check : true
42509     });
42510 };
42511
42512 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42513     /**
42514      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42515      */
42516     focusClass : undefined,
42517     /**
42518      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42519      */
42520     fieldClass: "x-form-field",
42521     /**
42522      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42523      */
42524     checked: false,
42525     /**
42526      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42527      * {tag: "input", type: "checkbox", autocomplete: "off"})
42528      */
42529     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42530     /**
42531      * @cfg {String} boxLabel The text that appears beside the checkbox
42532      */
42533     boxLabel : "",
42534     /**
42535      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42536      */  
42537     inputValue : '1',
42538     /**
42539      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42540      */
42541      valueOff: '0', // value when not checked..
42542
42543     actionMode : 'viewEl', 
42544     //
42545     // private
42546     itemCls : 'x-menu-check-item x-form-item',
42547     groupClass : 'x-menu-group-item',
42548     inputType : 'hidden',
42549     
42550     
42551     inSetChecked: false, // check that we are not calling self...
42552     
42553     inputElement: false, // real input element?
42554     basedOn: false, // ????
42555     
42556     isFormField: true, // not sure where this is needed!!!!
42557
42558     onResize : function(){
42559         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42560         if(!this.boxLabel){
42561             this.el.alignTo(this.wrap, 'c-c');
42562         }
42563     },
42564
42565     initEvents : function(){
42566         Roo.form.Checkbox.superclass.initEvents.call(this);
42567         this.el.on("click", this.onClick,  this);
42568         this.el.on("change", this.onClick,  this);
42569     },
42570
42571
42572     getResizeEl : function(){
42573         return this.wrap;
42574     },
42575
42576     getPositionEl : function(){
42577         return this.wrap;
42578     },
42579
42580     // private
42581     onRender : function(ct, position){
42582         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42583         /*
42584         if(this.inputValue !== undefined){
42585             this.el.dom.value = this.inputValue;
42586         }
42587         */
42588         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42589         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42590         var viewEl = this.wrap.createChild({ 
42591             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42592         this.viewEl = viewEl;   
42593         this.wrap.on('click', this.onClick,  this); 
42594         
42595         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42596         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42597         
42598         
42599         
42600         if(this.boxLabel){
42601             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42602         //    viewEl.on('click', this.onClick,  this); 
42603         }
42604         //if(this.checked){
42605             this.setChecked(this.checked);
42606         //}else{
42607             //this.checked = this.el.dom;
42608         //}
42609
42610     },
42611
42612     // private
42613     initValue : Roo.emptyFn,
42614
42615     /**
42616      * Returns the checked state of the checkbox.
42617      * @return {Boolean} True if checked, else false
42618      */
42619     getValue : function(){
42620         if(this.el){
42621             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42622         }
42623         return this.valueOff;
42624         
42625     },
42626
42627         // private
42628     onClick : function(){ 
42629         if (this.disabled) {
42630             return;
42631         }
42632         this.setChecked(!this.checked);
42633
42634         //if(this.el.dom.checked != this.checked){
42635         //    this.setValue(this.el.dom.checked);
42636        // }
42637     },
42638
42639     /**
42640      * Sets the checked state of the checkbox.
42641      * On is always based on a string comparison between inputValue and the param.
42642      * @param {Boolean/String} value - the value to set 
42643      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42644      */
42645     setValue : function(v,suppressEvent){
42646         
42647         
42648         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42649         //if(this.el && this.el.dom){
42650         //    this.el.dom.checked = this.checked;
42651         //    this.el.dom.defaultChecked = this.checked;
42652         //}
42653         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42654         //this.fireEvent("check", this, this.checked);
42655     },
42656     // private..
42657     setChecked : function(state,suppressEvent)
42658     {
42659         if (this.inSetChecked) {
42660             this.checked = state;
42661             return;
42662         }
42663         
42664     
42665         if(this.wrap){
42666             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42667         }
42668         this.checked = state;
42669         if(suppressEvent !== true){
42670             this.fireEvent('check', this, state);
42671         }
42672         this.inSetChecked = true;
42673         this.el.dom.value = state ? this.inputValue : this.valueOff;
42674         this.inSetChecked = false;
42675         
42676     },
42677     // handle setting of hidden value by some other method!!?!?
42678     setFromHidden: function()
42679     {
42680         if(!this.el){
42681             return;
42682         }
42683         //console.log("SET FROM HIDDEN");
42684         //alert('setFrom hidden');
42685         this.setValue(this.el.dom.value);
42686     },
42687     
42688     onDestroy : function()
42689     {
42690         if(this.viewEl){
42691             Roo.get(this.viewEl).remove();
42692         }
42693          
42694         Roo.form.Checkbox.superclass.onDestroy.call(this);
42695     },
42696     
42697     setBoxLabel : function(str)
42698     {
42699         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42700     }
42701
42702 });/*
42703  * Based on:
42704  * Ext JS Library 1.1.1
42705  * Copyright(c) 2006-2007, Ext JS, LLC.
42706  *
42707  * Originally Released Under LGPL - original licence link has changed is not relivant.
42708  *
42709  * Fork - LGPL
42710  * <script type="text/javascript">
42711  */
42712  
42713 /**
42714  * @class Roo.form.Radio
42715  * @extends Roo.form.Checkbox
42716  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42717  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42718  * @constructor
42719  * Creates a new Radio
42720  * @param {Object} config Configuration options
42721  */
42722 Roo.form.Radio = function(){
42723     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42724 };
42725 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42726     inputType: 'radio',
42727
42728     /**
42729      * If this radio is part of a group, it will return the selected value
42730      * @return {String}
42731      */
42732     getGroupValue : function(){
42733         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42734     },
42735     
42736     
42737     onRender : function(ct, position){
42738         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42739         
42740         if(this.inputValue !== undefined){
42741             this.el.dom.value = this.inputValue;
42742         }
42743          
42744         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42745         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42746         //var viewEl = this.wrap.createChild({ 
42747         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42748         //this.viewEl = viewEl;   
42749         //this.wrap.on('click', this.onClick,  this); 
42750         
42751         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42752         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42753         
42754         
42755         
42756         if(this.boxLabel){
42757             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42758         //    viewEl.on('click', this.onClick,  this); 
42759         }
42760          if(this.checked){
42761             this.el.dom.checked =   'checked' ;
42762         }
42763          
42764     } 
42765     
42766     
42767 });//<script type="text/javascript">
42768
42769 /*
42770  * Based  Ext JS Library 1.1.1
42771  * Copyright(c) 2006-2007, Ext JS, LLC.
42772  * LGPL
42773  *
42774  */
42775  
42776 /**
42777  * @class Roo.HtmlEditorCore
42778  * @extends Roo.Component
42779  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42780  *
42781  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42782  */
42783
42784 Roo.HtmlEditorCore = function(config){
42785     
42786     
42787     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42788     
42789     
42790     this.addEvents({
42791         /**
42792          * @event initialize
42793          * Fires when the editor is fully initialized (including the iframe)
42794          * @param {Roo.HtmlEditorCore} this
42795          */
42796         initialize: true,
42797         /**
42798          * @event activate
42799          * Fires when the editor is first receives the focus. Any insertion must wait
42800          * until after this event.
42801          * @param {Roo.HtmlEditorCore} this
42802          */
42803         activate: true,
42804          /**
42805          * @event beforesync
42806          * Fires before the textarea is updated with content from the editor iframe. Return false
42807          * to cancel the sync.
42808          * @param {Roo.HtmlEditorCore} this
42809          * @param {String} html
42810          */
42811         beforesync: true,
42812          /**
42813          * @event beforepush
42814          * Fires before the iframe editor is updated with content from the textarea. Return false
42815          * to cancel the push.
42816          * @param {Roo.HtmlEditorCore} this
42817          * @param {String} html
42818          */
42819         beforepush: true,
42820          /**
42821          * @event sync
42822          * Fires when the textarea is updated with content from the editor iframe.
42823          * @param {Roo.HtmlEditorCore} this
42824          * @param {String} html
42825          */
42826         sync: true,
42827          /**
42828          * @event push
42829          * Fires when the iframe editor is updated with content from the textarea.
42830          * @param {Roo.HtmlEditorCore} this
42831          * @param {String} html
42832          */
42833         push: true,
42834         
42835         /**
42836          * @event editorevent
42837          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42838          * @param {Roo.HtmlEditorCore} this
42839          */
42840         editorevent: true
42841         
42842     });
42843     
42844     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42845     
42846     // defaults : white / black...
42847     this.applyBlacklists();
42848     
42849     
42850     
42851 };
42852
42853
42854 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42855
42856
42857      /**
42858      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42859      */
42860     
42861     owner : false,
42862     
42863      /**
42864      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42865      *                        Roo.resizable.
42866      */
42867     resizable : false,
42868      /**
42869      * @cfg {Number} height (in pixels)
42870      */   
42871     height: 300,
42872    /**
42873      * @cfg {Number} width (in pixels)
42874      */   
42875     width: 500,
42876     
42877     /**
42878      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42879      * 
42880      */
42881     stylesheets: false,
42882     
42883     // id of frame..
42884     frameId: false,
42885     
42886     // private properties
42887     validationEvent : false,
42888     deferHeight: true,
42889     initialized : false,
42890     activated : false,
42891     sourceEditMode : false,
42892     onFocus : Roo.emptyFn,
42893     iframePad:3,
42894     hideMode:'offsets',
42895     
42896     clearUp: true,
42897     
42898     // blacklist + whitelisted elements..
42899     black: false,
42900     white: false,
42901      
42902     bodyCls : '',
42903
42904     /**
42905      * Protected method that will not generally be called directly. It
42906      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42907      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42908      */
42909     getDocMarkup : function(){
42910         // body styles..
42911         var st = '';
42912         
42913         // inherit styels from page...?? 
42914         if (this.stylesheets === false) {
42915             
42916             Roo.get(document.head).select('style').each(function(node) {
42917                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42918             });
42919             
42920             Roo.get(document.head).select('link').each(function(node) { 
42921                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42922             });
42923             
42924         } else if (!this.stylesheets.length) {
42925                 // simple..
42926                 st = '<style type="text/css">' +
42927                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42928                    '</style>';
42929         } else { 
42930             st = '<style type="text/css">' +
42931                     this.stylesheets +
42932                 '</style>';
42933         }
42934         
42935         st +=  '<style type="text/css">' +
42936             'IMG { cursor: pointer } ' +
42937         '</style>';
42938
42939         var cls = 'roo-htmleditor-body';
42940         
42941         if(this.bodyCls.length){
42942             cls += ' ' + this.bodyCls;
42943         }
42944         
42945         return '<html><head>' + st  +
42946             //<style type="text/css">' +
42947             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42948             //'</style>' +
42949             ' </head><body class="' +  cls + '"></body></html>';
42950     },
42951
42952     // private
42953     onRender : function(ct, position)
42954     {
42955         var _t = this;
42956         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42957         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42958         
42959         
42960         this.el.dom.style.border = '0 none';
42961         this.el.dom.setAttribute('tabIndex', -1);
42962         this.el.addClass('x-hidden hide');
42963         
42964         
42965         
42966         if(Roo.isIE){ // fix IE 1px bogus margin
42967             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42968         }
42969        
42970         
42971         this.frameId = Roo.id();
42972         
42973          
42974         
42975         var iframe = this.owner.wrap.createChild({
42976             tag: 'iframe',
42977             cls: 'form-control', // bootstrap..
42978             id: this.frameId,
42979             name: this.frameId,
42980             frameBorder : 'no',
42981             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42982         }, this.el
42983         );
42984         
42985         
42986         this.iframe = iframe.dom;
42987
42988          this.assignDocWin();
42989         
42990         this.doc.designMode = 'on';
42991        
42992         this.doc.open();
42993         this.doc.write(this.getDocMarkup());
42994         this.doc.close();
42995
42996         
42997         var task = { // must defer to wait for browser to be ready
42998             run : function(){
42999                 //console.log("run task?" + this.doc.readyState);
43000                 this.assignDocWin();
43001                 if(this.doc.body || this.doc.readyState == 'complete'){
43002                     try {
43003                         this.doc.designMode="on";
43004                     } catch (e) {
43005                         return;
43006                     }
43007                     Roo.TaskMgr.stop(task);
43008                     this.initEditor.defer(10, this);
43009                 }
43010             },
43011             interval : 10,
43012             duration: 10000,
43013             scope: this
43014         };
43015         Roo.TaskMgr.start(task);
43016
43017     },
43018
43019     // private
43020     onResize : function(w, h)
43021     {
43022          Roo.log('resize: ' +w + ',' + h );
43023         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43024         if(!this.iframe){
43025             return;
43026         }
43027         if(typeof w == 'number'){
43028             
43029             this.iframe.style.width = w + 'px';
43030         }
43031         if(typeof h == 'number'){
43032             
43033             this.iframe.style.height = h + 'px';
43034             if(this.doc){
43035                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43036             }
43037         }
43038         
43039     },
43040
43041     /**
43042      * Toggles the editor between standard and source edit mode.
43043      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43044      */
43045     toggleSourceEdit : function(sourceEditMode){
43046         
43047         this.sourceEditMode = sourceEditMode === true;
43048         
43049         if(this.sourceEditMode){
43050  
43051             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43052             
43053         }else{
43054             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43055             //this.iframe.className = '';
43056             this.deferFocus();
43057         }
43058         //this.setSize(this.owner.wrap.getSize());
43059         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43060     },
43061
43062     
43063   
43064
43065     /**
43066      * Protected method that will not generally be called directly. If you need/want
43067      * custom HTML cleanup, this is the method you should override.
43068      * @param {String} html The HTML to be cleaned
43069      * return {String} The cleaned HTML
43070      */
43071     cleanHtml : function(html){
43072         html = String(html);
43073         if(html.length > 5){
43074             if(Roo.isSafari){ // strip safari nonsense
43075                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43076             }
43077         }
43078         if(html == '&nbsp;'){
43079             html = '';
43080         }
43081         return html;
43082     },
43083
43084     /**
43085      * HTML Editor -> Textarea
43086      * Protected method that will not generally be called directly. Syncs the contents
43087      * of the editor iframe with the textarea.
43088      */
43089     syncValue : function(){
43090         if(this.initialized){
43091             var bd = (this.doc.body || this.doc.documentElement);
43092             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43093             var html = bd.innerHTML;
43094             if(Roo.isSafari){
43095                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43096                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43097                 if(m && m[1]){
43098                     html = '<div style="'+m[0]+'">' + html + '</div>';
43099                 }
43100             }
43101             html = this.cleanHtml(html);
43102             // fix up the special chars.. normaly like back quotes in word...
43103             // however we do not want to do this with chinese..
43104             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43105                 var cc = b.charCodeAt();
43106                 if (
43107                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43108                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43109                     (cc >= 0xf900 && cc < 0xfb00 )
43110                 ) {
43111                         return b;
43112                 }
43113                 return "&#"+cc+";" 
43114             });
43115             if(this.owner.fireEvent('beforesync', this, html) !== false){
43116                 this.el.dom.value = html;
43117                 this.owner.fireEvent('sync', this, html);
43118             }
43119         }
43120     },
43121
43122     /**
43123      * Protected method that will not generally be called directly. Pushes the value of the textarea
43124      * into the iframe editor.
43125      */
43126     pushValue : function(){
43127         if(this.initialized){
43128             var v = this.el.dom.value.trim();
43129             
43130 //            if(v.length < 1){
43131 //                v = '&#160;';
43132 //            }
43133             
43134             if(this.owner.fireEvent('beforepush', this, v) !== false){
43135                 var d = (this.doc.body || this.doc.documentElement);
43136                 d.innerHTML = v;
43137                 this.cleanUpPaste();
43138                 this.el.dom.value = d.innerHTML;
43139                 this.owner.fireEvent('push', this, v);
43140             }
43141         }
43142     },
43143
43144     // private
43145     deferFocus : function(){
43146         this.focus.defer(10, this);
43147     },
43148
43149     // doc'ed in Field
43150     focus : function(){
43151         if(this.win && !this.sourceEditMode){
43152             this.win.focus();
43153         }else{
43154             this.el.focus();
43155         }
43156     },
43157     
43158     assignDocWin: function()
43159     {
43160         var iframe = this.iframe;
43161         
43162          if(Roo.isIE){
43163             this.doc = iframe.contentWindow.document;
43164             this.win = iframe.contentWindow;
43165         } else {
43166 //            if (!Roo.get(this.frameId)) {
43167 //                return;
43168 //            }
43169 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43170 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43171             
43172             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43173                 return;
43174             }
43175             
43176             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43177             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43178         }
43179     },
43180     
43181     // private
43182     initEditor : function(){
43183         //console.log("INIT EDITOR");
43184         this.assignDocWin();
43185         
43186         
43187         
43188         this.doc.designMode="on";
43189         this.doc.open();
43190         this.doc.write(this.getDocMarkup());
43191         this.doc.close();
43192         
43193         var dbody = (this.doc.body || this.doc.documentElement);
43194         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43195         // this copies styles from the containing element into thsi one..
43196         // not sure why we need all of this..
43197         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43198         
43199         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43200         //ss['background-attachment'] = 'fixed'; // w3c
43201         dbody.bgProperties = 'fixed'; // ie
43202         //Roo.DomHelper.applyStyles(dbody, ss);
43203         Roo.EventManager.on(this.doc, {
43204             //'mousedown': this.onEditorEvent,
43205             'mouseup': this.onEditorEvent,
43206             'dblclick': this.onEditorEvent,
43207             'click': this.onEditorEvent,
43208             'keyup': this.onEditorEvent,
43209             buffer:100,
43210             scope: this
43211         });
43212         if(Roo.isGecko){
43213             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43214         }
43215         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43216             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43217         }
43218         this.initialized = true;
43219
43220         this.owner.fireEvent('initialize', this);
43221         this.pushValue();
43222     },
43223
43224     // private
43225     onDestroy : function(){
43226         
43227         
43228         
43229         if(this.rendered){
43230             
43231             //for (var i =0; i < this.toolbars.length;i++) {
43232             //    // fixme - ask toolbars for heights?
43233             //    this.toolbars[i].onDestroy();
43234            // }
43235             
43236             //this.wrap.dom.innerHTML = '';
43237             //this.wrap.remove();
43238         }
43239     },
43240
43241     // private
43242     onFirstFocus : function(){
43243         
43244         this.assignDocWin();
43245         
43246         
43247         this.activated = true;
43248          
43249     
43250         if(Roo.isGecko){ // prevent silly gecko errors
43251             this.win.focus();
43252             var s = this.win.getSelection();
43253             if(!s.focusNode || s.focusNode.nodeType != 3){
43254                 var r = s.getRangeAt(0);
43255                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43256                 r.collapse(true);
43257                 this.deferFocus();
43258             }
43259             try{
43260                 this.execCmd('useCSS', true);
43261                 this.execCmd('styleWithCSS', false);
43262             }catch(e){}
43263         }
43264         this.owner.fireEvent('activate', this);
43265     },
43266
43267     // private
43268     adjustFont: function(btn){
43269         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43270         //if(Roo.isSafari){ // safari
43271         //    adjust *= 2;
43272        // }
43273         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43274         if(Roo.isSafari){ // safari
43275             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43276             v =  (v < 10) ? 10 : v;
43277             v =  (v > 48) ? 48 : v;
43278             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43279             
43280         }
43281         
43282         
43283         v = Math.max(1, v+adjust);
43284         
43285         this.execCmd('FontSize', v  );
43286     },
43287
43288     onEditorEvent : function(e)
43289     {
43290         this.owner.fireEvent('editorevent', this, e);
43291       //  this.updateToolbar();
43292         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43293     },
43294
43295     insertTag : function(tg)
43296     {
43297         // could be a bit smarter... -> wrap the current selected tRoo..
43298         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43299             
43300             range = this.createRange(this.getSelection());
43301             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43302             wrappingNode.appendChild(range.extractContents());
43303             range.insertNode(wrappingNode);
43304
43305             return;
43306             
43307             
43308             
43309         }
43310         this.execCmd("formatblock",   tg);
43311         
43312     },
43313     
43314     insertText : function(txt)
43315     {
43316         
43317         
43318         var range = this.createRange();
43319         range.deleteContents();
43320                //alert(Sender.getAttribute('label'));
43321                
43322         range.insertNode(this.doc.createTextNode(txt));
43323     } ,
43324     
43325      
43326
43327     /**
43328      * Executes a Midas editor command on the editor document and performs necessary focus and
43329      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43330      * @param {String} cmd The Midas command
43331      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43332      */
43333     relayCmd : function(cmd, value){
43334         this.win.focus();
43335         this.execCmd(cmd, value);
43336         this.owner.fireEvent('editorevent', this);
43337         //this.updateToolbar();
43338         this.owner.deferFocus();
43339     },
43340
43341     /**
43342      * Executes a Midas editor command directly on the editor document.
43343      * For visual commands, you should use {@link #relayCmd} instead.
43344      * <b>This should only be called after the editor is initialized.</b>
43345      * @param {String} cmd The Midas command
43346      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43347      */
43348     execCmd : function(cmd, value){
43349         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43350         this.syncValue();
43351     },
43352  
43353  
43354    
43355     /**
43356      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43357      * to insert tRoo.
43358      * @param {String} text | dom node.. 
43359      */
43360     insertAtCursor : function(text)
43361     {
43362         
43363         if(!this.activated){
43364             return;
43365         }
43366         /*
43367         if(Roo.isIE){
43368             this.win.focus();
43369             var r = this.doc.selection.createRange();
43370             if(r){
43371                 r.collapse(true);
43372                 r.pasteHTML(text);
43373                 this.syncValue();
43374                 this.deferFocus();
43375             
43376             }
43377             return;
43378         }
43379         */
43380         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43381             this.win.focus();
43382             
43383             
43384             // from jquery ui (MIT licenced)
43385             var range, node;
43386             var win = this.win;
43387             
43388             if (win.getSelection && win.getSelection().getRangeAt) {
43389                 range = win.getSelection().getRangeAt(0);
43390                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43391                 range.insertNode(node);
43392             } else if (win.document.selection && win.document.selection.createRange) {
43393                 // no firefox support
43394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43395                 win.document.selection.createRange().pasteHTML(txt);
43396             } else {
43397                 // no firefox support
43398                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43399                 this.execCmd('InsertHTML', txt);
43400             } 
43401             
43402             this.syncValue();
43403             
43404             this.deferFocus();
43405         }
43406     },
43407  // private
43408     mozKeyPress : function(e){
43409         if(e.ctrlKey){
43410             var c = e.getCharCode(), cmd;
43411           
43412             if(c > 0){
43413                 c = String.fromCharCode(c).toLowerCase();
43414                 switch(c){
43415                     case 'b':
43416                         cmd = 'bold';
43417                         break;
43418                     case 'i':
43419                         cmd = 'italic';
43420                         break;
43421                     
43422                     case 'u':
43423                         cmd = 'underline';
43424                         break;
43425                     
43426                     case 'v':
43427                         this.cleanUpPaste.defer(100, this);
43428                         return;
43429                         
43430                 }
43431                 if(cmd){
43432                     this.win.focus();
43433                     this.execCmd(cmd);
43434                     this.deferFocus();
43435                     e.preventDefault();
43436                 }
43437                 
43438             }
43439         }
43440     },
43441
43442     // private
43443     fixKeys : function(){ // load time branching for fastest keydown performance
43444         if(Roo.isIE){
43445             return function(e){
43446                 var k = e.getKey(), r;
43447                 if(k == e.TAB){
43448                     e.stopEvent();
43449                     r = this.doc.selection.createRange();
43450                     if(r){
43451                         r.collapse(true);
43452                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43453                         this.deferFocus();
43454                     }
43455                     return;
43456                 }
43457                 
43458                 if(k == e.ENTER){
43459                     r = this.doc.selection.createRange();
43460                     if(r){
43461                         var target = r.parentElement();
43462                         if(!target || target.tagName.toLowerCase() != 'li'){
43463                             e.stopEvent();
43464                             r.pasteHTML('<br />');
43465                             r.collapse(false);
43466                             r.select();
43467                         }
43468                     }
43469                 }
43470                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43471                     this.cleanUpPaste.defer(100, this);
43472                     return;
43473                 }
43474                 
43475                 
43476             };
43477         }else if(Roo.isOpera){
43478             return function(e){
43479                 var k = e.getKey();
43480                 if(k == e.TAB){
43481                     e.stopEvent();
43482                     this.win.focus();
43483                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43484                     this.deferFocus();
43485                 }
43486                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43487                     this.cleanUpPaste.defer(100, this);
43488                     return;
43489                 }
43490                 
43491             };
43492         }else if(Roo.isSafari){
43493             return function(e){
43494                 var k = e.getKey();
43495                 
43496                 if(k == e.TAB){
43497                     e.stopEvent();
43498                     this.execCmd('InsertText','\t');
43499                     this.deferFocus();
43500                     return;
43501                 }
43502                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43503                     this.cleanUpPaste.defer(100, this);
43504                     return;
43505                 }
43506                 
43507              };
43508         }
43509     }(),
43510     
43511     getAllAncestors: function()
43512     {
43513         var p = this.getSelectedNode();
43514         var a = [];
43515         if (!p) {
43516             a.push(p); // push blank onto stack..
43517             p = this.getParentElement();
43518         }
43519         
43520         
43521         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43522             a.push(p);
43523             p = p.parentNode;
43524         }
43525         a.push(this.doc.body);
43526         return a;
43527     },
43528     lastSel : false,
43529     lastSelNode : false,
43530     
43531     
43532     getSelection : function() 
43533     {
43534         this.assignDocWin();
43535         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43536     },
43537     
43538     getSelectedNode: function() 
43539     {
43540         // this may only work on Gecko!!!
43541         
43542         // should we cache this!!!!
43543         
43544         
43545         
43546          
43547         var range = this.createRange(this.getSelection()).cloneRange();
43548         
43549         if (Roo.isIE) {
43550             var parent = range.parentElement();
43551             while (true) {
43552                 var testRange = range.duplicate();
43553                 testRange.moveToElementText(parent);
43554                 if (testRange.inRange(range)) {
43555                     break;
43556                 }
43557                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43558                     break;
43559                 }
43560                 parent = parent.parentElement;
43561             }
43562             return parent;
43563         }
43564         
43565         // is ancestor a text element.
43566         var ac =  range.commonAncestorContainer;
43567         if (ac.nodeType == 3) {
43568             ac = ac.parentNode;
43569         }
43570         
43571         var ar = ac.childNodes;
43572          
43573         var nodes = [];
43574         var other_nodes = [];
43575         var has_other_nodes = false;
43576         for (var i=0;i<ar.length;i++) {
43577             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43578                 continue;
43579             }
43580             // fullly contained node.
43581             
43582             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43583                 nodes.push(ar[i]);
43584                 continue;
43585             }
43586             
43587             // probably selected..
43588             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43589                 other_nodes.push(ar[i]);
43590                 continue;
43591             }
43592             // outer..
43593             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43594                 continue;
43595             }
43596             
43597             
43598             has_other_nodes = true;
43599         }
43600         if (!nodes.length && other_nodes.length) {
43601             nodes= other_nodes;
43602         }
43603         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43604             return false;
43605         }
43606         
43607         return nodes[0];
43608     },
43609     createRange: function(sel)
43610     {
43611         // this has strange effects when using with 
43612         // top toolbar - not sure if it's a great idea.
43613         //this.editor.contentWindow.focus();
43614         if (typeof sel != "undefined") {
43615             try {
43616                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43617             } catch(e) {
43618                 return this.doc.createRange();
43619             }
43620         } else {
43621             return this.doc.createRange();
43622         }
43623     },
43624     getParentElement: function()
43625     {
43626         
43627         this.assignDocWin();
43628         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43629         
43630         var range = this.createRange(sel);
43631          
43632         try {
43633             var p = range.commonAncestorContainer;
43634             while (p.nodeType == 3) { // text node
43635                 p = p.parentNode;
43636             }
43637             return p;
43638         } catch (e) {
43639             return null;
43640         }
43641     
43642     },
43643     /***
43644      *
43645      * Range intersection.. the hard stuff...
43646      *  '-1' = before
43647      *  '0' = hits..
43648      *  '1' = after.
43649      *         [ -- selected range --- ]
43650      *   [fail]                        [fail]
43651      *
43652      *    basically..
43653      *      if end is before start or  hits it. fail.
43654      *      if start is after end or hits it fail.
43655      *
43656      *   if either hits (but other is outside. - then it's not 
43657      *   
43658      *    
43659      **/
43660     
43661     
43662     // @see http://www.thismuchiknow.co.uk/?p=64.
43663     rangeIntersectsNode : function(range, node)
43664     {
43665         var nodeRange = node.ownerDocument.createRange();
43666         try {
43667             nodeRange.selectNode(node);
43668         } catch (e) {
43669             nodeRange.selectNodeContents(node);
43670         }
43671     
43672         var rangeStartRange = range.cloneRange();
43673         rangeStartRange.collapse(true);
43674     
43675         var rangeEndRange = range.cloneRange();
43676         rangeEndRange.collapse(false);
43677     
43678         var nodeStartRange = nodeRange.cloneRange();
43679         nodeStartRange.collapse(true);
43680     
43681         var nodeEndRange = nodeRange.cloneRange();
43682         nodeEndRange.collapse(false);
43683     
43684         return rangeStartRange.compareBoundaryPoints(
43685                  Range.START_TO_START, nodeEndRange) == -1 &&
43686                rangeEndRange.compareBoundaryPoints(
43687                  Range.START_TO_START, nodeStartRange) == 1;
43688         
43689          
43690     },
43691     rangeCompareNode : function(range, node)
43692     {
43693         var nodeRange = node.ownerDocument.createRange();
43694         try {
43695             nodeRange.selectNode(node);
43696         } catch (e) {
43697             nodeRange.selectNodeContents(node);
43698         }
43699         
43700         
43701         range.collapse(true);
43702     
43703         nodeRange.collapse(true);
43704      
43705         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43706         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43707          
43708         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43709         
43710         var nodeIsBefore   =  ss == 1;
43711         var nodeIsAfter    = ee == -1;
43712         
43713         if (nodeIsBefore && nodeIsAfter) {
43714             return 0; // outer
43715         }
43716         if (!nodeIsBefore && nodeIsAfter) {
43717             return 1; //right trailed.
43718         }
43719         
43720         if (nodeIsBefore && !nodeIsAfter) {
43721             return 2;  // left trailed.
43722         }
43723         // fully contined.
43724         return 3;
43725     },
43726
43727     // private? - in a new class?
43728     cleanUpPaste :  function()
43729     {
43730         // cleans up the whole document..
43731         Roo.log('cleanuppaste');
43732         
43733         this.cleanUpChildren(this.doc.body);
43734         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43735         if (clean != this.doc.body.innerHTML) {
43736             this.doc.body.innerHTML = clean;
43737         }
43738         
43739     },
43740     
43741     cleanWordChars : function(input) {// change the chars to hex code
43742         var he = Roo.HtmlEditorCore;
43743         
43744         var output = input;
43745         Roo.each(he.swapCodes, function(sw) { 
43746             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43747             
43748             output = output.replace(swapper, sw[1]);
43749         });
43750         
43751         return output;
43752     },
43753     
43754     
43755     cleanUpChildren : function (n)
43756     {
43757         if (!n.childNodes.length) {
43758             return;
43759         }
43760         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43761            this.cleanUpChild(n.childNodes[i]);
43762         }
43763     },
43764     
43765     
43766         
43767     
43768     cleanUpChild : function (node)
43769     {
43770         var ed = this;
43771         //console.log(node);
43772         if (node.nodeName == "#text") {
43773             // clean up silly Windows -- stuff?
43774             return; 
43775         }
43776         if (node.nodeName == "#comment") {
43777             node.parentNode.removeChild(node);
43778             // clean up silly Windows -- stuff?
43779             return; 
43780         }
43781         var lcname = node.tagName.toLowerCase();
43782         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43783         // whitelist of tags..
43784         
43785         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43786             // remove node.
43787             node.parentNode.removeChild(node);
43788             return;
43789             
43790         }
43791         
43792         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43793         
43794         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43795         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43796         
43797         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43798         //    remove_keep_children = true;
43799         //}
43800         
43801         if (remove_keep_children) {
43802             this.cleanUpChildren(node);
43803             // inserts everything just before this node...
43804             while (node.childNodes.length) {
43805                 var cn = node.childNodes[0];
43806                 node.removeChild(cn);
43807                 node.parentNode.insertBefore(cn, node);
43808             }
43809             node.parentNode.removeChild(node);
43810             return;
43811         }
43812         
43813         if (!node.attributes || !node.attributes.length) {
43814             this.cleanUpChildren(node);
43815             return;
43816         }
43817         
43818         function cleanAttr(n,v)
43819         {
43820             
43821             if (v.match(/^\./) || v.match(/^\//)) {
43822                 return;
43823             }
43824             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
43825                 return;
43826             }
43827             if (v.match(/^#/)) {
43828                 return;
43829             }
43830 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43831             node.removeAttribute(n);
43832             
43833         }
43834         
43835         var cwhite = this.cwhite;
43836         var cblack = this.cblack;
43837             
43838         function cleanStyle(n,v)
43839         {
43840             if (v.match(/expression/)) { //XSS?? should we even bother..
43841                 node.removeAttribute(n);
43842                 return;
43843             }
43844             
43845             var parts = v.split(/;/);
43846             var clean = [];
43847             
43848             Roo.each(parts, function(p) {
43849                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43850                 if (!p.length) {
43851                     return true;
43852                 }
43853                 var l = p.split(':').shift().replace(/\s+/g,'');
43854                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43855                 
43856                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43857 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43858                     //node.removeAttribute(n);
43859                     return true;
43860                 }
43861                 //Roo.log()
43862                 // only allow 'c whitelisted system attributes'
43863                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43864 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43865                     //node.removeAttribute(n);
43866                     return true;
43867                 }
43868                 
43869                 
43870                  
43871                 
43872                 clean.push(p);
43873                 return true;
43874             });
43875             if (clean.length) { 
43876                 node.setAttribute(n, clean.join(';'));
43877             } else {
43878                 node.removeAttribute(n);
43879             }
43880             
43881         }
43882         
43883         
43884         for (var i = node.attributes.length-1; i > -1 ; i--) {
43885             var a = node.attributes[i];
43886             //console.log(a);
43887             
43888             if (a.name.toLowerCase().substr(0,2)=='on')  {
43889                 node.removeAttribute(a.name);
43890                 continue;
43891             }
43892             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43893                 node.removeAttribute(a.name);
43894                 continue;
43895             }
43896             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43897                 cleanAttr(a.name,a.value); // fixme..
43898                 continue;
43899             }
43900             if (a.name == 'style') {
43901                 cleanStyle(a.name,a.value);
43902                 continue;
43903             }
43904             /// clean up MS crap..
43905             // tecnically this should be a list of valid class'es..
43906             
43907             
43908             if (a.name == 'class') {
43909                 if (a.value.match(/^Mso/)) {
43910                     node.className = '';
43911                 }
43912                 
43913                 if (a.value.match(/^body$/)) {
43914                     node.className = '';
43915                 }
43916                 continue;
43917             }
43918             
43919             // style cleanup!?
43920             // class cleanup?
43921             
43922         }
43923         
43924         
43925         this.cleanUpChildren(node);
43926         
43927         
43928     },
43929     
43930     /**
43931      * Clean up MS wordisms...
43932      */
43933     cleanWord : function(node)
43934     {
43935         
43936         
43937         if (!node) {
43938             this.cleanWord(this.doc.body);
43939             return;
43940         }
43941         if (node.nodeName == "#text") {
43942             // clean up silly Windows -- stuff?
43943             return; 
43944         }
43945         if (node.nodeName == "#comment") {
43946             node.parentNode.removeChild(node);
43947             // clean up silly Windows -- stuff?
43948             return; 
43949         }
43950         
43951         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43952             node.parentNode.removeChild(node);
43953             return;
43954         }
43955         
43956         // remove - but keep children..
43957         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43958             while (node.childNodes.length) {
43959                 var cn = node.childNodes[0];
43960                 node.removeChild(cn);
43961                 node.parentNode.insertBefore(cn, node);
43962             }
43963             node.parentNode.removeChild(node);
43964             this.iterateChildren(node, this.cleanWord);
43965             return;
43966         }
43967         // clean styles
43968         if (node.className.length) {
43969             
43970             var cn = node.className.split(/\W+/);
43971             var cna = [];
43972             Roo.each(cn, function(cls) {
43973                 if (cls.match(/Mso[a-zA-Z]+/)) {
43974                     return;
43975                 }
43976                 cna.push(cls);
43977             });
43978             node.className = cna.length ? cna.join(' ') : '';
43979             if (!cna.length) {
43980                 node.removeAttribute("class");
43981             }
43982         }
43983         
43984         if (node.hasAttribute("lang")) {
43985             node.removeAttribute("lang");
43986         }
43987         
43988         if (node.hasAttribute("style")) {
43989             
43990             var styles = node.getAttribute("style").split(";");
43991             var nstyle = [];
43992             Roo.each(styles, function(s) {
43993                 if (!s.match(/:/)) {
43994                     return;
43995                 }
43996                 var kv = s.split(":");
43997                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43998                     return;
43999                 }
44000                 // what ever is left... we allow.
44001                 nstyle.push(s);
44002             });
44003             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44004             if (!nstyle.length) {
44005                 node.removeAttribute('style');
44006             }
44007         }
44008         this.iterateChildren(node, this.cleanWord);
44009         
44010         
44011         
44012     },
44013     /**
44014      * iterateChildren of a Node, calling fn each time, using this as the scole..
44015      * @param {DomNode} node node to iterate children of.
44016      * @param {Function} fn method of this class to call on each item.
44017      */
44018     iterateChildren : function(node, fn)
44019     {
44020         if (!node.childNodes.length) {
44021                 return;
44022         }
44023         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44024            fn.call(this, node.childNodes[i])
44025         }
44026     },
44027     
44028     
44029     /**
44030      * cleanTableWidths.
44031      *
44032      * Quite often pasting from word etc.. results in tables with column and widths.
44033      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44034      *
44035      */
44036     cleanTableWidths : function(node)
44037     {
44038          
44039          
44040         if (!node) {
44041             this.cleanTableWidths(this.doc.body);
44042             return;
44043         }
44044         
44045         // ignore list...
44046         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44047             return; 
44048         }
44049         Roo.log(node.tagName);
44050         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44051             this.iterateChildren(node, this.cleanTableWidths);
44052             return;
44053         }
44054         if (node.hasAttribute('width')) {
44055             node.removeAttribute('width');
44056         }
44057         
44058          
44059         if (node.hasAttribute("style")) {
44060             // pretty basic...
44061             
44062             var styles = node.getAttribute("style").split(";");
44063             var nstyle = [];
44064             Roo.each(styles, function(s) {
44065                 if (!s.match(/:/)) {
44066                     return;
44067                 }
44068                 var kv = s.split(":");
44069                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44070                     return;
44071                 }
44072                 // what ever is left... we allow.
44073                 nstyle.push(s);
44074             });
44075             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44076             if (!nstyle.length) {
44077                 node.removeAttribute('style');
44078             }
44079         }
44080         
44081         this.iterateChildren(node, this.cleanTableWidths);
44082         
44083         
44084     },
44085     
44086     
44087     
44088     
44089     domToHTML : function(currentElement, depth, nopadtext) {
44090         
44091         depth = depth || 0;
44092         nopadtext = nopadtext || false;
44093     
44094         if (!currentElement) {
44095             return this.domToHTML(this.doc.body);
44096         }
44097         
44098         //Roo.log(currentElement);
44099         var j;
44100         var allText = false;
44101         var nodeName = currentElement.nodeName;
44102         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44103         
44104         if  (nodeName == '#text') {
44105             
44106             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44107         }
44108         
44109         
44110         var ret = '';
44111         if (nodeName != 'BODY') {
44112              
44113             var i = 0;
44114             // Prints the node tagName, such as <A>, <IMG>, etc
44115             if (tagName) {
44116                 var attr = [];
44117                 for(i = 0; i < currentElement.attributes.length;i++) {
44118                     // quoting?
44119                     var aname = currentElement.attributes.item(i).name;
44120                     if (!currentElement.attributes.item(i).value.length) {
44121                         continue;
44122                     }
44123                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44124                 }
44125                 
44126                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44127             } 
44128             else {
44129                 
44130                 // eack
44131             }
44132         } else {
44133             tagName = false;
44134         }
44135         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44136             return ret;
44137         }
44138         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44139             nopadtext = true;
44140         }
44141         
44142         
44143         // Traverse the tree
44144         i = 0;
44145         var currentElementChild = currentElement.childNodes.item(i);
44146         var allText = true;
44147         var innerHTML  = '';
44148         lastnode = '';
44149         while (currentElementChild) {
44150             // Formatting code (indent the tree so it looks nice on the screen)
44151             var nopad = nopadtext;
44152             if (lastnode == 'SPAN') {
44153                 nopad  = true;
44154             }
44155             // text
44156             if  (currentElementChild.nodeName == '#text') {
44157                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44158                 toadd = nopadtext ? toadd : toadd.trim();
44159                 if (!nopad && toadd.length > 80) {
44160                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44161                 }
44162                 innerHTML  += toadd;
44163                 
44164                 i++;
44165                 currentElementChild = currentElement.childNodes.item(i);
44166                 lastNode = '';
44167                 continue;
44168             }
44169             allText = false;
44170             
44171             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44172                 
44173             // Recursively traverse the tree structure of the child node
44174             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44175             lastnode = currentElementChild.nodeName;
44176             i++;
44177             currentElementChild=currentElement.childNodes.item(i);
44178         }
44179         
44180         ret += innerHTML;
44181         
44182         if (!allText) {
44183                 // The remaining code is mostly for formatting the tree
44184             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44185         }
44186         
44187         
44188         if (tagName) {
44189             ret+= "</"+tagName+">";
44190         }
44191         return ret;
44192         
44193     },
44194         
44195     applyBlacklists : function()
44196     {
44197         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44198         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44199         
44200         this.white = [];
44201         this.black = [];
44202         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44203             if (b.indexOf(tag) > -1) {
44204                 return;
44205             }
44206             this.white.push(tag);
44207             
44208         }, this);
44209         
44210         Roo.each(w, function(tag) {
44211             if (b.indexOf(tag) > -1) {
44212                 return;
44213             }
44214             if (this.white.indexOf(tag) > -1) {
44215                 return;
44216             }
44217             this.white.push(tag);
44218             
44219         }, this);
44220         
44221         
44222         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44223             if (w.indexOf(tag) > -1) {
44224                 return;
44225             }
44226             this.black.push(tag);
44227             
44228         }, this);
44229         
44230         Roo.each(b, function(tag) {
44231             if (w.indexOf(tag) > -1) {
44232                 return;
44233             }
44234             if (this.black.indexOf(tag) > -1) {
44235                 return;
44236             }
44237             this.black.push(tag);
44238             
44239         }, this);
44240         
44241         
44242         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44243         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44244         
44245         this.cwhite = [];
44246         this.cblack = [];
44247         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44248             if (b.indexOf(tag) > -1) {
44249                 return;
44250             }
44251             this.cwhite.push(tag);
44252             
44253         }, this);
44254         
44255         Roo.each(w, function(tag) {
44256             if (b.indexOf(tag) > -1) {
44257                 return;
44258             }
44259             if (this.cwhite.indexOf(tag) > -1) {
44260                 return;
44261             }
44262             this.cwhite.push(tag);
44263             
44264         }, this);
44265         
44266         
44267         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44268             if (w.indexOf(tag) > -1) {
44269                 return;
44270             }
44271             this.cblack.push(tag);
44272             
44273         }, this);
44274         
44275         Roo.each(b, function(tag) {
44276             if (w.indexOf(tag) > -1) {
44277                 return;
44278             }
44279             if (this.cblack.indexOf(tag) > -1) {
44280                 return;
44281             }
44282             this.cblack.push(tag);
44283             
44284         }, this);
44285     },
44286     
44287     setStylesheets : function(stylesheets)
44288     {
44289         if(typeof(stylesheets) == 'string'){
44290             Roo.get(this.iframe.contentDocument.head).createChild({
44291                 tag : 'link',
44292                 rel : 'stylesheet',
44293                 type : 'text/css',
44294                 href : stylesheets
44295             });
44296             
44297             return;
44298         }
44299         var _this = this;
44300      
44301         Roo.each(stylesheets, function(s) {
44302             if(!s.length){
44303                 return;
44304             }
44305             
44306             Roo.get(_this.iframe.contentDocument.head).createChild({
44307                 tag : 'link',
44308                 rel : 'stylesheet',
44309                 type : 'text/css',
44310                 href : s
44311             });
44312         });
44313
44314         
44315     },
44316     
44317     removeStylesheets : function()
44318     {
44319         var _this = this;
44320         
44321         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44322             s.remove();
44323         });
44324     },
44325     
44326     setStyle : function(style)
44327     {
44328         Roo.get(this.iframe.contentDocument.head).createChild({
44329             tag : 'style',
44330             type : 'text/css',
44331             html : style
44332         });
44333
44334         return;
44335     }
44336     
44337     // hide stuff that is not compatible
44338     /**
44339      * @event blur
44340      * @hide
44341      */
44342     /**
44343      * @event change
44344      * @hide
44345      */
44346     /**
44347      * @event focus
44348      * @hide
44349      */
44350     /**
44351      * @event specialkey
44352      * @hide
44353      */
44354     /**
44355      * @cfg {String} fieldClass @hide
44356      */
44357     /**
44358      * @cfg {String} focusClass @hide
44359      */
44360     /**
44361      * @cfg {String} autoCreate @hide
44362      */
44363     /**
44364      * @cfg {String} inputType @hide
44365      */
44366     /**
44367      * @cfg {String} invalidClass @hide
44368      */
44369     /**
44370      * @cfg {String} invalidText @hide
44371      */
44372     /**
44373      * @cfg {String} msgFx @hide
44374      */
44375     /**
44376      * @cfg {String} validateOnBlur @hide
44377      */
44378 });
44379
44380 Roo.HtmlEditorCore.white = [
44381         'area', 'br', 'img', 'input', 'hr', 'wbr',
44382         
44383        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44384        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44385        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44386        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44387        'table',   'ul',         'xmp', 
44388        
44389        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44390       'thead',   'tr', 
44391      
44392       'dir', 'menu', 'ol', 'ul', 'dl',
44393        
44394       'embed',  'object'
44395 ];
44396
44397
44398 Roo.HtmlEditorCore.black = [
44399     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44400         'applet', // 
44401         'base',   'basefont', 'bgsound', 'blink',  'body', 
44402         'frame',  'frameset', 'head',    'html',   'ilayer', 
44403         'iframe', 'layer',  'link',     'meta',    'object',   
44404         'script', 'style' ,'title',  'xml' // clean later..
44405 ];
44406 Roo.HtmlEditorCore.clean = [
44407     'script', 'style', 'title', 'xml'
44408 ];
44409 Roo.HtmlEditorCore.remove = [
44410     'font'
44411 ];
44412 // attributes..
44413
44414 Roo.HtmlEditorCore.ablack = [
44415     'on'
44416 ];
44417     
44418 Roo.HtmlEditorCore.aclean = [ 
44419     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44420 ];
44421
44422 // protocols..
44423 Roo.HtmlEditorCore.pwhite= [
44424         'http',  'https',  'mailto'
44425 ];
44426
44427 // white listed style attributes.
44428 Roo.HtmlEditorCore.cwhite= [
44429       //  'text-align', /// default is to allow most things..
44430       
44431          
44432 //        'font-size'//??
44433 ];
44434
44435 // black listed style attributes.
44436 Roo.HtmlEditorCore.cblack= [
44437       //  'font-size' -- this can be set by the project 
44438 ];
44439
44440
44441 Roo.HtmlEditorCore.swapCodes   =[ 
44442     [    8211, "--" ], 
44443     [    8212, "--" ], 
44444     [    8216,  "'" ],  
44445     [    8217, "'" ],  
44446     [    8220, '"' ],  
44447     [    8221, '"' ],  
44448     [    8226, "*" ],  
44449     [    8230, "..." ]
44450 ]; 
44451
44452     //<script type="text/javascript">
44453
44454 /*
44455  * Ext JS Library 1.1.1
44456  * Copyright(c) 2006-2007, Ext JS, LLC.
44457  * Licence LGPL
44458  * 
44459  */
44460  
44461  
44462 Roo.form.HtmlEditor = function(config){
44463     
44464     
44465     
44466     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44467     
44468     if (!this.toolbars) {
44469         this.toolbars = [];
44470     }
44471     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44472     
44473     
44474 };
44475
44476 /**
44477  * @class Roo.form.HtmlEditor
44478  * @extends Roo.form.Field
44479  * Provides a lightweight HTML Editor component.
44480  *
44481  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44482  * 
44483  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44484  * supported by this editor.</b><br/><br/>
44485  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44486  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44487  */
44488 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44489     /**
44490      * @cfg {Boolean} clearUp
44491      */
44492     clearUp : true,
44493       /**
44494      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44495      */
44496     toolbars : false,
44497    
44498      /**
44499      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44500      *                        Roo.resizable.
44501      */
44502     resizable : false,
44503      /**
44504      * @cfg {Number} height (in pixels)
44505      */   
44506     height: 300,
44507    /**
44508      * @cfg {Number} width (in pixels)
44509      */   
44510     width: 500,
44511     
44512     /**
44513      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44514      * 
44515      */
44516     stylesheets: false,
44517     
44518     
44519      /**
44520      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44521      * 
44522      */
44523     cblack: false,
44524     /**
44525      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44526      * 
44527      */
44528     cwhite: false,
44529     
44530      /**
44531      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44532      * 
44533      */
44534     black: false,
44535     /**
44536      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44537      * 
44538      */
44539     white: false,
44540     
44541     // id of frame..
44542     frameId: false,
44543     
44544     // private properties
44545     validationEvent : false,
44546     deferHeight: true,
44547     initialized : false,
44548     activated : false,
44549     
44550     onFocus : Roo.emptyFn,
44551     iframePad:3,
44552     hideMode:'offsets',
44553     
44554     actionMode : 'container', // defaults to hiding it...
44555     
44556     defaultAutoCreate : { // modified by initCompnoent..
44557         tag: "textarea",
44558         style:"width:500px;height:300px;",
44559         autocomplete: "new-password"
44560     },
44561
44562     // private
44563     initComponent : function(){
44564         this.addEvents({
44565             /**
44566              * @event initialize
44567              * Fires when the editor is fully initialized (including the iframe)
44568              * @param {HtmlEditor} this
44569              */
44570             initialize: true,
44571             /**
44572              * @event activate
44573              * Fires when the editor is first receives the focus. Any insertion must wait
44574              * until after this event.
44575              * @param {HtmlEditor} this
44576              */
44577             activate: true,
44578              /**
44579              * @event beforesync
44580              * Fires before the textarea is updated with content from the editor iframe. Return false
44581              * to cancel the sync.
44582              * @param {HtmlEditor} this
44583              * @param {String} html
44584              */
44585             beforesync: true,
44586              /**
44587              * @event beforepush
44588              * Fires before the iframe editor is updated with content from the textarea. Return false
44589              * to cancel the push.
44590              * @param {HtmlEditor} this
44591              * @param {String} html
44592              */
44593             beforepush: true,
44594              /**
44595              * @event sync
44596              * Fires when the textarea is updated with content from the editor iframe.
44597              * @param {HtmlEditor} this
44598              * @param {String} html
44599              */
44600             sync: true,
44601              /**
44602              * @event push
44603              * Fires when the iframe editor is updated with content from the textarea.
44604              * @param {HtmlEditor} this
44605              * @param {String} html
44606              */
44607             push: true,
44608              /**
44609              * @event editmodechange
44610              * Fires when the editor switches edit modes
44611              * @param {HtmlEditor} this
44612              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44613              */
44614             editmodechange: true,
44615             /**
44616              * @event editorevent
44617              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44618              * @param {HtmlEditor} this
44619              */
44620             editorevent: true,
44621             /**
44622              * @event firstfocus
44623              * Fires when on first focus - needed by toolbars..
44624              * @param {HtmlEditor} this
44625              */
44626             firstfocus: true,
44627             /**
44628              * @event autosave
44629              * Auto save the htmlEditor value as a file into Events
44630              * @param {HtmlEditor} this
44631              */
44632             autosave: true,
44633             /**
44634              * @event savedpreview
44635              * preview the saved version of htmlEditor
44636              * @param {HtmlEditor} this
44637              */
44638             savedpreview: true,
44639             
44640             /**
44641             * @event stylesheetsclick
44642             * Fires when press the Sytlesheets button
44643             * @param {Roo.HtmlEditorCore} this
44644             */
44645             stylesheetsclick: true
44646         });
44647         this.defaultAutoCreate =  {
44648             tag: "textarea",
44649             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44650             autocomplete: "new-password"
44651         };
44652     },
44653
44654     /**
44655      * Protected method that will not generally be called directly. It
44656      * is called when the editor creates its toolbar. Override this method if you need to
44657      * add custom toolbar buttons.
44658      * @param {HtmlEditor} editor
44659      */
44660     createToolbar : function(editor){
44661         Roo.log("create toolbars");
44662         if (!editor.toolbars || !editor.toolbars.length) {
44663             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44664         }
44665         
44666         for (var i =0 ; i < editor.toolbars.length;i++) {
44667             editor.toolbars[i] = Roo.factory(
44668                     typeof(editor.toolbars[i]) == 'string' ?
44669                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44670                 Roo.form.HtmlEditor);
44671             editor.toolbars[i].init(editor);
44672         }
44673          
44674         
44675     },
44676
44677      
44678     // private
44679     onRender : function(ct, position)
44680     {
44681         var _t = this;
44682         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44683         
44684         this.wrap = this.el.wrap({
44685             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44686         });
44687         
44688         this.editorcore.onRender(ct, position);
44689          
44690         if (this.resizable) {
44691             this.resizeEl = new Roo.Resizable(this.wrap, {
44692                 pinned : true,
44693                 wrap: true,
44694                 dynamic : true,
44695                 minHeight : this.height,
44696                 height: this.height,
44697                 handles : this.resizable,
44698                 width: this.width,
44699                 listeners : {
44700                     resize : function(r, w, h) {
44701                         _t.onResize(w,h); // -something
44702                     }
44703                 }
44704             });
44705             
44706         }
44707         this.createToolbar(this);
44708        
44709         
44710         if(!this.width){
44711             this.setSize(this.wrap.getSize());
44712         }
44713         if (this.resizeEl) {
44714             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44715             // should trigger onReize..
44716         }
44717         
44718         this.keyNav = new Roo.KeyNav(this.el, {
44719             
44720             "tab" : function(e){
44721                 e.preventDefault();
44722                 
44723                 var value = this.getValue();
44724                 
44725                 var start = this.el.dom.selectionStart;
44726                 var end = this.el.dom.selectionEnd;
44727                 
44728                 if(!e.shiftKey){
44729                     
44730                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44731                     this.el.dom.setSelectionRange(end + 1, end + 1);
44732                     return;
44733                 }
44734                 
44735                 var f = value.substring(0, start).split("\t");
44736                 
44737                 if(f.pop().length != 0){
44738                     return;
44739                 }
44740                 
44741                 this.setValue(f.join("\t") + value.substring(end));
44742                 this.el.dom.setSelectionRange(start - 1, start - 1);
44743                 
44744             },
44745             
44746             "home" : function(e){
44747                 e.preventDefault();
44748                 
44749                 var curr = this.el.dom.selectionStart;
44750                 var lines = this.getValue().split("\n");
44751                 
44752                 if(!lines.length){
44753                     return;
44754                 }
44755                 
44756                 if(e.ctrlKey){
44757                     this.el.dom.setSelectionRange(0, 0);
44758                     return;
44759                 }
44760                 
44761                 var pos = 0;
44762                 
44763                 for (var i = 0; i < lines.length;i++) {
44764                     pos += lines[i].length;
44765                     
44766                     if(i != 0){
44767                         pos += 1;
44768                     }
44769                     
44770                     if(pos < curr){
44771                         continue;
44772                     }
44773                     
44774                     pos -= lines[i].length;
44775                     
44776                     break;
44777                 }
44778                 
44779                 if(!e.shiftKey){
44780                     this.el.dom.setSelectionRange(pos, pos);
44781                     return;
44782                 }
44783                 
44784                 this.el.dom.selectionStart = pos;
44785                 this.el.dom.selectionEnd = curr;
44786             },
44787             
44788             "end" : function(e){
44789                 e.preventDefault();
44790                 
44791                 var curr = this.el.dom.selectionStart;
44792                 var lines = this.getValue().split("\n");
44793                 
44794                 if(!lines.length){
44795                     return;
44796                 }
44797                 
44798                 if(e.ctrlKey){
44799                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44800                     return;
44801                 }
44802                 
44803                 var pos = 0;
44804                 
44805                 for (var i = 0; i < lines.length;i++) {
44806                     
44807                     pos += lines[i].length;
44808                     
44809                     if(i != 0){
44810                         pos += 1;
44811                     }
44812                     
44813                     if(pos < curr){
44814                         continue;
44815                     }
44816                     
44817                     break;
44818                 }
44819                 
44820                 if(!e.shiftKey){
44821                     this.el.dom.setSelectionRange(pos, pos);
44822                     return;
44823                 }
44824                 
44825                 this.el.dom.selectionStart = curr;
44826                 this.el.dom.selectionEnd = pos;
44827             },
44828
44829             scope : this,
44830
44831             doRelay : function(foo, bar, hname){
44832                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44833             },
44834
44835             forceKeyDown: true
44836         });
44837         
44838 //        if(this.autosave && this.w){
44839 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44840 //        }
44841     },
44842
44843     // private
44844     onResize : function(w, h)
44845     {
44846         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44847         var ew = false;
44848         var eh = false;
44849         
44850         if(this.el ){
44851             if(typeof w == 'number'){
44852                 var aw = w - this.wrap.getFrameWidth('lr');
44853                 this.el.setWidth(this.adjustWidth('textarea', aw));
44854                 ew = aw;
44855             }
44856             if(typeof h == 'number'){
44857                 var tbh = 0;
44858                 for (var i =0; i < this.toolbars.length;i++) {
44859                     // fixme - ask toolbars for heights?
44860                     tbh += this.toolbars[i].tb.el.getHeight();
44861                     if (this.toolbars[i].footer) {
44862                         tbh += this.toolbars[i].footer.el.getHeight();
44863                     }
44864                 }
44865                 
44866                 
44867                 
44868                 
44869                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44870                 ah -= 5; // knock a few pixes off for look..
44871 //                Roo.log(ah);
44872                 this.el.setHeight(this.adjustWidth('textarea', ah));
44873                 var eh = ah;
44874             }
44875         }
44876         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44877         this.editorcore.onResize(ew,eh);
44878         
44879     },
44880
44881     /**
44882      * Toggles the editor between standard and source edit mode.
44883      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44884      */
44885     toggleSourceEdit : function(sourceEditMode)
44886     {
44887         this.editorcore.toggleSourceEdit(sourceEditMode);
44888         
44889         if(this.editorcore.sourceEditMode){
44890             Roo.log('editor - showing textarea');
44891             
44892 //            Roo.log('in');
44893 //            Roo.log(this.syncValue());
44894             this.editorcore.syncValue();
44895             this.el.removeClass('x-hidden');
44896             this.el.dom.removeAttribute('tabIndex');
44897             this.el.focus();
44898             
44899             for (var i = 0; i < this.toolbars.length; i++) {
44900                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44901                     this.toolbars[i].tb.hide();
44902                     this.toolbars[i].footer.hide();
44903                 }
44904             }
44905             
44906         }else{
44907             Roo.log('editor - hiding textarea');
44908 //            Roo.log('out')
44909 //            Roo.log(this.pushValue()); 
44910             this.editorcore.pushValue();
44911             
44912             this.el.addClass('x-hidden');
44913             this.el.dom.setAttribute('tabIndex', -1);
44914             
44915             for (var i = 0; i < this.toolbars.length; i++) {
44916                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44917                     this.toolbars[i].tb.show();
44918                     this.toolbars[i].footer.show();
44919                 }
44920             }
44921             
44922             //this.deferFocus();
44923         }
44924         
44925         this.setSize(this.wrap.getSize());
44926         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44927         
44928         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44929     },
44930  
44931     // private (for BoxComponent)
44932     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44933
44934     // private (for BoxComponent)
44935     getResizeEl : function(){
44936         return this.wrap;
44937     },
44938
44939     // private (for BoxComponent)
44940     getPositionEl : function(){
44941         return this.wrap;
44942     },
44943
44944     // private
44945     initEvents : function(){
44946         this.originalValue = this.getValue();
44947     },
44948
44949     /**
44950      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44951      * @method
44952      */
44953     markInvalid : Roo.emptyFn,
44954     /**
44955      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44956      * @method
44957      */
44958     clearInvalid : Roo.emptyFn,
44959
44960     setValue : function(v){
44961         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44962         this.editorcore.pushValue();
44963     },
44964
44965      
44966     // private
44967     deferFocus : function(){
44968         this.focus.defer(10, this);
44969     },
44970
44971     // doc'ed in Field
44972     focus : function(){
44973         this.editorcore.focus();
44974         
44975     },
44976       
44977
44978     // private
44979     onDestroy : function(){
44980         
44981         
44982         
44983         if(this.rendered){
44984             
44985             for (var i =0; i < this.toolbars.length;i++) {
44986                 // fixme - ask toolbars for heights?
44987                 this.toolbars[i].onDestroy();
44988             }
44989             
44990             this.wrap.dom.innerHTML = '';
44991             this.wrap.remove();
44992         }
44993     },
44994
44995     // private
44996     onFirstFocus : function(){
44997         //Roo.log("onFirstFocus");
44998         this.editorcore.onFirstFocus();
44999          for (var i =0; i < this.toolbars.length;i++) {
45000             this.toolbars[i].onFirstFocus();
45001         }
45002         
45003     },
45004     
45005     // private
45006     syncValue : function()
45007     {
45008         this.editorcore.syncValue();
45009     },
45010     
45011     pushValue : function()
45012     {
45013         this.editorcore.pushValue();
45014     },
45015     
45016     setStylesheets : function(stylesheets)
45017     {
45018         this.editorcore.setStylesheets(stylesheets);
45019     },
45020     
45021     removeStylesheets : function()
45022     {
45023         this.editorcore.removeStylesheets();
45024     }
45025      
45026     
45027     // hide stuff that is not compatible
45028     /**
45029      * @event blur
45030      * @hide
45031      */
45032     /**
45033      * @event change
45034      * @hide
45035      */
45036     /**
45037      * @event focus
45038      * @hide
45039      */
45040     /**
45041      * @event specialkey
45042      * @hide
45043      */
45044     /**
45045      * @cfg {String} fieldClass @hide
45046      */
45047     /**
45048      * @cfg {String} focusClass @hide
45049      */
45050     /**
45051      * @cfg {String} autoCreate @hide
45052      */
45053     /**
45054      * @cfg {String} inputType @hide
45055      */
45056     /**
45057      * @cfg {String} invalidClass @hide
45058      */
45059     /**
45060      * @cfg {String} invalidText @hide
45061      */
45062     /**
45063      * @cfg {String} msgFx @hide
45064      */
45065     /**
45066      * @cfg {String} validateOnBlur @hide
45067      */
45068 });
45069  
45070     // <script type="text/javascript">
45071 /*
45072  * Based on
45073  * Ext JS Library 1.1.1
45074  * Copyright(c) 2006-2007, Ext JS, LLC.
45075  *  
45076  
45077  */
45078
45079 /**
45080  * @class Roo.form.HtmlEditorToolbar1
45081  * Basic Toolbar
45082  * 
45083  * Usage:
45084  *
45085  new Roo.form.HtmlEditor({
45086     ....
45087     toolbars : [
45088         new Roo.form.HtmlEditorToolbar1({
45089             disable : { fonts: 1 , format: 1, ..., ... , ...],
45090             btns : [ .... ]
45091         })
45092     }
45093      
45094  * 
45095  * @cfg {Object} disable List of elements to disable..
45096  * @cfg {Array} btns List of additional buttons.
45097  * 
45098  * 
45099  * NEEDS Extra CSS? 
45100  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45101  */
45102  
45103 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45104 {
45105     
45106     Roo.apply(this, config);
45107     
45108     // default disabled, based on 'good practice'..
45109     this.disable = this.disable || {};
45110     Roo.applyIf(this.disable, {
45111         fontSize : true,
45112         colors : true,
45113         specialElements : true
45114     });
45115     
45116     
45117     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45118     // dont call parent... till later.
45119 }
45120
45121 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45122     
45123     tb: false,
45124     
45125     rendered: false,
45126     
45127     editor : false,
45128     editorcore : false,
45129     /**
45130      * @cfg {Object} disable  List of toolbar elements to disable
45131          
45132      */
45133     disable : false,
45134     
45135     
45136      /**
45137      * @cfg {String} createLinkText The default text for the create link prompt
45138      */
45139     createLinkText : 'Please enter the URL for the link:',
45140     /**
45141      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45142      */
45143     defaultLinkValue : 'http:/'+'/',
45144    
45145     
45146       /**
45147      * @cfg {Array} fontFamilies An array of available font families
45148      */
45149     fontFamilies : [
45150         'Arial',
45151         'Courier New',
45152         'Tahoma',
45153         'Times New Roman',
45154         'Verdana'
45155     ],
45156     
45157     specialChars : [
45158            "&#169;",
45159           "&#174;",     
45160           "&#8482;",    
45161           "&#163;" ,    
45162          // "&#8212;",    
45163           "&#8230;",    
45164           "&#247;" ,    
45165         //  "&#225;" ,     ?? a acute?
45166            "&#8364;"    , //Euro
45167        //   "&#8220;"    ,
45168         //  "&#8221;"    ,
45169         //  "&#8226;"    ,
45170           "&#176;"  //   , // degrees
45171
45172          // "&#233;"     , // e ecute
45173          // "&#250;"     , // u ecute?
45174     ],
45175     
45176     specialElements : [
45177         {
45178             text: "Insert Table",
45179             xtype: 'MenuItem',
45180             xns : Roo.Menu,
45181             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45182                 
45183         },
45184         {    
45185             text: "Insert Image",
45186             xtype: 'MenuItem',
45187             xns : Roo.Menu,
45188             ihtml : '<img src="about:blank"/>'
45189             
45190         }
45191         
45192          
45193     ],
45194     
45195     
45196     inputElements : [ 
45197             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45198             "input:submit", "input:button", "select", "textarea", "label" ],
45199     formats : [
45200         ["p"] ,  
45201         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45202         ["pre"],[ "code"], 
45203         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45204         ['div'],['span']
45205     ],
45206     
45207     cleanStyles : [
45208         "font-size"
45209     ],
45210      /**
45211      * @cfg {String} defaultFont default font to use.
45212      */
45213     defaultFont: 'tahoma',
45214    
45215     fontSelect : false,
45216     
45217     
45218     formatCombo : false,
45219     
45220     init : function(editor)
45221     {
45222         this.editor = editor;
45223         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45224         var editorcore = this.editorcore;
45225         
45226         var _t = this;
45227         
45228         var fid = editorcore.frameId;
45229         var etb = this;
45230         function btn(id, toggle, handler){
45231             var xid = fid + '-'+ id ;
45232             return {
45233                 id : xid,
45234                 cmd : id,
45235                 cls : 'x-btn-icon x-edit-'+id,
45236                 enableToggle:toggle !== false,
45237                 scope: _t, // was editor...
45238                 handler:handler||_t.relayBtnCmd,
45239                 clickEvent:'mousedown',
45240                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45241                 tabIndex:-1
45242             };
45243         }
45244         
45245         
45246         
45247         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45248         this.tb = tb;
45249          // stop form submits
45250         tb.el.on('click', function(e){
45251             e.preventDefault(); // what does this do?
45252         });
45253
45254         if(!this.disable.font) { // && !Roo.isSafari){
45255             /* why no safari for fonts 
45256             editor.fontSelect = tb.el.createChild({
45257                 tag:'select',
45258                 tabIndex: -1,
45259                 cls:'x-font-select',
45260                 html: this.createFontOptions()
45261             });
45262             
45263             editor.fontSelect.on('change', function(){
45264                 var font = editor.fontSelect.dom.value;
45265                 editor.relayCmd('fontname', font);
45266                 editor.deferFocus();
45267             }, editor);
45268             
45269             tb.add(
45270                 editor.fontSelect.dom,
45271                 '-'
45272             );
45273             */
45274             
45275         };
45276         if(!this.disable.formats){
45277             this.formatCombo = new Roo.form.ComboBox({
45278                 store: new Roo.data.SimpleStore({
45279                     id : 'tag',
45280                     fields: ['tag'],
45281                     data : this.formats // from states.js
45282                 }),
45283                 blockFocus : true,
45284                 name : '',
45285                 //autoCreate : {tag: "div",  size: "20"},
45286                 displayField:'tag',
45287                 typeAhead: false,
45288                 mode: 'local',
45289                 editable : false,
45290                 triggerAction: 'all',
45291                 emptyText:'Add tag',
45292                 selectOnFocus:true,
45293                 width:135,
45294                 listeners : {
45295                     'select': function(c, r, i) {
45296                         editorcore.insertTag(r.get('tag'));
45297                         editor.focus();
45298                     }
45299                 }
45300
45301             });
45302             tb.addField(this.formatCombo);
45303             
45304         }
45305         
45306         if(!this.disable.format){
45307             tb.add(
45308                 btn('bold'),
45309                 btn('italic'),
45310                 btn('underline'),
45311                 btn('strikethrough')
45312             );
45313         };
45314         if(!this.disable.fontSize){
45315             tb.add(
45316                 '-',
45317                 
45318                 
45319                 btn('increasefontsize', false, editorcore.adjustFont),
45320                 btn('decreasefontsize', false, editorcore.adjustFont)
45321             );
45322         };
45323         
45324         
45325         if(!this.disable.colors){
45326             tb.add(
45327                 '-', {
45328                     id:editorcore.frameId +'-forecolor',
45329                     cls:'x-btn-icon x-edit-forecolor',
45330                     clickEvent:'mousedown',
45331                     tooltip: this.buttonTips['forecolor'] || undefined,
45332                     tabIndex:-1,
45333                     menu : new Roo.menu.ColorMenu({
45334                         allowReselect: true,
45335                         focus: Roo.emptyFn,
45336                         value:'000000',
45337                         plain:true,
45338                         selectHandler: function(cp, color){
45339                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45340                             editor.deferFocus();
45341                         },
45342                         scope: editorcore,
45343                         clickEvent:'mousedown'
45344                     })
45345                 }, {
45346                     id:editorcore.frameId +'backcolor',
45347                     cls:'x-btn-icon x-edit-backcolor',
45348                     clickEvent:'mousedown',
45349                     tooltip: this.buttonTips['backcolor'] || undefined,
45350                     tabIndex:-1,
45351                     menu : new Roo.menu.ColorMenu({
45352                         focus: Roo.emptyFn,
45353                         value:'FFFFFF',
45354                         plain:true,
45355                         allowReselect: true,
45356                         selectHandler: function(cp, color){
45357                             if(Roo.isGecko){
45358                                 editorcore.execCmd('useCSS', false);
45359                                 editorcore.execCmd('hilitecolor', color);
45360                                 editorcore.execCmd('useCSS', true);
45361                                 editor.deferFocus();
45362                             }else{
45363                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45364                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45365                                 editor.deferFocus();
45366                             }
45367                         },
45368                         scope:editorcore,
45369                         clickEvent:'mousedown'
45370                     })
45371                 }
45372             );
45373         };
45374         // now add all the items...
45375         
45376
45377         if(!this.disable.alignments){
45378             tb.add(
45379                 '-',
45380                 btn('justifyleft'),
45381                 btn('justifycenter'),
45382                 btn('justifyright')
45383             );
45384         };
45385
45386         //if(!Roo.isSafari){
45387             if(!this.disable.links){
45388                 tb.add(
45389                     '-',
45390                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45391                 );
45392             };
45393
45394             if(!this.disable.lists){
45395                 tb.add(
45396                     '-',
45397                     btn('insertorderedlist'),
45398                     btn('insertunorderedlist')
45399                 );
45400             }
45401             if(!this.disable.sourceEdit){
45402                 tb.add(
45403                     '-',
45404                     btn('sourceedit', true, function(btn){
45405                         this.toggleSourceEdit(btn.pressed);
45406                     })
45407                 );
45408             }
45409         //}
45410         
45411         var smenu = { };
45412         // special menu.. - needs to be tidied up..
45413         if (!this.disable.special) {
45414             smenu = {
45415                 text: "&#169;",
45416                 cls: 'x-edit-none',
45417                 
45418                 menu : {
45419                     items : []
45420                 }
45421             };
45422             for (var i =0; i < this.specialChars.length; i++) {
45423                 smenu.menu.items.push({
45424                     
45425                     html: this.specialChars[i],
45426                     handler: function(a,b) {
45427                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45428                         //editor.insertAtCursor(a.html);
45429                         
45430                     },
45431                     tabIndex:-1
45432                 });
45433             }
45434             
45435             
45436             tb.add(smenu);
45437             
45438             
45439         }
45440         
45441         var cmenu = { };
45442         if (!this.disable.cleanStyles) {
45443             cmenu = {
45444                 cls: 'x-btn-icon x-btn-clear',
45445                 
45446                 menu : {
45447                     items : []
45448                 }
45449             };
45450             for (var i =0; i < this.cleanStyles.length; i++) {
45451                 cmenu.menu.items.push({
45452                     actiontype : this.cleanStyles[i],
45453                     html: 'Remove ' + this.cleanStyles[i],
45454                     handler: function(a,b) {
45455 //                        Roo.log(a);
45456 //                        Roo.log(b);
45457                         var c = Roo.get(editorcore.doc.body);
45458                         c.select('[style]').each(function(s) {
45459                             s.dom.style.removeProperty(a.actiontype);
45460                         });
45461                         editorcore.syncValue();
45462                     },
45463                     tabIndex:-1
45464                 });
45465             }
45466              cmenu.menu.items.push({
45467                 actiontype : 'tablewidths',
45468                 html: 'Remove Table Widths',
45469                 handler: function(a,b) {
45470                     editorcore.cleanTableWidths();
45471                     editorcore.syncValue();
45472                 },
45473                 tabIndex:-1
45474             });
45475             cmenu.menu.items.push({
45476                 actiontype : 'word',
45477                 html: 'Remove MS Word Formating',
45478                 handler: function(a,b) {
45479                     editorcore.cleanWord();
45480                     editorcore.syncValue();
45481                 },
45482                 tabIndex:-1
45483             });
45484             
45485             cmenu.menu.items.push({
45486                 actiontype : 'all',
45487                 html: 'Remove All Styles',
45488                 handler: function(a,b) {
45489                     
45490                     var c = Roo.get(editorcore.doc.body);
45491                     c.select('[style]').each(function(s) {
45492                         s.dom.removeAttribute('style');
45493                     });
45494                     editorcore.syncValue();
45495                 },
45496                 tabIndex:-1
45497             });
45498             
45499             cmenu.menu.items.push({
45500                 actiontype : 'all',
45501                 html: 'Remove All CSS Classes',
45502                 handler: function(a,b) {
45503                     
45504                     var c = Roo.get(editorcore.doc.body);
45505                     c.select('[class]').each(function(s) {
45506                         s.dom.className = '';
45507                     });
45508                     editorcore.syncValue();
45509                 },
45510                 tabIndex:-1
45511             });
45512             
45513              cmenu.menu.items.push({
45514                 actiontype : 'tidy',
45515                 html: 'Tidy HTML Source',
45516                 handler: function(a,b) {
45517                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45518                     editorcore.syncValue();
45519                 },
45520                 tabIndex:-1
45521             });
45522             
45523             
45524             tb.add(cmenu);
45525         }
45526          
45527         if (!this.disable.specialElements) {
45528             var semenu = {
45529                 text: "Other;",
45530                 cls: 'x-edit-none',
45531                 menu : {
45532                     items : []
45533                 }
45534             };
45535             for (var i =0; i < this.specialElements.length; i++) {
45536                 semenu.menu.items.push(
45537                     Roo.apply({ 
45538                         handler: function(a,b) {
45539                             editor.insertAtCursor(this.ihtml);
45540                         }
45541                     }, this.specialElements[i])
45542                 );
45543                     
45544             }
45545             
45546             tb.add(semenu);
45547             
45548             
45549         }
45550          
45551         
45552         if (this.btns) {
45553             for(var i =0; i< this.btns.length;i++) {
45554                 var b = Roo.factory(this.btns[i],Roo.form);
45555                 b.cls =  'x-edit-none';
45556                 
45557                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45558                     b.cls += ' x-init-enable';
45559                 }
45560                 
45561                 b.scope = editorcore;
45562                 tb.add(b);
45563             }
45564         
45565         }
45566         
45567         
45568         
45569         // disable everything...
45570         
45571         this.tb.items.each(function(item){
45572             
45573            if(
45574                 item.id != editorcore.frameId+ '-sourceedit' && 
45575                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45576             ){
45577                 
45578                 item.disable();
45579             }
45580         });
45581         this.rendered = true;
45582         
45583         // the all the btns;
45584         editor.on('editorevent', this.updateToolbar, this);
45585         // other toolbars need to implement this..
45586         //editor.on('editmodechange', this.updateToolbar, this);
45587     },
45588     
45589     
45590     relayBtnCmd : function(btn) {
45591         this.editorcore.relayCmd(btn.cmd);
45592     },
45593     // private used internally
45594     createLink : function(){
45595         Roo.log("create link?");
45596         var url = prompt(this.createLinkText, this.defaultLinkValue);
45597         if(url && url != 'http:/'+'/'){
45598             this.editorcore.relayCmd('createlink', url);
45599         }
45600     },
45601
45602     
45603     /**
45604      * Protected method that will not generally be called directly. It triggers
45605      * a toolbar update by reading the markup state of the current selection in the editor.
45606      */
45607     updateToolbar: function(){
45608
45609         if(!this.editorcore.activated){
45610             this.editor.onFirstFocus();
45611             return;
45612         }
45613
45614         var btns = this.tb.items.map, 
45615             doc = this.editorcore.doc,
45616             frameId = this.editorcore.frameId;
45617
45618         if(!this.disable.font && !Roo.isSafari){
45619             /*
45620             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45621             if(name != this.fontSelect.dom.value){
45622                 this.fontSelect.dom.value = name;
45623             }
45624             */
45625         }
45626         if(!this.disable.format){
45627             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45628             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45629             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45630             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45631         }
45632         if(!this.disable.alignments){
45633             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45634             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45635             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45636         }
45637         if(!Roo.isSafari && !this.disable.lists){
45638             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45639             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45640         }
45641         
45642         var ans = this.editorcore.getAllAncestors();
45643         if (this.formatCombo) {
45644             
45645             
45646             var store = this.formatCombo.store;
45647             this.formatCombo.setValue("");
45648             for (var i =0; i < ans.length;i++) {
45649                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45650                     // select it..
45651                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45652                     break;
45653                 }
45654             }
45655         }
45656         
45657         
45658         
45659         // hides menus... - so this cant be on a menu...
45660         Roo.menu.MenuMgr.hideAll();
45661
45662         //this.editorsyncValue();
45663     },
45664    
45665     
45666     createFontOptions : function(){
45667         var buf = [], fs = this.fontFamilies, ff, lc;
45668         
45669         
45670         
45671         for(var i = 0, len = fs.length; i< len; i++){
45672             ff = fs[i];
45673             lc = ff.toLowerCase();
45674             buf.push(
45675                 '<option value="',lc,'" style="font-family:',ff,';"',
45676                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45677                     ff,
45678                 '</option>'
45679             );
45680         }
45681         return buf.join('');
45682     },
45683     
45684     toggleSourceEdit : function(sourceEditMode){
45685         
45686         Roo.log("toolbar toogle");
45687         if(sourceEditMode === undefined){
45688             sourceEditMode = !this.sourceEditMode;
45689         }
45690         this.sourceEditMode = sourceEditMode === true;
45691         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45692         // just toggle the button?
45693         if(btn.pressed !== this.sourceEditMode){
45694             btn.toggle(this.sourceEditMode);
45695             return;
45696         }
45697         
45698         if(sourceEditMode){
45699             Roo.log("disabling buttons");
45700             this.tb.items.each(function(item){
45701                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45702                     item.disable();
45703                 }
45704             });
45705           
45706         }else{
45707             Roo.log("enabling buttons");
45708             if(this.editorcore.initialized){
45709                 this.tb.items.each(function(item){
45710                     item.enable();
45711                 });
45712             }
45713             
45714         }
45715         Roo.log("calling toggole on editor");
45716         // tell the editor that it's been pressed..
45717         this.editor.toggleSourceEdit(sourceEditMode);
45718        
45719     },
45720      /**
45721      * Object collection of toolbar tooltips for the buttons in the editor. The key
45722      * is the command id associated with that button and the value is a valid QuickTips object.
45723      * For example:
45724 <pre><code>
45725 {
45726     bold : {
45727         title: 'Bold (Ctrl+B)',
45728         text: 'Make the selected text bold.',
45729         cls: 'x-html-editor-tip'
45730     },
45731     italic : {
45732         title: 'Italic (Ctrl+I)',
45733         text: 'Make the selected text italic.',
45734         cls: 'x-html-editor-tip'
45735     },
45736     ...
45737 </code></pre>
45738     * @type Object
45739      */
45740     buttonTips : {
45741         bold : {
45742             title: 'Bold (Ctrl+B)',
45743             text: 'Make the selected text bold.',
45744             cls: 'x-html-editor-tip'
45745         },
45746         italic : {
45747             title: 'Italic (Ctrl+I)',
45748             text: 'Make the selected text italic.',
45749             cls: 'x-html-editor-tip'
45750         },
45751         underline : {
45752             title: 'Underline (Ctrl+U)',
45753             text: 'Underline the selected text.',
45754             cls: 'x-html-editor-tip'
45755         },
45756         strikethrough : {
45757             title: 'Strikethrough',
45758             text: 'Strikethrough the selected text.',
45759             cls: 'x-html-editor-tip'
45760         },
45761         increasefontsize : {
45762             title: 'Grow Text',
45763             text: 'Increase the font size.',
45764             cls: 'x-html-editor-tip'
45765         },
45766         decreasefontsize : {
45767             title: 'Shrink Text',
45768             text: 'Decrease the font size.',
45769             cls: 'x-html-editor-tip'
45770         },
45771         backcolor : {
45772             title: 'Text Highlight Color',
45773             text: 'Change the background color of the selected text.',
45774             cls: 'x-html-editor-tip'
45775         },
45776         forecolor : {
45777             title: 'Font Color',
45778             text: 'Change the color of the selected text.',
45779             cls: 'x-html-editor-tip'
45780         },
45781         justifyleft : {
45782             title: 'Align Text Left',
45783             text: 'Align text to the left.',
45784             cls: 'x-html-editor-tip'
45785         },
45786         justifycenter : {
45787             title: 'Center Text',
45788             text: 'Center text in the editor.',
45789             cls: 'x-html-editor-tip'
45790         },
45791         justifyright : {
45792             title: 'Align Text Right',
45793             text: 'Align text to the right.',
45794             cls: 'x-html-editor-tip'
45795         },
45796         insertunorderedlist : {
45797             title: 'Bullet List',
45798             text: 'Start a bulleted list.',
45799             cls: 'x-html-editor-tip'
45800         },
45801         insertorderedlist : {
45802             title: 'Numbered List',
45803             text: 'Start a numbered list.',
45804             cls: 'x-html-editor-tip'
45805         },
45806         createlink : {
45807             title: 'Hyperlink',
45808             text: 'Make the selected text a hyperlink.',
45809             cls: 'x-html-editor-tip'
45810         },
45811         sourceedit : {
45812             title: 'Source Edit',
45813             text: 'Switch to source editing mode.',
45814             cls: 'x-html-editor-tip'
45815         }
45816     },
45817     // private
45818     onDestroy : function(){
45819         if(this.rendered){
45820             
45821             this.tb.items.each(function(item){
45822                 if(item.menu){
45823                     item.menu.removeAll();
45824                     if(item.menu.el){
45825                         item.menu.el.destroy();
45826                     }
45827                 }
45828                 item.destroy();
45829             });
45830              
45831         }
45832     },
45833     onFirstFocus: function() {
45834         this.tb.items.each(function(item){
45835            item.enable();
45836         });
45837     }
45838 });
45839
45840
45841
45842
45843 // <script type="text/javascript">
45844 /*
45845  * Based on
45846  * Ext JS Library 1.1.1
45847  * Copyright(c) 2006-2007, Ext JS, LLC.
45848  *  
45849  
45850  */
45851
45852  
45853 /**
45854  * @class Roo.form.HtmlEditor.ToolbarContext
45855  * Context Toolbar
45856  * 
45857  * Usage:
45858  *
45859  new Roo.form.HtmlEditor({
45860     ....
45861     toolbars : [
45862         { xtype: 'ToolbarStandard', styles : {} }
45863         { xtype: 'ToolbarContext', disable : {} }
45864     ]
45865 })
45866
45867      
45868  * 
45869  * @config : {Object} disable List of elements to disable.. (not done yet.)
45870  * @config : {Object} styles  Map of styles available.
45871  * 
45872  */
45873
45874 Roo.form.HtmlEditor.ToolbarContext = function(config)
45875 {
45876     
45877     Roo.apply(this, config);
45878     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45879     // dont call parent... till later.
45880     this.styles = this.styles || {};
45881 }
45882
45883  
45884
45885 Roo.form.HtmlEditor.ToolbarContext.types = {
45886     'IMG' : {
45887         width : {
45888             title: "Width",
45889             width: 40
45890         },
45891         height:  {
45892             title: "Height",
45893             width: 40
45894         },
45895         align: {
45896             title: "Align",
45897             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45898             width : 80
45899             
45900         },
45901         border: {
45902             title: "Border",
45903             width: 40
45904         },
45905         alt: {
45906             title: "Alt",
45907             width: 120
45908         },
45909         src : {
45910             title: "Src",
45911             width: 220
45912         }
45913         
45914     },
45915     'A' : {
45916         name : {
45917             title: "Name",
45918             width: 50
45919         },
45920         target:  {
45921             title: "Target",
45922             width: 120
45923         },
45924         href:  {
45925             title: "Href",
45926             width: 220
45927         } // border?
45928         
45929     },
45930     'TABLE' : {
45931         rows : {
45932             title: "Rows",
45933             width: 20
45934         },
45935         cols : {
45936             title: "Cols",
45937             width: 20
45938         },
45939         width : {
45940             title: "Width",
45941             width: 40
45942         },
45943         height : {
45944             title: "Height",
45945             width: 40
45946         },
45947         border : {
45948             title: "Border",
45949             width: 20
45950         }
45951     },
45952     'TD' : {
45953         width : {
45954             title: "Width",
45955             width: 40
45956         },
45957         height : {
45958             title: "Height",
45959             width: 40
45960         },   
45961         align: {
45962             title: "Align",
45963             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45964             width: 80
45965         },
45966         valign: {
45967             title: "Valign",
45968             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45969             width: 80
45970         },
45971         colspan: {
45972             title: "Colspan",
45973             width: 20
45974             
45975         },
45976          'font-family'  : {
45977             title : "Font",
45978             style : 'fontFamily',
45979             displayField: 'display',
45980             optname : 'font-family',
45981             width: 140
45982         }
45983     },
45984     'INPUT' : {
45985         name : {
45986             title: "name",
45987             width: 120
45988         },
45989         value : {
45990             title: "Value",
45991             width: 120
45992         },
45993         width : {
45994             title: "Width",
45995             width: 40
45996         }
45997     },
45998     'LABEL' : {
45999         'for' : {
46000             title: "For",
46001             width: 120
46002         }
46003     },
46004     'TEXTAREA' : {
46005           name : {
46006             title: "name",
46007             width: 120
46008         },
46009         rows : {
46010             title: "Rows",
46011             width: 20
46012         },
46013         cols : {
46014             title: "Cols",
46015             width: 20
46016         }
46017     },
46018     'SELECT' : {
46019         name : {
46020             title: "name",
46021             width: 120
46022         },
46023         selectoptions : {
46024             title: "Options",
46025             width: 200
46026         }
46027     },
46028     
46029     // should we really allow this??
46030     // should this just be 
46031     'BODY' : {
46032         title : {
46033             title: "Title",
46034             width: 200,
46035             disabled : true
46036         }
46037     },
46038     'SPAN' : {
46039         'font-family'  : {
46040             title : "Font",
46041             style : 'fontFamily',
46042             displayField: 'display',
46043             optname : 'font-family',
46044             width: 140
46045         }
46046     },
46047     'DIV' : {
46048         'font-family'  : {
46049             title : "Font",
46050             style : 'fontFamily',
46051             displayField: 'display',
46052             optname : 'font-family',
46053             width: 140
46054         }
46055     },
46056      'P' : {
46057         'font-family'  : {
46058             title : "Font",
46059             style : 'fontFamily',
46060             displayField: 'display',
46061             optname : 'font-family',
46062             width: 140
46063         }
46064     },
46065     
46066     '*' : {
46067         // empty..
46068     }
46069
46070 };
46071
46072 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46073 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46074
46075 Roo.form.HtmlEditor.ToolbarContext.options = {
46076         'font-family'  : [ 
46077                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46078                 [ 'Courier New', 'Courier New'],
46079                 [ 'Tahoma', 'Tahoma'],
46080                 [ 'Times New Roman,serif', 'Times'],
46081                 [ 'Verdana','Verdana' ]
46082         ]
46083 };
46084
46085 // fixme - these need to be configurable..
46086  
46087
46088 //Roo.form.HtmlEditor.ToolbarContext.types
46089
46090
46091 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46092     
46093     tb: false,
46094     
46095     rendered: false,
46096     
46097     editor : false,
46098     editorcore : false,
46099     /**
46100      * @cfg {Object} disable  List of toolbar elements to disable
46101          
46102      */
46103     disable : false,
46104     /**
46105      * @cfg {Object} styles List of styles 
46106      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46107      *
46108      * These must be defined in the page, so they get rendered correctly..
46109      * .headline { }
46110      * TD.underline { }
46111      * 
46112      */
46113     styles : false,
46114     
46115     options: false,
46116     
46117     toolbars : false,
46118     
46119     init : function(editor)
46120     {
46121         this.editor = editor;
46122         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46123         var editorcore = this.editorcore;
46124         
46125         var fid = editorcore.frameId;
46126         var etb = this;
46127         function btn(id, toggle, handler){
46128             var xid = fid + '-'+ id ;
46129             return {
46130                 id : xid,
46131                 cmd : id,
46132                 cls : 'x-btn-icon x-edit-'+id,
46133                 enableToggle:toggle !== false,
46134                 scope: editorcore, // was editor...
46135                 handler:handler||editorcore.relayBtnCmd,
46136                 clickEvent:'mousedown',
46137                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46138                 tabIndex:-1
46139             };
46140         }
46141         // create a new element.
46142         var wdiv = editor.wrap.createChild({
46143                 tag: 'div'
46144             }, editor.wrap.dom.firstChild.nextSibling, true);
46145         
46146         // can we do this more than once??
46147         
46148          // stop form submits
46149       
46150  
46151         // disable everything...
46152         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46153         this.toolbars = {};
46154            
46155         for (var i in  ty) {
46156           
46157             this.toolbars[i] = this.buildToolbar(ty[i],i);
46158         }
46159         this.tb = this.toolbars.BODY;
46160         this.tb.el.show();
46161         this.buildFooter();
46162         this.footer.show();
46163         editor.on('hide', function( ) { this.footer.hide() }, this);
46164         editor.on('show', function( ) { this.footer.show() }, this);
46165         
46166          
46167         this.rendered = true;
46168         
46169         // the all the btns;
46170         editor.on('editorevent', this.updateToolbar, this);
46171         // other toolbars need to implement this..
46172         //editor.on('editmodechange', this.updateToolbar, this);
46173     },
46174     
46175     
46176     
46177     /**
46178      * Protected method that will not generally be called directly. It triggers
46179      * a toolbar update by reading the markup state of the current selection in the editor.
46180      *
46181      * Note you can force an update by calling on('editorevent', scope, false)
46182      */
46183     updateToolbar: function(editor,ev,sel){
46184
46185         //Roo.log(ev);
46186         // capture mouse up - this is handy for selecting images..
46187         // perhaps should go somewhere else...
46188         if(!this.editorcore.activated){
46189              this.editor.onFirstFocus();
46190             return;
46191         }
46192         
46193         
46194         
46195         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46196         // selectNode - might want to handle IE?
46197         if (ev &&
46198             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46199             ev.target && ev.target.tagName == 'IMG') {
46200             // they have click on an image...
46201             // let's see if we can change the selection...
46202             sel = ev.target;
46203          
46204               var nodeRange = sel.ownerDocument.createRange();
46205             try {
46206                 nodeRange.selectNode(sel);
46207             } catch (e) {
46208                 nodeRange.selectNodeContents(sel);
46209             }
46210             //nodeRange.collapse(true);
46211             var s = this.editorcore.win.getSelection();
46212             s.removeAllRanges();
46213             s.addRange(nodeRange);
46214         }  
46215         
46216       
46217         var updateFooter = sel ? false : true;
46218         
46219         
46220         var ans = this.editorcore.getAllAncestors();
46221         
46222         // pick
46223         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46224         
46225         if (!sel) { 
46226             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46227             sel = sel ? sel : this.editorcore.doc.body;
46228             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46229             
46230         }
46231         // pick a menu that exists..
46232         var tn = sel.tagName.toUpperCase();
46233         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46234         
46235         tn = sel.tagName.toUpperCase();
46236         
46237         var lastSel = this.tb.selectedNode;
46238         
46239         this.tb.selectedNode = sel;
46240         
46241         // if current menu does not match..
46242         
46243         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46244                 
46245             this.tb.el.hide();
46246             ///console.log("show: " + tn);
46247             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46248             this.tb.el.show();
46249             // update name
46250             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46251             
46252             
46253             // update attributes
46254             if (this.tb.fields) {
46255                 this.tb.fields.each(function(e) {
46256                     if (e.stylename) {
46257                         e.setValue(sel.style[e.stylename]);
46258                         return;
46259                     } 
46260                    e.setValue(sel.getAttribute(e.attrname));
46261                 });
46262             }
46263             
46264             var hasStyles = false;
46265             for(var i in this.styles) {
46266                 hasStyles = true;
46267                 break;
46268             }
46269             
46270             // update styles
46271             if (hasStyles) { 
46272                 var st = this.tb.fields.item(0);
46273                 
46274                 st.store.removeAll();
46275                
46276                 
46277                 var cn = sel.className.split(/\s+/);
46278                 
46279                 var avs = [];
46280                 if (this.styles['*']) {
46281                     
46282                     Roo.each(this.styles['*'], function(v) {
46283                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46284                     });
46285                 }
46286                 if (this.styles[tn]) { 
46287                     Roo.each(this.styles[tn], function(v) {
46288                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46289                     });
46290                 }
46291                 
46292                 st.store.loadData(avs);
46293                 st.collapse();
46294                 st.setValue(cn);
46295             }
46296             // flag our selected Node.
46297             this.tb.selectedNode = sel;
46298            
46299            
46300             Roo.menu.MenuMgr.hideAll();
46301
46302         }
46303         
46304         if (!updateFooter) {
46305             //this.footDisp.dom.innerHTML = ''; 
46306             return;
46307         }
46308         // update the footer
46309         //
46310         var html = '';
46311         
46312         this.footerEls = ans.reverse();
46313         Roo.each(this.footerEls, function(a,i) {
46314             if (!a) { return; }
46315             html += html.length ? ' &gt; '  :  '';
46316             
46317             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46318             
46319         });
46320        
46321         // 
46322         var sz = this.footDisp.up('td').getSize();
46323         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46324         this.footDisp.dom.style.marginLeft = '5px';
46325         
46326         this.footDisp.dom.style.overflow = 'hidden';
46327         
46328         this.footDisp.dom.innerHTML = html;
46329             
46330         //this.editorsyncValue();
46331     },
46332      
46333     
46334    
46335        
46336     // private
46337     onDestroy : function(){
46338         if(this.rendered){
46339             
46340             this.tb.items.each(function(item){
46341                 if(item.menu){
46342                     item.menu.removeAll();
46343                     if(item.menu.el){
46344                         item.menu.el.destroy();
46345                     }
46346                 }
46347                 item.destroy();
46348             });
46349              
46350         }
46351     },
46352     onFirstFocus: function() {
46353         // need to do this for all the toolbars..
46354         this.tb.items.each(function(item){
46355            item.enable();
46356         });
46357     },
46358     buildToolbar: function(tlist, nm)
46359     {
46360         var editor = this.editor;
46361         var editorcore = this.editorcore;
46362          // create a new element.
46363         var wdiv = editor.wrap.createChild({
46364                 tag: 'div'
46365             }, editor.wrap.dom.firstChild.nextSibling, true);
46366         
46367        
46368         var tb = new Roo.Toolbar(wdiv);
46369         // add the name..
46370         
46371         tb.add(nm+ ":&nbsp;");
46372         
46373         var styles = [];
46374         for(var i in this.styles) {
46375             styles.push(i);
46376         }
46377         
46378         // styles...
46379         if (styles && styles.length) {
46380             
46381             // this needs a multi-select checkbox...
46382             tb.addField( new Roo.form.ComboBox({
46383                 store: new Roo.data.SimpleStore({
46384                     id : 'val',
46385                     fields: ['val', 'selected'],
46386                     data : [] 
46387                 }),
46388                 name : '-roo-edit-className',
46389                 attrname : 'className',
46390                 displayField: 'val',
46391                 typeAhead: false,
46392                 mode: 'local',
46393                 editable : false,
46394                 triggerAction: 'all',
46395                 emptyText:'Select Style',
46396                 selectOnFocus:true,
46397                 width: 130,
46398                 listeners : {
46399                     'select': function(c, r, i) {
46400                         // initial support only for on class per el..
46401                         tb.selectedNode.className =  r ? r.get('val') : '';
46402                         editorcore.syncValue();
46403                     }
46404                 }
46405     
46406             }));
46407         }
46408         
46409         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46410         var tbops = tbc.options;
46411         
46412         for (var i in tlist) {
46413             
46414             var item = tlist[i];
46415             tb.add(item.title + ":&nbsp;");
46416             
46417             
46418             //optname == used so you can configure the options available..
46419             var opts = item.opts ? item.opts : false;
46420             if (item.optname) {
46421                 opts = tbops[item.optname];
46422            
46423             }
46424             
46425             if (opts) {
46426                 // opts == pulldown..
46427                 tb.addField( new Roo.form.ComboBox({
46428                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46429                         id : 'val',
46430                         fields: ['val', 'display'],
46431                         data : opts  
46432                     }),
46433                     name : '-roo-edit-' + i,
46434                     attrname : i,
46435                     stylename : item.style ? item.style : false,
46436                     displayField: item.displayField ? item.displayField : 'val',
46437                     valueField :  'val',
46438                     typeAhead: false,
46439                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46440                     editable : false,
46441                     triggerAction: 'all',
46442                     emptyText:'Select',
46443                     selectOnFocus:true,
46444                     width: item.width ? item.width  : 130,
46445                     listeners : {
46446                         'select': function(c, r, i) {
46447                             if (c.stylename) {
46448                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46449                                 return;
46450                             }
46451                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46452                         }
46453                     }
46454
46455                 }));
46456                 continue;
46457                     
46458                  
46459                 
46460                 tb.addField( new Roo.form.TextField({
46461                     name: i,
46462                     width: 100,
46463                     //allowBlank:false,
46464                     value: ''
46465                 }));
46466                 continue;
46467             }
46468             tb.addField( new Roo.form.TextField({
46469                 name: '-roo-edit-' + i,
46470                 attrname : i,
46471                 
46472                 width: item.width,
46473                 //allowBlank:true,
46474                 value: '',
46475                 listeners: {
46476                     'change' : function(f, nv, ov) {
46477                         tb.selectedNode.setAttribute(f.attrname, nv);
46478                         editorcore.syncValue();
46479                     }
46480                 }
46481             }));
46482              
46483         }
46484         
46485         var _this = this;
46486         
46487         if(nm == 'BODY'){
46488             tb.addSeparator();
46489         
46490             tb.addButton( {
46491                 text: 'Stylesheets',
46492
46493                 listeners : {
46494                     click : function ()
46495                     {
46496                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46497                     }
46498                 }
46499             });
46500         }
46501         
46502         tb.addFill();
46503         tb.addButton( {
46504             text: 'Remove Tag',
46505     
46506             listeners : {
46507                 click : function ()
46508                 {
46509                     // remove
46510                     // undo does not work.
46511                      
46512                     var sn = tb.selectedNode;
46513                     
46514                     var pn = sn.parentNode;
46515                     
46516                     var stn =  sn.childNodes[0];
46517                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46518                     while (sn.childNodes.length) {
46519                         var node = sn.childNodes[0];
46520                         sn.removeChild(node);
46521                         //Roo.log(node);
46522                         pn.insertBefore(node, sn);
46523                         
46524                     }
46525                     pn.removeChild(sn);
46526                     var range = editorcore.createRange();
46527         
46528                     range.setStart(stn,0);
46529                     range.setEnd(en,0); //????
46530                     //range.selectNode(sel);
46531                     
46532                     
46533                     var selection = editorcore.getSelection();
46534                     selection.removeAllRanges();
46535                     selection.addRange(range);
46536                     
46537                     
46538                     
46539                     //_this.updateToolbar(null, null, pn);
46540                     _this.updateToolbar(null, null, null);
46541                     _this.footDisp.dom.innerHTML = ''; 
46542                 }
46543             }
46544             
46545                     
46546                 
46547             
46548         });
46549         
46550         
46551         tb.el.on('click', function(e){
46552             e.preventDefault(); // what does this do?
46553         });
46554         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46555         tb.el.hide();
46556         tb.name = nm;
46557         // dont need to disable them... as they will get hidden
46558         return tb;
46559          
46560         
46561     },
46562     buildFooter : function()
46563     {
46564         
46565         var fel = this.editor.wrap.createChild();
46566         this.footer = new Roo.Toolbar(fel);
46567         // toolbar has scrolly on left / right?
46568         var footDisp= new Roo.Toolbar.Fill();
46569         var _t = this;
46570         this.footer.add(
46571             {
46572                 text : '&lt;',
46573                 xtype: 'Button',
46574                 handler : function() {
46575                     _t.footDisp.scrollTo('left',0,true)
46576                 }
46577             }
46578         );
46579         this.footer.add( footDisp );
46580         this.footer.add( 
46581             {
46582                 text : '&gt;',
46583                 xtype: 'Button',
46584                 handler : function() {
46585                     // no animation..
46586                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46587                 }
46588             }
46589         );
46590         var fel = Roo.get(footDisp.el);
46591         fel.addClass('x-editor-context');
46592         this.footDispWrap = fel; 
46593         this.footDispWrap.overflow  = 'hidden';
46594         
46595         this.footDisp = fel.createChild();
46596         this.footDispWrap.on('click', this.onContextClick, this)
46597         
46598         
46599     },
46600     onContextClick : function (ev,dom)
46601     {
46602         ev.preventDefault();
46603         var  cn = dom.className;
46604         //Roo.log(cn);
46605         if (!cn.match(/x-ed-loc-/)) {
46606             return;
46607         }
46608         var n = cn.split('-').pop();
46609         var ans = this.footerEls;
46610         var sel = ans[n];
46611         
46612          // pick
46613         var range = this.editorcore.createRange();
46614         
46615         range.selectNodeContents(sel);
46616         //range.selectNode(sel);
46617         
46618         
46619         var selection = this.editorcore.getSelection();
46620         selection.removeAllRanges();
46621         selection.addRange(range);
46622         
46623         
46624         
46625         this.updateToolbar(null, null, sel);
46626         
46627         
46628     }
46629     
46630     
46631     
46632     
46633     
46634 });
46635
46636
46637
46638
46639
46640 /*
46641  * Based on:
46642  * Ext JS Library 1.1.1
46643  * Copyright(c) 2006-2007, Ext JS, LLC.
46644  *
46645  * Originally Released Under LGPL - original licence link has changed is not relivant.
46646  *
46647  * Fork - LGPL
46648  * <script type="text/javascript">
46649  */
46650  
46651 /**
46652  * @class Roo.form.BasicForm
46653  * @extends Roo.util.Observable
46654  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46655  * @constructor
46656  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46657  * @param {Object} config Configuration options
46658  */
46659 Roo.form.BasicForm = function(el, config){
46660     this.allItems = [];
46661     this.childForms = [];
46662     Roo.apply(this, config);
46663     /*
46664      * The Roo.form.Field items in this form.
46665      * @type MixedCollection
46666      */
46667      
46668      
46669     this.items = new Roo.util.MixedCollection(false, function(o){
46670         return o.id || (o.id = Roo.id());
46671     });
46672     this.addEvents({
46673         /**
46674          * @event beforeaction
46675          * Fires before any action is performed. Return false to cancel the action.
46676          * @param {Form} this
46677          * @param {Action} action The action to be performed
46678          */
46679         beforeaction: true,
46680         /**
46681          * @event actionfailed
46682          * Fires when an action fails.
46683          * @param {Form} this
46684          * @param {Action} action The action that failed
46685          */
46686         actionfailed : true,
46687         /**
46688          * @event actioncomplete
46689          * Fires when an action is completed.
46690          * @param {Form} this
46691          * @param {Action} action The action that completed
46692          */
46693         actioncomplete : true
46694     });
46695     if(el){
46696         this.initEl(el);
46697     }
46698     Roo.form.BasicForm.superclass.constructor.call(this);
46699 };
46700
46701 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46702     /**
46703      * @cfg {String} method
46704      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46705      */
46706     /**
46707      * @cfg {DataReader} reader
46708      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46709      * This is optional as there is built-in support for processing JSON.
46710      */
46711     /**
46712      * @cfg {DataReader} errorReader
46713      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46714      * This is completely optional as there is built-in support for processing JSON.
46715      */
46716     /**
46717      * @cfg {String} url
46718      * The URL to use for form actions if one isn't supplied in the action options.
46719      */
46720     /**
46721      * @cfg {Boolean} fileUpload
46722      * Set to true if this form is a file upload.
46723      */
46724      
46725     /**
46726      * @cfg {Object} baseParams
46727      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46728      */
46729      /**
46730      
46731     /**
46732      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46733      */
46734     timeout: 30,
46735
46736     // private
46737     activeAction : null,
46738
46739     /**
46740      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46741      * or setValues() data instead of when the form was first created.
46742      */
46743     trackResetOnLoad : false,
46744     
46745     
46746     /**
46747      * childForms - used for multi-tab forms
46748      * @type {Array}
46749      */
46750     childForms : false,
46751     
46752     /**
46753      * allItems - full list of fields.
46754      * @type {Array}
46755      */
46756     allItems : false,
46757     
46758     /**
46759      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46760      * element by passing it or its id or mask the form itself by passing in true.
46761      * @type Mixed
46762      */
46763     waitMsgTarget : false,
46764
46765     // private
46766     initEl : function(el){
46767         this.el = Roo.get(el);
46768         this.id = this.el.id || Roo.id();
46769         this.el.on('submit', this.onSubmit, this);
46770         this.el.addClass('x-form');
46771     },
46772
46773     // private
46774     onSubmit : function(e){
46775         e.stopEvent();
46776     },
46777
46778     /**
46779      * Returns true if client-side validation on the form is successful.
46780      * @return Boolean
46781      */
46782     isValid : function(){
46783         var valid = true;
46784         this.items.each(function(f){
46785            if(!f.validate()){
46786                valid = false;
46787            }
46788         });
46789         return valid;
46790     },
46791
46792     /**
46793      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46794      * @return Boolean
46795      */
46796     isDirty : function(){
46797         var dirty = false;
46798         this.items.each(function(f){
46799            if(f.isDirty()){
46800                dirty = true;
46801                return false;
46802            }
46803         });
46804         return dirty;
46805     },
46806     
46807     /**
46808      * Returns true if any fields in this form have changed since their original load. (New version)
46809      * @return Boolean
46810      */
46811     
46812     hasChanged : function()
46813     {
46814         var dirty = false;
46815         this.items.each(function(f){
46816            if(f.hasChanged()){
46817                dirty = true;
46818                return false;
46819            }
46820         });
46821         return dirty;
46822         
46823     },
46824     /**
46825      * Resets all hasChanged to 'false' -
46826      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46827      * So hasChanged storage is only to be used for this purpose
46828      * @return Boolean
46829      */
46830     resetHasChanged : function()
46831     {
46832         this.items.each(function(f){
46833            f.resetHasChanged();
46834         });
46835         
46836     },
46837     
46838     
46839     /**
46840      * Performs a predefined action (submit or load) or custom actions you define on this form.
46841      * @param {String} actionName The name of the action type
46842      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46843      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46844      * accept other config options):
46845      * <pre>
46846 Property          Type             Description
46847 ----------------  ---------------  ----------------------------------------------------------------------------------
46848 url               String           The url for the action (defaults to the form's url)
46849 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46850 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46851 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46852                                    validate the form on the client (defaults to false)
46853      * </pre>
46854      * @return {BasicForm} this
46855      */
46856     doAction : function(action, options){
46857         if(typeof action == 'string'){
46858             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46859         }
46860         if(this.fireEvent('beforeaction', this, action) !== false){
46861             this.beforeAction(action);
46862             action.run.defer(100, action);
46863         }
46864         return this;
46865     },
46866
46867     /**
46868      * Shortcut to do a submit action.
46869      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46870      * @return {BasicForm} this
46871      */
46872     submit : function(options){
46873         this.doAction('submit', options);
46874         return this;
46875     },
46876
46877     /**
46878      * Shortcut to do a load action.
46879      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46880      * @return {BasicForm} this
46881      */
46882     load : function(options){
46883         this.doAction('load', options);
46884         return this;
46885     },
46886
46887     /**
46888      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46889      * @param {Record} record The record to edit
46890      * @return {BasicForm} this
46891      */
46892     updateRecord : function(record){
46893         record.beginEdit();
46894         var fs = record.fields;
46895         fs.each(function(f){
46896             var field = this.findField(f.name);
46897             if(field){
46898                 record.set(f.name, field.getValue());
46899             }
46900         }, this);
46901         record.endEdit();
46902         return this;
46903     },
46904
46905     /**
46906      * Loads an Roo.data.Record into this form.
46907      * @param {Record} record The record to load
46908      * @return {BasicForm} this
46909      */
46910     loadRecord : function(record){
46911         this.setValues(record.data);
46912         return this;
46913     },
46914
46915     // private
46916     beforeAction : function(action){
46917         var o = action.options;
46918         
46919        
46920         if(this.waitMsgTarget === true){
46921             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46922         }else if(this.waitMsgTarget){
46923             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46924             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46925         }else {
46926             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46927         }
46928          
46929     },
46930
46931     // private
46932     afterAction : function(action, success){
46933         this.activeAction = null;
46934         var o = action.options;
46935         
46936         if(this.waitMsgTarget === true){
46937             this.el.unmask();
46938         }else if(this.waitMsgTarget){
46939             this.waitMsgTarget.unmask();
46940         }else{
46941             Roo.MessageBox.updateProgress(1);
46942             Roo.MessageBox.hide();
46943         }
46944          
46945         if(success){
46946             if(o.reset){
46947                 this.reset();
46948             }
46949             Roo.callback(o.success, o.scope, [this, action]);
46950             this.fireEvent('actioncomplete', this, action);
46951             
46952         }else{
46953             
46954             // failure condition..
46955             // we have a scenario where updates need confirming.
46956             // eg. if a locking scenario exists..
46957             // we look for { errors : { needs_confirm : true }} in the response.
46958             if (
46959                 (typeof(action.result) != 'undefined')  &&
46960                 (typeof(action.result.errors) != 'undefined')  &&
46961                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46962            ){
46963                 var _t = this;
46964                 Roo.MessageBox.confirm(
46965                     "Change requires confirmation",
46966                     action.result.errorMsg,
46967                     function(r) {
46968                         if (r != 'yes') {
46969                             return;
46970                         }
46971                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46972                     }
46973                     
46974                 );
46975                 
46976                 
46977                 
46978                 return;
46979             }
46980             
46981             Roo.callback(o.failure, o.scope, [this, action]);
46982             // show an error message if no failed handler is set..
46983             if (!this.hasListener('actionfailed')) {
46984                 Roo.MessageBox.alert("Error",
46985                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46986                         action.result.errorMsg :
46987                         "Saving Failed, please check your entries or try again"
46988                 );
46989             }
46990             
46991             this.fireEvent('actionfailed', this, action);
46992         }
46993         
46994     },
46995
46996     /**
46997      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46998      * @param {String} id The value to search for
46999      * @return Field
47000      */
47001     findField : function(id){
47002         var field = this.items.get(id);
47003         if(!field){
47004             this.items.each(function(f){
47005                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47006                     field = f;
47007                     return false;
47008                 }
47009             });
47010         }
47011         return field || null;
47012     },
47013
47014     /**
47015      * Add a secondary form to this one, 
47016      * Used to provide tabbed forms. One form is primary, with hidden values 
47017      * which mirror the elements from the other forms.
47018      * 
47019      * @param {Roo.form.Form} form to add.
47020      * 
47021      */
47022     addForm : function(form)
47023     {
47024        
47025         if (this.childForms.indexOf(form) > -1) {
47026             // already added..
47027             return;
47028         }
47029         this.childForms.push(form);
47030         var n = '';
47031         Roo.each(form.allItems, function (fe) {
47032             
47033             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47034             if (this.findField(n)) { // already added..
47035                 return;
47036             }
47037             var add = new Roo.form.Hidden({
47038                 name : n
47039             });
47040             add.render(this.el);
47041             
47042             this.add( add );
47043         }, this);
47044         
47045     },
47046     /**
47047      * Mark fields in this form invalid in bulk.
47048      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47049      * @return {BasicForm} this
47050      */
47051     markInvalid : function(errors){
47052         if(errors instanceof Array){
47053             for(var i = 0, len = errors.length; i < len; i++){
47054                 var fieldError = errors[i];
47055                 var f = this.findField(fieldError.id);
47056                 if(f){
47057                     f.markInvalid(fieldError.msg);
47058                 }
47059             }
47060         }else{
47061             var field, id;
47062             for(id in errors){
47063                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47064                     field.markInvalid(errors[id]);
47065                 }
47066             }
47067         }
47068         Roo.each(this.childForms || [], function (f) {
47069             f.markInvalid(errors);
47070         });
47071         
47072         return this;
47073     },
47074
47075     /**
47076      * Set values for fields in this form in bulk.
47077      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47078      * @return {BasicForm} this
47079      */
47080     setValues : function(values){
47081         if(values instanceof Array){ // array of objects
47082             for(var i = 0, len = values.length; i < len; i++){
47083                 var v = values[i];
47084                 var f = this.findField(v.id);
47085                 if(f){
47086                     f.setValue(v.value);
47087                     if(this.trackResetOnLoad){
47088                         f.originalValue = f.getValue();
47089                     }
47090                 }
47091             }
47092         }else{ // object hash
47093             var field, id;
47094             for(id in values){
47095                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47096                     
47097                     if (field.setFromData && 
47098                         field.valueField && 
47099                         field.displayField &&
47100                         // combos' with local stores can 
47101                         // be queried via setValue()
47102                         // to set their value..
47103                         (field.store && !field.store.isLocal)
47104                         ) {
47105                         // it's a combo
47106                         var sd = { };
47107                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47108                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47109                         field.setFromData(sd);
47110                         
47111                     } else {
47112                         field.setValue(values[id]);
47113                     }
47114                     
47115                     
47116                     if(this.trackResetOnLoad){
47117                         field.originalValue = field.getValue();
47118                     }
47119                 }
47120             }
47121         }
47122         this.resetHasChanged();
47123         
47124         
47125         Roo.each(this.childForms || [], function (f) {
47126             f.setValues(values);
47127             f.resetHasChanged();
47128         });
47129                 
47130         return this;
47131     },
47132
47133     /**
47134      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47135      * they are returned as an array.
47136      * @param {Boolean} asString
47137      * @return {Object}
47138      */
47139     getValues : function(asString){
47140         if (this.childForms) {
47141             // copy values from the child forms
47142             Roo.each(this.childForms, function (f) {
47143                 this.setValues(f.getValues());
47144             }, this);
47145         }
47146         
47147         
47148         
47149         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47150         if(asString === true){
47151             return fs;
47152         }
47153         return Roo.urlDecode(fs);
47154     },
47155     
47156     /**
47157      * Returns the fields in this form as an object with key/value pairs. 
47158      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47159      * @return {Object}
47160      */
47161     getFieldValues : function(with_hidden)
47162     {
47163         if (this.childForms) {
47164             // copy values from the child forms
47165             // should this call getFieldValues - probably not as we do not currently copy
47166             // hidden fields when we generate..
47167             Roo.each(this.childForms, function (f) {
47168                 this.setValues(f.getValues());
47169             }, this);
47170         }
47171         
47172         var ret = {};
47173         this.items.each(function(f){
47174             if (!f.getName()) {
47175                 return;
47176             }
47177             var v = f.getValue();
47178             if (f.inputType =='radio') {
47179                 if (typeof(ret[f.getName()]) == 'undefined') {
47180                     ret[f.getName()] = ''; // empty..
47181                 }
47182                 
47183                 if (!f.el.dom.checked) {
47184                     return;
47185                     
47186                 }
47187                 v = f.el.dom.value;
47188                 
47189             }
47190             
47191             // not sure if this supported any more..
47192             if ((typeof(v) == 'object') && f.getRawValue) {
47193                 v = f.getRawValue() ; // dates..
47194             }
47195             // combo boxes where name != hiddenName...
47196             if (f.name != f.getName()) {
47197                 ret[f.name] = f.getRawValue();
47198             }
47199             ret[f.getName()] = v;
47200         });
47201         
47202         return ret;
47203     },
47204
47205     /**
47206      * Clears all invalid messages in this form.
47207      * @return {BasicForm} this
47208      */
47209     clearInvalid : function(){
47210         this.items.each(function(f){
47211            f.clearInvalid();
47212         });
47213         
47214         Roo.each(this.childForms || [], function (f) {
47215             f.clearInvalid();
47216         });
47217         
47218         
47219         return this;
47220     },
47221
47222     /**
47223      * Resets this form.
47224      * @return {BasicForm} this
47225      */
47226     reset : function(){
47227         this.items.each(function(f){
47228             f.reset();
47229         });
47230         
47231         Roo.each(this.childForms || [], function (f) {
47232             f.reset();
47233         });
47234         this.resetHasChanged();
47235         
47236         return this;
47237     },
47238
47239     /**
47240      * Add Roo.form components to this form.
47241      * @param {Field} field1
47242      * @param {Field} field2 (optional)
47243      * @param {Field} etc (optional)
47244      * @return {BasicForm} this
47245      */
47246     add : function(){
47247         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47248         return this;
47249     },
47250
47251
47252     /**
47253      * Removes a field from the items collection (does NOT remove its markup).
47254      * @param {Field} field
47255      * @return {BasicForm} this
47256      */
47257     remove : function(field){
47258         this.items.remove(field);
47259         return this;
47260     },
47261
47262     /**
47263      * Looks at the fields in this form, checks them for an id attribute,
47264      * and calls applyTo on the existing dom element with that id.
47265      * @return {BasicForm} this
47266      */
47267     render : function(){
47268         this.items.each(function(f){
47269             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47270                 f.applyTo(f.id);
47271             }
47272         });
47273         return this;
47274     },
47275
47276     /**
47277      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47278      * @param {Object} values
47279      * @return {BasicForm} this
47280      */
47281     applyToFields : function(o){
47282         this.items.each(function(f){
47283            Roo.apply(f, o);
47284         });
47285         return this;
47286     },
47287
47288     /**
47289      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47290      * @param {Object} values
47291      * @return {BasicForm} this
47292      */
47293     applyIfToFields : function(o){
47294         this.items.each(function(f){
47295            Roo.applyIf(f, o);
47296         });
47297         return this;
47298     }
47299 });
47300
47301 // back compat
47302 Roo.BasicForm = Roo.form.BasicForm;/*
47303  * Based on:
47304  * Ext JS Library 1.1.1
47305  * Copyright(c) 2006-2007, Ext JS, LLC.
47306  *
47307  * Originally Released Under LGPL - original licence link has changed is not relivant.
47308  *
47309  * Fork - LGPL
47310  * <script type="text/javascript">
47311  */
47312
47313 /**
47314  * @class Roo.form.Form
47315  * @extends Roo.form.BasicForm
47316  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47317  * @constructor
47318  * @param {Object} config Configuration options
47319  */
47320 Roo.form.Form = function(config){
47321     var xitems =  [];
47322     if (config.items) {
47323         xitems = config.items;
47324         delete config.items;
47325     }
47326    
47327     
47328     Roo.form.Form.superclass.constructor.call(this, null, config);
47329     this.url = this.url || this.action;
47330     if(!this.root){
47331         this.root = new Roo.form.Layout(Roo.applyIf({
47332             id: Roo.id()
47333         }, config));
47334     }
47335     this.active = this.root;
47336     /**
47337      * Array of all the buttons that have been added to this form via {@link addButton}
47338      * @type Array
47339      */
47340     this.buttons = [];
47341     this.allItems = [];
47342     this.addEvents({
47343         /**
47344          * @event clientvalidation
47345          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47346          * @param {Form} this
47347          * @param {Boolean} valid true if the form has passed client-side validation
47348          */
47349         clientvalidation: true,
47350         /**
47351          * @event rendered
47352          * Fires when the form is rendered
47353          * @param {Roo.form.Form} form
47354          */
47355         rendered : true
47356     });
47357     
47358     if (this.progressUrl) {
47359             // push a hidden field onto the list of fields..
47360             this.addxtype( {
47361                     xns: Roo.form, 
47362                     xtype : 'Hidden', 
47363                     name : 'UPLOAD_IDENTIFIER' 
47364             });
47365         }
47366         
47367     
47368     Roo.each(xitems, this.addxtype, this);
47369     
47370     
47371     
47372 };
47373
47374 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47375     /**
47376      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47377      */
47378     /**
47379      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47380      */
47381     /**
47382      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47383      */
47384     buttonAlign:'center',
47385
47386     /**
47387      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47388      */
47389     minButtonWidth:75,
47390
47391     /**
47392      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47393      * This property cascades to child containers if not set.
47394      */
47395     labelAlign:'left',
47396
47397     /**
47398      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47399      * fires a looping event with that state. This is required to bind buttons to the valid
47400      * state using the config value formBind:true on the button.
47401      */
47402     monitorValid : false,
47403
47404     /**
47405      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47406      */
47407     monitorPoll : 200,
47408     
47409     /**
47410      * @cfg {String} progressUrl - Url to return progress data 
47411      */
47412     
47413     progressUrl : false,
47414   
47415     /**
47416      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47417      * fields are added and the column is closed. If no fields are passed the column remains open
47418      * until end() is called.
47419      * @param {Object} config The config to pass to the column
47420      * @param {Field} field1 (optional)
47421      * @param {Field} field2 (optional)
47422      * @param {Field} etc (optional)
47423      * @return Column The column container object
47424      */
47425     column : function(c){
47426         var col = new Roo.form.Column(c);
47427         this.start(col);
47428         if(arguments.length > 1){ // duplicate code required because of Opera
47429             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47430             this.end();
47431         }
47432         return col;
47433     },
47434
47435     /**
47436      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47437      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47438      * until end() is called.
47439      * @param {Object} config The config to pass to the fieldset
47440      * @param {Field} field1 (optional)
47441      * @param {Field} field2 (optional)
47442      * @param {Field} etc (optional)
47443      * @return FieldSet The fieldset container object
47444      */
47445     fieldset : function(c){
47446         var fs = new Roo.form.FieldSet(c);
47447         this.start(fs);
47448         if(arguments.length > 1){ // duplicate code required because of Opera
47449             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47450             this.end();
47451         }
47452         return fs;
47453     },
47454
47455     /**
47456      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47457      * fields are added and the container is closed. If no fields are passed the container remains open
47458      * until end() is called.
47459      * @param {Object} config The config to pass to the Layout
47460      * @param {Field} field1 (optional)
47461      * @param {Field} field2 (optional)
47462      * @param {Field} etc (optional)
47463      * @return Layout The container object
47464      */
47465     container : function(c){
47466         var l = new Roo.form.Layout(c);
47467         this.start(l);
47468         if(arguments.length > 1){ // duplicate code required because of Opera
47469             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47470             this.end();
47471         }
47472         return l;
47473     },
47474
47475     /**
47476      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47477      * @param {Object} container A Roo.form.Layout or subclass of Layout
47478      * @return {Form} this
47479      */
47480     start : function(c){
47481         // cascade label info
47482         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47483         this.active.stack.push(c);
47484         c.ownerCt = this.active;
47485         this.active = c;
47486         return this;
47487     },
47488
47489     /**
47490      * Closes the current open container
47491      * @return {Form} this
47492      */
47493     end : function(){
47494         if(this.active == this.root){
47495             return this;
47496         }
47497         this.active = this.active.ownerCt;
47498         return this;
47499     },
47500
47501     /**
47502      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47503      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47504      * as the label of the field.
47505      * @param {Field} field1
47506      * @param {Field} field2 (optional)
47507      * @param {Field} etc. (optional)
47508      * @return {Form} this
47509      */
47510     add : function(){
47511         this.active.stack.push.apply(this.active.stack, arguments);
47512         this.allItems.push.apply(this.allItems,arguments);
47513         var r = [];
47514         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47515             if(a[i].isFormField){
47516                 r.push(a[i]);
47517             }
47518         }
47519         if(r.length > 0){
47520             Roo.form.Form.superclass.add.apply(this, r);
47521         }
47522         return this;
47523     },
47524     
47525
47526     
47527     
47528     
47529      /**
47530      * Find any element that has been added to a form, using it's ID or name
47531      * This can include framesets, columns etc. along with regular fields..
47532      * @param {String} id - id or name to find.
47533      
47534      * @return {Element} e - or false if nothing found.
47535      */
47536     findbyId : function(id)
47537     {
47538         var ret = false;
47539         if (!id) {
47540             return ret;
47541         }
47542         Roo.each(this.allItems, function(f){
47543             if (f.id == id || f.name == id ){
47544                 ret = f;
47545                 return false;
47546             }
47547         });
47548         return ret;
47549     },
47550
47551     
47552     
47553     /**
47554      * Render this form into the passed container. This should only be called once!
47555      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47556      * @return {Form} this
47557      */
47558     render : function(ct)
47559     {
47560         
47561         
47562         
47563         ct = Roo.get(ct);
47564         var o = this.autoCreate || {
47565             tag: 'form',
47566             method : this.method || 'POST',
47567             id : this.id || Roo.id()
47568         };
47569         this.initEl(ct.createChild(o));
47570
47571         this.root.render(this.el);
47572         
47573        
47574              
47575         this.items.each(function(f){
47576             f.render('x-form-el-'+f.id);
47577         });
47578
47579         if(this.buttons.length > 0){
47580             // tables are required to maintain order and for correct IE layout
47581             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47582                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47583                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47584             }}, null, true);
47585             var tr = tb.getElementsByTagName('tr')[0];
47586             for(var i = 0, len = this.buttons.length; i < len; i++) {
47587                 var b = this.buttons[i];
47588                 var td = document.createElement('td');
47589                 td.className = 'x-form-btn-td';
47590                 b.render(tr.appendChild(td));
47591             }
47592         }
47593         if(this.monitorValid){ // initialize after render
47594             this.startMonitoring();
47595         }
47596         this.fireEvent('rendered', this);
47597         return this;
47598     },
47599
47600     /**
47601      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47602      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47603      * object or a valid Roo.DomHelper element config
47604      * @param {Function} handler The function called when the button is clicked
47605      * @param {Object} scope (optional) The scope of the handler function
47606      * @return {Roo.Button}
47607      */
47608     addButton : function(config, handler, scope){
47609         var bc = {
47610             handler: handler,
47611             scope: scope,
47612             minWidth: this.minButtonWidth,
47613             hideParent:true
47614         };
47615         if(typeof config == "string"){
47616             bc.text = config;
47617         }else{
47618             Roo.apply(bc, config);
47619         }
47620         var btn = new Roo.Button(null, bc);
47621         this.buttons.push(btn);
47622         return btn;
47623     },
47624
47625      /**
47626      * Adds a series of form elements (using the xtype property as the factory method.
47627      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47628      * @param {Object} config 
47629      */
47630     
47631     addxtype : function()
47632     {
47633         var ar = Array.prototype.slice.call(arguments, 0);
47634         var ret = false;
47635         for(var i = 0; i < ar.length; i++) {
47636             if (!ar[i]) {
47637                 continue; // skip -- if this happends something invalid got sent, we 
47638                 // should ignore it, as basically that interface element will not show up
47639                 // and that should be pretty obvious!!
47640             }
47641             
47642             if (Roo.form[ar[i].xtype]) {
47643                 ar[i].form = this;
47644                 var fe = Roo.factory(ar[i], Roo.form);
47645                 if (!ret) {
47646                     ret = fe;
47647                 }
47648                 fe.form = this;
47649                 if (fe.store) {
47650                     fe.store.form = this;
47651                 }
47652                 if (fe.isLayout) {  
47653                          
47654                     this.start(fe);
47655                     this.allItems.push(fe);
47656                     if (fe.items && fe.addxtype) {
47657                         fe.addxtype.apply(fe, fe.items);
47658                         delete fe.items;
47659                     }
47660                      this.end();
47661                     continue;
47662                 }
47663                 
47664                 
47665                  
47666                 this.add(fe);
47667               //  console.log('adding ' + ar[i].xtype);
47668             }
47669             if (ar[i].xtype == 'Button') {  
47670                 //console.log('adding button');
47671                 //console.log(ar[i]);
47672                 this.addButton(ar[i]);
47673                 this.allItems.push(fe);
47674                 continue;
47675             }
47676             
47677             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47678                 alert('end is not supported on xtype any more, use items');
47679             //    this.end();
47680             //    //console.log('adding end');
47681             }
47682             
47683         }
47684         return ret;
47685     },
47686     
47687     /**
47688      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47689      * option "monitorValid"
47690      */
47691     startMonitoring : function(){
47692         if(!this.bound){
47693             this.bound = true;
47694             Roo.TaskMgr.start({
47695                 run : this.bindHandler,
47696                 interval : this.monitorPoll || 200,
47697                 scope: this
47698             });
47699         }
47700     },
47701
47702     /**
47703      * Stops monitoring of the valid state of this form
47704      */
47705     stopMonitoring : function(){
47706         this.bound = false;
47707     },
47708
47709     // private
47710     bindHandler : function(){
47711         if(!this.bound){
47712             return false; // stops binding
47713         }
47714         var valid = true;
47715         this.items.each(function(f){
47716             if(!f.isValid(true)){
47717                 valid = false;
47718                 return false;
47719             }
47720         });
47721         for(var i = 0, len = this.buttons.length; i < len; i++){
47722             var btn = this.buttons[i];
47723             if(btn.formBind === true && btn.disabled === valid){
47724                 btn.setDisabled(!valid);
47725             }
47726         }
47727         this.fireEvent('clientvalidation', this, valid);
47728     }
47729     
47730     
47731     
47732     
47733     
47734     
47735     
47736     
47737 });
47738
47739
47740 // back compat
47741 Roo.Form = Roo.form.Form;
47742 /*
47743  * Based on:
47744  * Ext JS Library 1.1.1
47745  * Copyright(c) 2006-2007, Ext JS, LLC.
47746  *
47747  * Originally Released Under LGPL - original licence link has changed is not relivant.
47748  *
47749  * Fork - LGPL
47750  * <script type="text/javascript">
47751  */
47752
47753 // as we use this in bootstrap.
47754 Roo.namespace('Roo.form');
47755  /**
47756  * @class Roo.form.Action
47757  * Internal Class used to handle form actions
47758  * @constructor
47759  * @param {Roo.form.BasicForm} el The form element or its id
47760  * @param {Object} config Configuration options
47761  */
47762
47763  
47764  
47765 // define the action interface
47766 Roo.form.Action = function(form, options){
47767     this.form = form;
47768     this.options = options || {};
47769 };
47770 /**
47771  * Client Validation Failed
47772  * @const 
47773  */
47774 Roo.form.Action.CLIENT_INVALID = 'client';
47775 /**
47776  * Server Validation Failed
47777  * @const 
47778  */
47779 Roo.form.Action.SERVER_INVALID = 'server';
47780  /**
47781  * Connect to Server Failed
47782  * @const 
47783  */
47784 Roo.form.Action.CONNECT_FAILURE = 'connect';
47785 /**
47786  * Reading Data from Server Failed
47787  * @const 
47788  */
47789 Roo.form.Action.LOAD_FAILURE = 'load';
47790
47791 Roo.form.Action.prototype = {
47792     type : 'default',
47793     failureType : undefined,
47794     response : undefined,
47795     result : undefined,
47796
47797     // interface method
47798     run : function(options){
47799
47800     },
47801
47802     // interface method
47803     success : function(response){
47804
47805     },
47806
47807     // interface method
47808     handleResponse : function(response){
47809
47810     },
47811
47812     // default connection failure
47813     failure : function(response){
47814         
47815         this.response = response;
47816         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47817         this.form.afterAction(this, false);
47818     },
47819
47820     processResponse : function(response){
47821         this.response = response;
47822         if(!response.responseText){
47823             return true;
47824         }
47825         this.result = this.handleResponse(response);
47826         return this.result;
47827     },
47828
47829     // utility functions used internally
47830     getUrl : function(appendParams){
47831         var url = this.options.url || this.form.url || this.form.el.dom.action;
47832         if(appendParams){
47833             var p = this.getParams();
47834             if(p){
47835                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47836             }
47837         }
47838         return url;
47839     },
47840
47841     getMethod : function(){
47842         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47843     },
47844
47845     getParams : function(){
47846         var bp = this.form.baseParams;
47847         var p = this.options.params;
47848         if(p){
47849             if(typeof p == "object"){
47850                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47851             }else if(typeof p == 'string' && bp){
47852                 p += '&' + Roo.urlEncode(bp);
47853             }
47854         }else if(bp){
47855             p = Roo.urlEncode(bp);
47856         }
47857         return p;
47858     },
47859
47860     createCallback : function(){
47861         return {
47862             success: this.success,
47863             failure: this.failure,
47864             scope: this,
47865             timeout: (this.form.timeout*1000),
47866             upload: this.form.fileUpload ? this.success : undefined
47867         };
47868     }
47869 };
47870
47871 Roo.form.Action.Submit = function(form, options){
47872     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47873 };
47874
47875 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47876     type : 'submit',
47877
47878     haveProgress : false,
47879     uploadComplete : false,
47880     
47881     // uploadProgress indicator.
47882     uploadProgress : function()
47883     {
47884         if (!this.form.progressUrl) {
47885             return;
47886         }
47887         
47888         if (!this.haveProgress) {
47889             Roo.MessageBox.progress("Uploading", "Uploading");
47890         }
47891         if (this.uploadComplete) {
47892            Roo.MessageBox.hide();
47893            return;
47894         }
47895         
47896         this.haveProgress = true;
47897    
47898         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47899         
47900         var c = new Roo.data.Connection();
47901         c.request({
47902             url : this.form.progressUrl,
47903             params: {
47904                 id : uid
47905             },
47906             method: 'GET',
47907             success : function(req){
47908                //console.log(data);
47909                 var rdata = false;
47910                 var edata;
47911                 try  {
47912                    rdata = Roo.decode(req.responseText)
47913                 } catch (e) {
47914                     Roo.log("Invalid data from server..");
47915                     Roo.log(edata);
47916                     return;
47917                 }
47918                 if (!rdata || !rdata.success) {
47919                     Roo.log(rdata);
47920                     Roo.MessageBox.alert(Roo.encode(rdata));
47921                     return;
47922                 }
47923                 var data = rdata.data;
47924                 
47925                 if (this.uploadComplete) {
47926                    Roo.MessageBox.hide();
47927                    return;
47928                 }
47929                    
47930                 if (data){
47931                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47932                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47933                     );
47934                 }
47935                 this.uploadProgress.defer(2000,this);
47936             },
47937        
47938             failure: function(data) {
47939                 Roo.log('progress url failed ');
47940                 Roo.log(data);
47941             },
47942             scope : this
47943         });
47944            
47945     },
47946     
47947     
47948     run : function()
47949     {
47950         // run get Values on the form, so it syncs any secondary forms.
47951         this.form.getValues();
47952         
47953         var o = this.options;
47954         var method = this.getMethod();
47955         var isPost = method == 'POST';
47956         if(o.clientValidation === false || this.form.isValid()){
47957             
47958             if (this.form.progressUrl) {
47959                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47960                     (new Date() * 1) + '' + Math.random());
47961                     
47962             } 
47963             
47964             
47965             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47966                 form:this.form.el.dom,
47967                 url:this.getUrl(!isPost),
47968                 method: method,
47969                 params:isPost ? this.getParams() : null,
47970                 isUpload: this.form.fileUpload
47971             }));
47972             
47973             this.uploadProgress();
47974
47975         }else if (o.clientValidation !== false){ // client validation failed
47976             this.failureType = Roo.form.Action.CLIENT_INVALID;
47977             this.form.afterAction(this, false);
47978         }
47979     },
47980
47981     success : function(response)
47982     {
47983         this.uploadComplete= true;
47984         if (this.haveProgress) {
47985             Roo.MessageBox.hide();
47986         }
47987         
47988         
47989         var result = this.processResponse(response);
47990         if(result === true || result.success){
47991             this.form.afterAction(this, true);
47992             return;
47993         }
47994         if(result.errors){
47995             this.form.markInvalid(result.errors);
47996             this.failureType = Roo.form.Action.SERVER_INVALID;
47997         }
47998         this.form.afterAction(this, false);
47999     },
48000     failure : function(response)
48001     {
48002         this.uploadComplete= true;
48003         if (this.haveProgress) {
48004             Roo.MessageBox.hide();
48005         }
48006         
48007         this.response = response;
48008         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48009         this.form.afterAction(this, false);
48010     },
48011     
48012     handleResponse : function(response){
48013         if(this.form.errorReader){
48014             var rs = this.form.errorReader.read(response);
48015             var errors = [];
48016             if(rs.records){
48017                 for(var i = 0, len = rs.records.length; i < len; i++) {
48018                     var r = rs.records[i];
48019                     errors[i] = r.data;
48020                 }
48021             }
48022             if(errors.length < 1){
48023                 errors = null;
48024             }
48025             return {
48026                 success : rs.success,
48027                 errors : errors
48028             };
48029         }
48030         var ret = false;
48031         try {
48032             ret = Roo.decode(response.responseText);
48033         } catch (e) {
48034             ret = {
48035                 success: false,
48036                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48037                 errors : []
48038             };
48039         }
48040         return ret;
48041         
48042     }
48043 });
48044
48045
48046 Roo.form.Action.Load = function(form, options){
48047     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48048     this.reader = this.form.reader;
48049 };
48050
48051 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48052     type : 'load',
48053
48054     run : function(){
48055         
48056         Roo.Ajax.request(Roo.apply(
48057                 this.createCallback(), {
48058                     method:this.getMethod(),
48059                     url:this.getUrl(false),
48060                     params:this.getParams()
48061         }));
48062     },
48063
48064     success : function(response){
48065         
48066         var result = this.processResponse(response);
48067         if(result === true || !result.success || !result.data){
48068             this.failureType = Roo.form.Action.LOAD_FAILURE;
48069             this.form.afterAction(this, false);
48070             return;
48071         }
48072         this.form.clearInvalid();
48073         this.form.setValues(result.data);
48074         this.form.afterAction(this, true);
48075     },
48076
48077     handleResponse : function(response){
48078         if(this.form.reader){
48079             var rs = this.form.reader.read(response);
48080             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48081             return {
48082                 success : rs.success,
48083                 data : data
48084             };
48085         }
48086         return Roo.decode(response.responseText);
48087     }
48088 });
48089
48090 Roo.form.Action.ACTION_TYPES = {
48091     'load' : Roo.form.Action.Load,
48092     'submit' : Roo.form.Action.Submit
48093 };/*
48094  * Based on:
48095  * Ext JS Library 1.1.1
48096  * Copyright(c) 2006-2007, Ext JS, LLC.
48097  *
48098  * Originally Released Under LGPL - original licence link has changed is not relivant.
48099  *
48100  * Fork - LGPL
48101  * <script type="text/javascript">
48102  */
48103  
48104 /**
48105  * @class Roo.form.Layout
48106  * @extends Roo.Component
48107  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48108  * @constructor
48109  * @param {Object} config Configuration options
48110  */
48111 Roo.form.Layout = function(config){
48112     var xitems = [];
48113     if (config.items) {
48114         xitems = config.items;
48115         delete config.items;
48116     }
48117     Roo.form.Layout.superclass.constructor.call(this, config);
48118     this.stack = [];
48119     Roo.each(xitems, this.addxtype, this);
48120      
48121 };
48122
48123 Roo.extend(Roo.form.Layout, Roo.Component, {
48124     /**
48125      * @cfg {String/Object} autoCreate
48126      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48127      */
48128     /**
48129      * @cfg {String/Object/Function} style
48130      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48131      * a function which returns such a specification.
48132      */
48133     /**
48134      * @cfg {String} labelAlign
48135      * Valid values are "left," "top" and "right" (defaults to "left")
48136      */
48137     /**
48138      * @cfg {Number} labelWidth
48139      * Fixed width in pixels of all field labels (defaults to undefined)
48140      */
48141     /**
48142      * @cfg {Boolean} clear
48143      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48144      */
48145     clear : true,
48146     /**
48147      * @cfg {String} labelSeparator
48148      * The separator to use after field labels (defaults to ':')
48149      */
48150     labelSeparator : ':',
48151     /**
48152      * @cfg {Boolean} hideLabels
48153      * True to suppress the display of field labels in this layout (defaults to false)
48154      */
48155     hideLabels : false,
48156
48157     // private
48158     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48159     
48160     isLayout : true,
48161     
48162     // private
48163     onRender : function(ct, position){
48164         if(this.el){ // from markup
48165             this.el = Roo.get(this.el);
48166         }else {  // generate
48167             var cfg = this.getAutoCreate();
48168             this.el = ct.createChild(cfg, position);
48169         }
48170         if(this.style){
48171             this.el.applyStyles(this.style);
48172         }
48173         if(this.labelAlign){
48174             this.el.addClass('x-form-label-'+this.labelAlign);
48175         }
48176         if(this.hideLabels){
48177             this.labelStyle = "display:none";
48178             this.elementStyle = "padding-left:0;";
48179         }else{
48180             if(typeof this.labelWidth == 'number'){
48181                 this.labelStyle = "width:"+this.labelWidth+"px;";
48182                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48183             }
48184             if(this.labelAlign == 'top'){
48185                 this.labelStyle = "width:auto;";
48186                 this.elementStyle = "padding-left:0;";
48187             }
48188         }
48189         var stack = this.stack;
48190         var slen = stack.length;
48191         if(slen > 0){
48192             if(!this.fieldTpl){
48193                 var t = new Roo.Template(
48194                     '<div class="x-form-item {5}">',
48195                         '<label for="{0}" style="{2}">{1}{4}</label>',
48196                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48197                         '</div>',
48198                     '</div><div class="x-form-clear-left"></div>'
48199                 );
48200                 t.disableFormats = true;
48201                 t.compile();
48202                 Roo.form.Layout.prototype.fieldTpl = t;
48203             }
48204             for(var i = 0; i < slen; i++) {
48205                 if(stack[i].isFormField){
48206                     this.renderField(stack[i]);
48207                 }else{
48208                     this.renderComponent(stack[i]);
48209                 }
48210             }
48211         }
48212         if(this.clear){
48213             this.el.createChild({cls:'x-form-clear'});
48214         }
48215     },
48216
48217     // private
48218     renderField : function(f){
48219         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48220                f.id, //0
48221                f.fieldLabel, //1
48222                f.labelStyle||this.labelStyle||'', //2
48223                this.elementStyle||'', //3
48224                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48225                f.itemCls||this.itemCls||''  //5
48226        ], true).getPrevSibling());
48227     },
48228
48229     // private
48230     renderComponent : function(c){
48231         c.render(c.isLayout ? this.el : this.el.createChild());    
48232     },
48233     /**
48234      * Adds a object form elements (using the xtype property as the factory method.)
48235      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48236      * @param {Object} config 
48237      */
48238     addxtype : function(o)
48239     {
48240         // create the lement.
48241         o.form = this.form;
48242         var fe = Roo.factory(o, Roo.form);
48243         this.form.allItems.push(fe);
48244         this.stack.push(fe);
48245         
48246         if (fe.isFormField) {
48247             this.form.items.add(fe);
48248         }
48249          
48250         return fe;
48251     }
48252 });
48253
48254 /**
48255  * @class Roo.form.Column
48256  * @extends Roo.form.Layout
48257  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48258  * @constructor
48259  * @param {Object} config Configuration options
48260  */
48261 Roo.form.Column = function(config){
48262     Roo.form.Column.superclass.constructor.call(this, config);
48263 };
48264
48265 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48266     /**
48267      * @cfg {Number/String} width
48268      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48269      */
48270     /**
48271      * @cfg {String/Object} autoCreate
48272      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48273      */
48274
48275     // private
48276     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48277
48278     // private
48279     onRender : function(ct, position){
48280         Roo.form.Column.superclass.onRender.call(this, ct, position);
48281         if(this.width){
48282             this.el.setWidth(this.width);
48283         }
48284     }
48285 });
48286
48287
48288 /**
48289  * @class Roo.form.Row
48290  * @extends Roo.form.Layout
48291  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48292  * @constructor
48293  * @param {Object} config Configuration options
48294  */
48295
48296  
48297 Roo.form.Row = function(config){
48298     Roo.form.Row.superclass.constructor.call(this, config);
48299 };
48300  
48301 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48302       /**
48303      * @cfg {Number/String} width
48304      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48305      */
48306     /**
48307      * @cfg {Number/String} height
48308      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48309      */
48310     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48311     
48312     padWidth : 20,
48313     // private
48314     onRender : function(ct, position){
48315         //console.log('row render');
48316         if(!this.rowTpl){
48317             var t = new Roo.Template(
48318                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48319                     '<label for="{0}" style="{2}">{1}{4}</label>',
48320                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48321                     '</div>',
48322                 '</div>'
48323             );
48324             t.disableFormats = true;
48325             t.compile();
48326             Roo.form.Layout.prototype.rowTpl = t;
48327         }
48328         this.fieldTpl = this.rowTpl;
48329         
48330         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48331         var labelWidth = 100;
48332         
48333         if ((this.labelAlign != 'top')) {
48334             if (typeof this.labelWidth == 'number') {
48335                 labelWidth = this.labelWidth
48336             }
48337             this.padWidth =  20 + labelWidth;
48338             
48339         }
48340         
48341         Roo.form.Column.superclass.onRender.call(this, ct, position);
48342         if(this.width){
48343             this.el.setWidth(this.width);
48344         }
48345         if(this.height){
48346             this.el.setHeight(this.height);
48347         }
48348     },
48349     
48350     // private
48351     renderField : function(f){
48352         f.fieldEl = this.fieldTpl.append(this.el, [
48353                f.id, f.fieldLabel,
48354                f.labelStyle||this.labelStyle||'',
48355                this.elementStyle||'',
48356                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48357                f.itemCls||this.itemCls||'',
48358                f.width ? f.width + this.padWidth : 160 + this.padWidth
48359        ],true);
48360     }
48361 });
48362  
48363
48364 /**
48365  * @class Roo.form.FieldSet
48366  * @extends Roo.form.Layout
48367  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48368  * @constructor
48369  * @param {Object} config Configuration options
48370  */
48371 Roo.form.FieldSet = function(config){
48372     Roo.form.FieldSet.superclass.constructor.call(this, config);
48373 };
48374
48375 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48376     /**
48377      * @cfg {String} legend
48378      * The text to display as the legend for the FieldSet (defaults to '')
48379      */
48380     /**
48381      * @cfg {String/Object} autoCreate
48382      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48383      */
48384
48385     // private
48386     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48387
48388     // private
48389     onRender : function(ct, position){
48390         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48391         if(this.legend){
48392             this.setLegend(this.legend);
48393         }
48394     },
48395
48396     // private
48397     setLegend : function(text){
48398         if(this.rendered){
48399             this.el.child('legend').update(text);
48400         }
48401     }
48402 });/*
48403  * Based on:
48404  * Ext JS Library 1.1.1
48405  * Copyright(c) 2006-2007, Ext JS, LLC.
48406  *
48407  * Originally Released Under LGPL - original licence link has changed is not relivant.
48408  *
48409  * Fork - LGPL
48410  * <script type="text/javascript">
48411  */
48412 /**
48413  * @class Roo.form.VTypes
48414  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48415  * @singleton
48416  */
48417 Roo.form.VTypes = function(){
48418     // closure these in so they are only created once.
48419     var alpha = /^[a-zA-Z_]+$/;
48420     var alphanum = /^[a-zA-Z0-9_]+$/;
48421     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48422     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48423
48424     // All these messages and functions are configurable
48425     return {
48426         /**
48427          * The function used to validate email addresses
48428          * @param {String} value The email address
48429          */
48430         'email' : function(v){
48431             return email.test(v);
48432         },
48433         /**
48434          * The error text to display when the email validation function returns false
48435          * @type String
48436          */
48437         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48438         /**
48439          * The keystroke filter mask to be applied on email input
48440          * @type RegExp
48441          */
48442         'emailMask' : /[a-z0-9_\.\-@]/i,
48443
48444         /**
48445          * The function used to validate URLs
48446          * @param {String} value The URL
48447          */
48448         'url' : function(v){
48449             return url.test(v);
48450         },
48451         /**
48452          * The error text to display when the url validation function returns false
48453          * @type String
48454          */
48455         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48456         
48457         /**
48458          * The function used to validate alpha values
48459          * @param {String} value The value
48460          */
48461         'alpha' : function(v){
48462             return alpha.test(v);
48463         },
48464         /**
48465          * The error text to display when the alpha validation function returns false
48466          * @type String
48467          */
48468         'alphaText' : 'This field should only contain letters and _',
48469         /**
48470          * The keystroke filter mask to be applied on alpha input
48471          * @type RegExp
48472          */
48473         'alphaMask' : /[a-z_]/i,
48474
48475         /**
48476          * The function used to validate alphanumeric values
48477          * @param {String} value The value
48478          */
48479         'alphanum' : function(v){
48480             return alphanum.test(v);
48481         },
48482         /**
48483          * The error text to display when the alphanumeric validation function returns false
48484          * @type String
48485          */
48486         'alphanumText' : 'This field should only contain letters, numbers and _',
48487         /**
48488          * The keystroke filter mask to be applied on alphanumeric input
48489          * @type RegExp
48490          */
48491         'alphanumMask' : /[a-z0-9_]/i
48492     };
48493 }();//<script type="text/javascript">
48494
48495 /**
48496  * @class Roo.form.FCKeditor
48497  * @extends Roo.form.TextArea
48498  * Wrapper around the FCKEditor http://www.fckeditor.net
48499  * @constructor
48500  * Creates a new FCKeditor
48501  * @param {Object} config Configuration options
48502  */
48503 Roo.form.FCKeditor = function(config){
48504     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48505     this.addEvents({
48506          /**
48507          * @event editorinit
48508          * Fired when the editor is initialized - you can add extra handlers here..
48509          * @param {FCKeditor} this
48510          * @param {Object} the FCK object.
48511          */
48512         editorinit : true
48513     });
48514     
48515     
48516 };
48517 Roo.form.FCKeditor.editors = { };
48518 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48519 {
48520     //defaultAutoCreate : {
48521     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48522     //},
48523     // private
48524     /**
48525      * @cfg {Object} fck options - see fck manual for details.
48526      */
48527     fckconfig : false,
48528     
48529     /**
48530      * @cfg {Object} fck toolbar set (Basic or Default)
48531      */
48532     toolbarSet : 'Basic',
48533     /**
48534      * @cfg {Object} fck BasePath
48535      */ 
48536     basePath : '/fckeditor/',
48537     
48538     
48539     frame : false,
48540     
48541     value : '',
48542     
48543    
48544     onRender : function(ct, position)
48545     {
48546         if(!this.el){
48547             this.defaultAutoCreate = {
48548                 tag: "textarea",
48549                 style:"width:300px;height:60px;",
48550                 autocomplete: "new-password"
48551             };
48552         }
48553         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48554         /*
48555         if(this.grow){
48556             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48557             if(this.preventScrollbars){
48558                 this.el.setStyle("overflow", "hidden");
48559             }
48560             this.el.setHeight(this.growMin);
48561         }
48562         */
48563         //console.log('onrender' + this.getId() );
48564         Roo.form.FCKeditor.editors[this.getId()] = this;
48565          
48566
48567         this.replaceTextarea() ;
48568         
48569     },
48570     
48571     getEditor : function() {
48572         return this.fckEditor;
48573     },
48574     /**
48575      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48576      * @param {Mixed} value The value to set
48577      */
48578     
48579     
48580     setValue : function(value)
48581     {
48582         //console.log('setValue: ' + value);
48583         
48584         if(typeof(value) == 'undefined') { // not sure why this is happending...
48585             return;
48586         }
48587         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48588         
48589         //if(!this.el || !this.getEditor()) {
48590         //    this.value = value;
48591             //this.setValue.defer(100,this,[value]);    
48592         //    return;
48593         //} 
48594         
48595         if(!this.getEditor()) {
48596             return;
48597         }
48598         
48599         this.getEditor().SetData(value);
48600         
48601         //
48602
48603     },
48604
48605     /**
48606      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48607      * @return {Mixed} value The field value
48608      */
48609     getValue : function()
48610     {
48611         
48612         if (this.frame && this.frame.dom.style.display == 'none') {
48613             return Roo.form.FCKeditor.superclass.getValue.call(this);
48614         }
48615         
48616         if(!this.el || !this.getEditor()) {
48617            
48618            // this.getValue.defer(100,this); 
48619             return this.value;
48620         }
48621        
48622         
48623         var value=this.getEditor().GetData();
48624         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48625         return Roo.form.FCKeditor.superclass.getValue.call(this);
48626         
48627
48628     },
48629
48630     /**
48631      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48632      * @return {Mixed} value The field value
48633      */
48634     getRawValue : function()
48635     {
48636         if (this.frame && this.frame.dom.style.display == 'none') {
48637             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48638         }
48639         
48640         if(!this.el || !this.getEditor()) {
48641             //this.getRawValue.defer(100,this); 
48642             return this.value;
48643             return;
48644         }
48645         
48646         
48647         
48648         var value=this.getEditor().GetData();
48649         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48650         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48651          
48652     },
48653     
48654     setSize : function(w,h) {
48655         
48656         
48657         
48658         //if (this.frame && this.frame.dom.style.display == 'none') {
48659         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48660         //    return;
48661         //}
48662         //if(!this.el || !this.getEditor()) {
48663         //    this.setSize.defer(100,this, [w,h]); 
48664         //    return;
48665         //}
48666         
48667         
48668         
48669         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48670         
48671         this.frame.dom.setAttribute('width', w);
48672         this.frame.dom.setAttribute('height', h);
48673         this.frame.setSize(w,h);
48674         
48675     },
48676     
48677     toggleSourceEdit : function(value) {
48678         
48679       
48680          
48681         this.el.dom.style.display = value ? '' : 'none';
48682         this.frame.dom.style.display = value ?  'none' : '';
48683         
48684     },
48685     
48686     
48687     focus: function(tag)
48688     {
48689         if (this.frame.dom.style.display == 'none') {
48690             return Roo.form.FCKeditor.superclass.focus.call(this);
48691         }
48692         if(!this.el || !this.getEditor()) {
48693             this.focus.defer(100,this, [tag]); 
48694             return;
48695         }
48696         
48697         
48698         
48699         
48700         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48701         this.getEditor().Focus();
48702         if (tgs.length) {
48703             if (!this.getEditor().Selection.GetSelection()) {
48704                 this.focus.defer(100,this, [tag]); 
48705                 return;
48706             }
48707             
48708             
48709             var r = this.getEditor().EditorDocument.createRange();
48710             r.setStart(tgs[0],0);
48711             r.setEnd(tgs[0],0);
48712             this.getEditor().Selection.GetSelection().removeAllRanges();
48713             this.getEditor().Selection.GetSelection().addRange(r);
48714             this.getEditor().Focus();
48715         }
48716         
48717     },
48718     
48719     
48720     
48721     replaceTextarea : function()
48722     {
48723         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48724             return ;
48725         }
48726         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48727         //{
48728             // We must check the elements firstly using the Id and then the name.
48729         var oTextarea = document.getElementById( this.getId() );
48730         
48731         var colElementsByName = document.getElementsByName( this.getId() ) ;
48732          
48733         oTextarea.style.display = 'none' ;
48734
48735         if ( oTextarea.tabIndex ) {            
48736             this.TabIndex = oTextarea.tabIndex ;
48737         }
48738         
48739         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48740         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48741         this.frame = Roo.get(this.getId() + '___Frame')
48742     },
48743     
48744     _getConfigHtml : function()
48745     {
48746         var sConfig = '' ;
48747
48748         for ( var o in this.fckconfig ) {
48749             sConfig += sConfig.length > 0  ? '&amp;' : '';
48750             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48751         }
48752
48753         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48754     },
48755     
48756     
48757     _getIFrameHtml : function()
48758     {
48759         var sFile = 'fckeditor.html' ;
48760         /* no idea what this is about..
48761         try
48762         {
48763             if ( (/fcksource=true/i).test( window.top.location.search ) )
48764                 sFile = 'fckeditor.original.html' ;
48765         }
48766         catch (e) { 
48767         */
48768
48769         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48770         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48771         
48772         
48773         var html = '<iframe id="' + this.getId() +
48774             '___Frame" src="' + sLink +
48775             '" width="' + this.width +
48776             '" height="' + this.height + '"' +
48777             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48778             ' frameborder="0" scrolling="no"></iframe>' ;
48779
48780         return html ;
48781     },
48782     
48783     _insertHtmlBefore : function( html, element )
48784     {
48785         if ( element.insertAdjacentHTML )       {
48786             // IE
48787             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48788         } else { // Gecko
48789             var oRange = document.createRange() ;
48790             oRange.setStartBefore( element ) ;
48791             var oFragment = oRange.createContextualFragment( html );
48792             element.parentNode.insertBefore( oFragment, element ) ;
48793         }
48794     }
48795     
48796     
48797   
48798     
48799     
48800     
48801     
48802
48803 });
48804
48805 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48806
48807 function FCKeditor_OnComplete(editorInstance){
48808     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48809     f.fckEditor = editorInstance;
48810     //console.log("loaded");
48811     f.fireEvent('editorinit', f, editorInstance);
48812
48813   
48814
48815  
48816
48817
48818
48819
48820
48821
48822
48823
48824
48825
48826
48827
48828
48829
48830
48831 //<script type="text/javascript">
48832 /**
48833  * @class Roo.form.GridField
48834  * @extends Roo.form.Field
48835  * Embed a grid (or editable grid into a form)
48836  * STATUS ALPHA
48837  * 
48838  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48839  * it needs 
48840  * xgrid.store = Roo.data.Store
48841  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48842  * xgrid.store.reader = Roo.data.JsonReader 
48843  * 
48844  * 
48845  * @constructor
48846  * Creates a new GridField
48847  * @param {Object} config Configuration options
48848  */
48849 Roo.form.GridField = function(config){
48850     Roo.form.GridField.superclass.constructor.call(this, config);
48851      
48852 };
48853
48854 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48855     /**
48856      * @cfg {Number} width  - used to restrict width of grid..
48857      */
48858     width : 100,
48859     /**
48860      * @cfg {Number} height - used to restrict height of grid..
48861      */
48862     height : 50,
48863      /**
48864      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48865          * 
48866          *}
48867      */
48868     xgrid : false, 
48869     /**
48870      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48871      * {tag: "input", type: "checkbox", autocomplete: "off"})
48872      */
48873    // defaultAutoCreate : { tag: 'div' },
48874     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48875     /**
48876      * @cfg {String} addTitle Text to include for adding a title.
48877      */
48878     addTitle : false,
48879     //
48880     onResize : function(){
48881         Roo.form.Field.superclass.onResize.apply(this, arguments);
48882     },
48883
48884     initEvents : function(){
48885         // Roo.form.Checkbox.superclass.initEvents.call(this);
48886         // has no events...
48887        
48888     },
48889
48890
48891     getResizeEl : function(){
48892         return this.wrap;
48893     },
48894
48895     getPositionEl : function(){
48896         return this.wrap;
48897     },
48898
48899     // private
48900     onRender : function(ct, position){
48901         
48902         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48903         var style = this.style;
48904         delete this.style;
48905         
48906         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48907         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48908         this.viewEl = this.wrap.createChild({ tag: 'div' });
48909         if (style) {
48910             this.viewEl.applyStyles(style);
48911         }
48912         if (this.width) {
48913             this.viewEl.setWidth(this.width);
48914         }
48915         if (this.height) {
48916             this.viewEl.setHeight(this.height);
48917         }
48918         //if(this.inputValue !== undefined){
48919         //this.setValue(this.value);
48920         
48921         
48922         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48923         
48924         
48925         this.grid.render();
48926         this.grid.getDataSource().on('remove', this.refreshValue, this);
48927         this.grid.getDataSource().on('update', this.refreshValue, this);
48928         this.grid.on('afteredit', this.refreshValue, this);
48929  
48930     },
48931      
48932     
48933     /**
48934      * Sets the value of the item. 
48935      * @param {String} either an object  or a string..
48936      */
48937     setValue : function(v){
48938         //this.value = v;
48939         v = v || []; // empty set..
48940         // this does not seem smart - it really only affects memoryproxy grids..
48941         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48942             var ds = this.grid.getDataSource();
48943             // assumes a json reader..
48944             var data = {}
48945             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48946             ds.loadData( data);
48947         }
48948         // clear selection so it does not get stale.
48949         if (this.grid.sm) { 
48950             this.grid.sm.clearSelections();
48951         }
48952         
48953         Roo.form.GridField.superclass.setValue.call(this, v);
48954         this.refreshValue();
48955         // should load data in the grid really....
48956     },
48957     
48958     // private
48959     refreshValue: function() {
48960          var val = [];
48961         this.grid.getDataSource().each(function(r) {
48962             val.push(r.data);
48963         });
48964         this.el.dom.value = Roo.encode(val);
48965     }
48966     
48967      
48968     
48969     
48970 });/*
48971  * Based on:
48972  * Ext JS Library 1.1.1
48973  * Copyright(c) 2006-2007, Ext JS, LLC.
48974  *
48975  * Originally Released Under LGPL - original licence link has changed is not relivant.
48976  *
48977  * Fork - LGPL
48978  * <script type="text/javascript">
48979  */
48980 /**
48981  * @class Roo.form.DisplayField
48982  * @extends Roo.form.Field
48983  * A generic Field to display non-editable data.
48984  * @cfg {Boolean} closable (true|false) default false
48985  * @constructor
48986  * Creates a new Display Field item.
48987  * @param {Object} config Configuration options
48988  */
48989 Roo.form.DisplayField = function(config){
48990     Roo.form.DisplayField.superclass.constructor.call(this, config);
48991     
48992     this.addEvents({
48993         /**
48994          * @event close
48995          * Fires after the click the close btn
48996              * @param {Roo.form.DisplayField} this
48997              */
48998         close : true
48999     });
49000 };
49001
49002 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49003     inputType:      'hidden',
49004     allowBlank:     true,
49005     readOnly:         true,
49006     
49007  
49008     /**
49009      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49010      */
49011     focusClass : undefined,
49012     /**
49013      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49014      */
49015     fieldClass: 'x-form-field',
49016     
49017      /**
49018      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49019      */
49020     valueRenderer: undefined,
49021     
49022     width: 100,
49023     /**
49024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49025      * {tag: "input", type: "checkbox", autocomplete: "off"})
49026      */
49027      
49028  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49029  
49030     closable : false,
49031     
49032     onResize : function(){
49033         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49034         
49035     },
49036
49037     initEvents : function(){
49038         // Roo.form.Checkbox.superclass.initEvents.call(this);
49039         // has no events...
49040         
49041         if(this.closable){
49042             this.closeEl.on('click', this.onClose, this);
49043         }
49044        
49045     },
49046
49047
49048     getResizeEl : function(){
49049         return this.wrap;
49050     },
49051
49052     getPositionEl : function(){
49053         return this.wrap;
49054     },
49055
49056     // private
49057     onRender : function(ct, position){
49058         
49059         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49060         //if(this.inputValue !== undefined){
49061         this.wrap = this.el.wrap();
49062         
49063         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49064         
49065         if(this.closable){
49066             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49067         }
49068         
49069         if (this.bodyStyle) {
49070             this.viewEl.applyStyles(this.bodyStyle);
49071         }
49072         //this.viewEl.setStyle('padding', '2px');
49073         
49074         this.setValue(this.value);
49075         
49076     },
49077 /*
49078     // private
49079     initValue : Roo.emptyFn,
49080
49081   */
49082
49083         // private
49084     onClick : function(){
49085         
49086     },
49087
49088     /**
49089      * Sets the checked state of the checkbox.
49090      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49091      */
49092     setValue : function(v){
49093         this.value = v;
49094         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49095         // this might be called before we have a dom element..
49096         if (!this.viewEl) {
49097             return;
49098         }
49099         this.viewEl.dom.innerHTML = html;
49100         Roo.form.DisplayField.superclass.setValue.call(this, v);
49101
49102     },
49103     
49104     onClose : function(e)
49105     {
49106         e.preventDefault();
49107         
49108         this.fireEvent('close', this);
49109     }
49110 });/*
49111  * 
49112  * Licence- LGPL
49113  * 
49114  */
49115
49116 /**
49117  * @class Roo.form.DayPicker
49118  * @extends Roo.form.Field
49119  * A Day picker show [M] [T] [W] ....
49120  * @constructor
49121  * Creates a new Day Picker
49122  * @param {Object} config Configuration options
49123  */
49124 Roo.form.DayPicker= function(config){
49125     Roo.form.DayPicker.superclass.constructor.call(this, config);
49126      
49127 };
49128
49129 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49130     /**
49131      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49132      */
49133     focusClass : undefined,
49134     /**
49135      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49136      */
49137     fieldClass: "x-form-field",
49138    
49139     /**
49140      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49141      * {tag: "input", type: "checkbox", autocomplete: "off"})
49142      */
49143     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49144     
49145    
49146     actionMode : 'viewEl', 
49147     //
49148     // private
49149  
49150     inputType : 'hidden',
49151     
49152      
49153     inputElement: false, // real input element?
49154     basedOn: false, // ????
49155     
49156     isFormField: true, // not sure where this is needed!!!!
49157
49158     onResize : function(){
49159         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49160         if(!this.boxLabel){
49161             this.el.alignTo(this.wrap, 'c-c');
49162         }
49163     },
49164
49165     initEvents : function(){
49166         Roo.form.Checkbox.superclass.initEvents.call(this);
49167         this.el.on("click", this.onClick,  this);
49168         this.el.on("change", this.onClick,  this);
49169     },
49170
49171
49172     getResizeEl : function(){
49173         return this.wrap;
49174     },
49175
49176     getPositionEl : function(){
49177         return this.wrap;
49178     },
49179
49180     
49181     // private
49182     onRender : function(ct, position){
49183         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49184        
49185         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49186         
49187         var r1 = '<table><tr>';
49188         var r2 = '<tr class="x-form-daypick-icons">';
49189         for (var i=0; i < 7; i++) {
49190             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49191             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49192         }
49193         
49194         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49195         viewEl.select('img').on('click', this.onClick, this);
49196         this.viewEl = viewEl;   
49197         
49198         
49199         // this will not work on Chrome!!!
49200         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49201         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49202         
49203         
49204           
49205
49206     },
49207
49208     // private
49209     initValue : Roo.emptyFn,
49210
49211     /**
49212      * Returns the checked state of the checkbox.
49213      * @return {Boolean} True if checked, else false
49214      */
49215     getValue : function(){
49216         return this.el.dom.value;
49217         
49218     },
49219
49220         // private
49221     onClick : function(e){ 
49222         //this.setChecked(!this.checked);
49223         Roo.get(e.target).toggleClass('x-menu-item-checked');
49224         this.refreshValue();
49225         //if(this.el.dom.checked != this.checked){
49226         //    this.setValue(this.el.dom.checked);
49227        // }
49228     },
49229     
49230     // private
49231     refreshValue : function()
49232     {
49233         var val = '';
49234         this.viewEl.select('img',true).each(function(e,i,n)  {
49235             val += e.is(".x-menu-item-checked") ? String(n) : '';
49236         });
49237         this.setValue(val, true);
49238     },
49239
49240     /**
49241      * Sets the checked state of the checkbox.
49242      * On is always based on a string comparison between inputValue and the param.
49243      * @param {Boolean/String} value - the value to set 
49244      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49245      */
49246     setValue : function(v,suppressEvent){
49247         if (!this.el.dom) {
49248             return;
49249         }
49250         var old = this.el.dom.value ;
49251         this.el.dom.value = v;
49252         if (suppressEvent) {
49253             return ;
49254         }
49255          
49256         // update display..
49257         this.viewEl.select('img',true).each(function(e,i,n)  {
49258             
49259             var on = e.is(".x-menu-item-checked");
49260             var newv = v.indexOf(String(n)) > -1;
49261             if (on != newv) {
49262                 e.toggleClass('x-menu-item-checked');
49263             }
49264             
49265         });
49266         
49267         
49268         this.fireEvent('change', this, v, old);
49269         
49270         
49271     },
49272    
49273     // handle setting of hidden value by some other method!!?!?
49274     setFromHidden: function()
49275     {
49276         if(!this.el){
49277             return;
49278         }
49279         //console.log("SET FROM HIDDEN");
49280         //alert('setFrom hidden');
49281         this.setValue(this.el.dom.value);
49282     },
49283     
49284     onDestroy : function()
49285     {
49286         if(this.viewEl){
49287             Roo.get(this.viewEl).remove();
49288         }
49289          
49290         Roo.form.DayPicker.superclass.onDestroy.call(this);
49291     }
49292
49293 });/*
49294  * RooJS Library 1.1.1
49295  * Copyright(c) 2008-2011  Alan Knowles
49296  *
49297  * License - LGPL
49298  */
49299  
49300
49301 /**
49302  * @class Roo.form.ComboCheck
49303  * @extends Roo.form.ComboBox
49304  * A combobox for multiple select items.
49305  *
49306  * FIXME - could do with a reset button..
49307  * 
49308  * @constructor
49309  * Create a new ComboCheck
49310  * @param {Object} config Configuration options
49311  */
49312 Roo.form.ComboCheck = function(config){
49313     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49314     // should verify some data...
49315     // like
49316     // hiddenName = required..
49317     // displayField = required
49318     // valudField == required
49319     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49320     var _t = this;
49321     Roo.each(req, function(e) {
49322         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49323             throw "Roo.form.ComboCheck : missing value for: " + e;
49324         }
49325     });
49326     
49327     
49328 };
49329
49330 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49331      
49332      
49333     editable : false,
49334      
49335     selectedClass: 'x-menu-item-checked', 
49336     
49337     // private
49338     onRender : function(ct, position){
49339         var _t = this;
49340         
49341         
49342         
49343         if(!this.tpl){
49344             var cls = 'x-combo-list';
49345
49346             
49347             this.tpl =  new Roo.Template({
49348                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49349                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49350                    '<span>{' + this.displayField + '}</span>' +
49351                     '</div>' 
49352                 
49353             });
49354         }
49355  
49356         
49357         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49358         this.view.singleSelect = false;
49359         this.view.multiSelect = true;
49360         this.view.toggleSelect = true;
49361         this.pageTb.add(new Roo.Toolbar.Fill(), {
49362             
49363             text: 'Done',
49364             handler: function()
49365             {
49366                 _t.collapse();
49367             }
49368         });
49369     },
49370     
49371     onViewOver : function(e, t){
49372         // do nothing...
49373         return;
49374         
49375     },
49376     
49377     onViewClick : function(doFocus,index){
49378         return;
49379         
49380     },
49381     select: function () {
49382         //Roo.log("SELECT CALLED");
49383     },
49384      
49385     selectByValue : function(xv, scrollIntoView){
49386         var ar = this.getValueArray();
49387         var sels = [];
49388         
49389         Roo.each(ar, function(v) {
49390             if(v === undefined || v === null){
49391                 return;
49392             }
49393             var r = this.findRecord(this.valueField, v);
49394             if(r){
49395                 sels.push(this.store.indexOf(r))
49396                 
49397             }
49398         },this);
49399         this.view.select(sels);
49400         return false;
49401     },
49402     
49403     
49404     
49405     onSelect : function(record, index){
49406        // Roo.log("onselect Called");
49407        // this is only called by the clear button now..
49408         this.view.clearSelections();
49409         this.setValue('[]');
49410         if (this.value != this.valueBefore) {
49411             this.fireEvent('change', this, this.value, this.valueBefore);
49412             this.valueBefore = this.value;
49413         }
49414     },
49415     getValueArray : function()
49416     {
49417         var ar = [] ;
49418         
49419         try {
49420             //Roo.log(this.value);
49421             if (typeof(this.value) == 'undefined') {
49422                 return [];
49423             }
49424             var ar = Roo.decode(this.value);
49425             return  ar instanceof Array ? ar : []; //?? valid?
49426             
49427         } catch(e) {
49428             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49429             return [];
49430         }
49431          
49432     },
49433     expand : function ()
49434     {
49435         
49436         Roo.form.ComboCheck.superclass.expand.call(this);
49437         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49438         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49439         
49440
49441     },
49442     
49443     collapse : function(){
49444         Roo.form.ComboCheck.superclass.collapse.call(this);
49445         var sl = this.view.getSelectedIndexes();
49446         var st = this.store;
49447         var nv = [];
49448         var tv = [];
49449         var r;
49450         Roo.each(sl, function(i) {
49451             r = st.getAt(i);
49452             nv.push(r.get(this.valueField));
49453         },this);
49454         this.setValue(Roo.encode(nv));
49455         if (this.value != this.valueBefore) {
49456
49457             this.fireEvent('change', this, this.value, this.valueBefore);
49458             this.valueBefore = this.value;
49459         }
49460         
49461     },
49462     
49463     setValue : function(v){
49464         // Roo.log(v);
49465         this.value = v;
49466         
49467         var vals = this.getValueArray();
49468         var tv = [];
49469         Roo.each(vals, function(k) {
49470             var r = this.findRecord(this.valueField, k);
49471             if(r){
49472                 tv.push(r.data[this.displayField]);
49473             }else if(this.valueNotFoundText !== undefined){
49474                 tv.push( this.valueNotFoundText );
49475             }
49476         },this);
49477        // Roo.log(tv);
49478         
49479         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49480         this.hiddenField.value = v;
49481         this.value = v;
49482     }
49483     
49484 });/*
49485  * Based on:
49486  * Ext JS Library 1.1.1
49487  * Copyright(c) 2006-2007, Ext JS, LLC.
49488  *
49489  * Originally Released Under LGPL - original licence link has changed is not relivant.
49490  *
49491  * Fork - LGPL
49492  * <script type="text/javascript">
49493  */
49494  
49495 /**
49496  * @class Roo.form.Signature
49497  * @extends Roo.form.Field
49498  * Signature field.  
49499  * @constructor
49500  * 
49501  * @param {Object} config Configuration options
49502  */
49503
49504 Roo.form.Signature = function(config){
49505     Roo.form.Signature.superclass.constructor.call(this, config);
49506     
49507     this.addEvents({// not in used??
49508          /**
49509          * @event confirm
49510          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49511              * @param {Roo.form.Signature} combo This combo box
49512              */
49513         'confirm' : true,
49514         /**
49515          * @event reset
49516          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49517              * @param {Roo.form.ComboBox} combo This combo box
49518              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49519              */
49520         'reset' : true
49521     });
49522 };
49523
49524 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49525     /**
49526      * @cfg {Object} labels Label to use when rendering a form.
49527      * defaults to 
49528      * labels : { 
49529      *      clear : "Clear",
49530      *      confirm : "Confirm"
49531      *  }
49532      */
49533     labels : { 
49534         clear : "Clear",
49535         confirm : "Confirm"
49536     },
49537     /**
49538      * @cfg {Number} width The signature panel width (defaults to 300)
49539      */
49540     width: 300,
49541     /**
49542      * @cfg {Number} height The signature panel height (defaults to 100)
49543      */
49544     height : 100,
49545     /**
49546      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49547      */
49548     allowBlank : false,
49549     
49550     //private
49551     // {Object} signPanel The signature SVG panel element (defaults to {})
49552     signPanel : {},
49553     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49554     isMouseDown : false,
49555     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49556     isConfirmed : false,
49557     // {String} signatureTmp SVG mapping string (defaults to empty string)
49558     signatureTmp : '',
49559     
49560     
49561     defaultAutoCreate : { // modified by initCompnoent..
49562         tag: "input",
49563         type:"hidden"
49564     },
49565
49566     // private
49567     onRender : function(ct, position){
49568         
49569         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49570         
49571         this.wrap = this.el.wrap({
49572             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49573         });
49574         
49575         this.createToolbar(this);
49576         this.signPanel = this.wrap.createChild({
49577                 tag: 'div',
49578                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49579             }, this.el
49580         );
49581             
49582         this.svgID = Roo.id();
49583         this.svgEl = this.signPanel.createChild({
49584               xmlns : 'http://www.w3.org/2000/svg',
49585               tag : 'svg',
49586               id : this.svgID + "-svg",
49587               width: this.width,
49588               height: this.height,
49589               viewBox: '0 0 '+this.width+' '+this.height,
49590               cn : [
49591                 {
49592                     tag: "rect",
49593                     id: this.svgID + "-svg-r",
49594                     width: this.width,
49595                     height: this.height,
49596                     fill: "#ffa"
49597                 },
49598                 {
49599                     tag: "line",
49600                     id: this.svgID + "-svg-l",
49601                     x1: "0", // start
49602                     y1: (this.height*0.8), // start set the line in 80% of height
49603                     x2: this.width, // end
49604                     y2: (this.height*0.8), // end set the line in 80% of height
49605                     'stroke': "#666",
49606                     'stroke-width': "1",
49607                     'stroke-dasharray': "3",
49608                     'shape-rendering': "crispEdges",
49609                     'pointer-events': "none"
49610                 },
49611                 {
49612                     tag: "path",
49613                     id: this.svgID + "-svg-p",
49614                     'stroke': "navy",
49615                     'stroke-width': "3",
49616                     'fill': "none",
49617                     'pointer-events': 'none'
49618                 }
49619               ]
49620         });
49621         this.createSVG();
49622         this.svgBox = this.svgEl.dom.getScreenCTM();
49623     },
49624     createSVG : function(){ 
49625         var svg = this.signPanel;
49626         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49627         var t = this;
49628
49629         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49630         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49631         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49632         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49633         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49634         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49635         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49636         
49637     },
49638     isTouchEvent : function(e){
49639         return e.type.match(/^touch/);
49640     },
49641     getCoords : function (e) {
49642         var pt    = this.svgEl.dom.createSVGPoint();
49643         pt.x = e.clientX; 
49644         pt.y = e.clientY;
49645         if (this.isTouchEvent(e)) {
49646             pt.x =  e.targetTouches[0].clientX;
49647             pt.y = e.targetTouches[0].clientY;
49648         }
49649         var a = this.svgEl.dom.getScreenCTM();
49650         var b = a.inverse();
49651         var mx = pt.matrixTransform(b);
49652         return mx.x + ',' + mx.y;
49653     },
49654     //mouse event headler 
49655     down : function (e) {
49656         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49657         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49658         
49659         this.isMouseDown = true;
49660         
49661         e.preventDefault();
49662     },
49663     move : function (e) {
49664         if (this.isMouseDown) {
49665             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49666             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49667         }
49668         
49669         e.preventDefault();
49670     },
49671     up : function (e) {
49672         this.isMouseDown = false;
49673         var sp = this.signatureTmp.split(' ');
49674         
49675         if(sp.length > 1){
49676             if(!sp[sp.length-2].match(/^L/)){
49677                 sp.pop();
49678                 sp.pop();
49679                 sp.push("");
49680                 this.signatureTmp = sp.join(" ");
49681             }
49682         }
49683         if(this.getValue() != this.signatureTmp){
49684             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49685             this.isConfirmed = false;
49686         }
49687         e.preventDefault();
49688     },
49689     
49690     /**
49691      * Protected method that will not generally be called directly. It
49692      * is called when the editor creates its toolbar. Override this method if you need to
49693      * add custom toolbar buttons.
49694      * @param {HtmlEditor} editor
49695      */
49696     createToolbar : function(editor){
49697          function btn(id, toggle, handler){
49698             var xid = fid + '-'+ id ;
49699             return {
49700                 id : xid,
49701                 cmd : id,
49702                 cls : 'x-btn-icon x-edit-'+id,
49703                 enableToggle:toggle !== false,
49704                 scope: editor, // was editor...
49705                 handler:handler||editor.relayBtnCmd,
49706                 clickEvent:'mousedown',
49707                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49708                 tabIndex:-1
49709             };
49710         }
49711         
49712         
49713         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49714         this.tb = tb;
49715         this.tb.add(
49716            {
49717                 cls : ' x-signature-btn x-signature-'+id,
49718                 scope: editor, // was editor...
49719                 handler: this.reset,
49720                 clickEvent:'mousedown',
49721                 text: this.labels.clear
49722             },
49723             {
49724                  xtype : 'Fill',
49725                  xns: Roo.Toolbar
49726             }, 
49727             {
49728                 cls : '  x-signature-btn x-signature-'+id,
49729                 scope: editor, // was editor...
49730                 handler: this.confirmHandler,
49731                 clickEvent:'mousedown',
49732                 text: this.labels.confirm
49733             }
49734         );
49735     
49736     },
49737     //public
49738     /**
49739      * when user is clicked confirm then show this image.....
49740      * 
49741      * @return {String} Image Data URI
49742      */
49743     getImageDataURI : function(){
49744         var svg = this.svgEl.dom.parentNode.innerHTML;
49745         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49746         return src; 
49747     },
49748     /**
49749      * 
49750      * @return {Boolean} this.isConfirmed
49751      */
49752     getConfirmed : function(){
49753         return this.isConfirmed;
49754     },
49755     /**
49756      * 
49757      * @return {Number} this.width
49758      */
49759     getWidth : function(){
49760         return this.width;
49761     },
49762     /**
49763      * 
49764      * @return {Number} this.height
49765      */
49766     getHeight : function(){
49767         return this.height;
49768     },
49769     // private
49770     getSignature : function(){
49771         return this.signatureTmp;
49772     },
49773     // private
49774     reset : function(){
49775         this.signatureTmp = '';
49776         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49777         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49778         this.isConfirmed = false;
49779         Roo.form.Signature.superclass.reset.call(this);
49780     },
49781     setSignature : function(s){
49782         this.signatureTmp = s;
49783         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49784         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49785         this.setValue(s);
49786         this.isConfirmed = false;
49787         Roo.form.Signature.superclass.reset.call(this);
49788     }, 
49789     test : function(){
49790 //        Roo.log(this.signPanel.dom.contentWindow.up())
49791     },
49792     //private
49793     setConfirmed : function(){
49794         
49795         
49796         
49797 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49798     },
49799     // private
49800     confirmHandler : function(){
49801         if(!this.getSignature()){
49802             return;
49803         }
49804         
49805         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49806         this.setValue(this.getSignature());
49807         this.isConfirmed = true;
49808         
49809         this.fireEvent('confirm', this);
49810     },
49811     // private
49812     // Subclasses should provide the validation implementation by overriding this
49813     validateValue : function(value){
49814         if(this.allowBlank){
49815             return true;
49816         }
49817         
49818         if(this.isConfirmed){
49819             return true;
49820         }
49821         return false;
49822     }
49823 });/*
49824  * Based on:
49825  * Ext JS Library 1.1.1
49826  * Copyright(c) 2006-2007, Ext JS, LLC.
49827  *
49828  * Originally Released Under LGPL - original licence link has changed is not relivant.
49829  *
49830  * Fork - LGPL
49831  * <script type="text/javascript">
49832  */
49833  
49834
49835 /**
49836  * @class Roo.form.ComboBox
49837  * @extends Roo.form.TriggerField
49838  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49839  * @constructor
49840  * Create a new ComboBox.
49841  * @param {Object} config Configuration options
49842  */
49843 Roo.form.Select = function(config){
49844     Roo.form.Select.superclass.constructor.call(this, config);
49845      
49846 };
49847
49848 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49849     /**
49850      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49851      */
49852     /**
49853      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49854      * rendering into an Roo.Editor, defaults to false)
49855      */
49856     /**
49857      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49858      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49859      */
49860     /**
49861      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49862      */
49863     /**
49864      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49865      * the dropdown list (defaults to undefined, with no header element)
49866      */
49867
49868      /**
49869      * @cfg {String/Roo.Template} tpl The template to use to render the output
49870      */
49871      
49872     // private
49873     defaultAutoCreate : {tag: "select"  },
49874     /**
49875      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49876      */
49877     listWidth: undefined,
49878     /**
49879      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49880      * mode = 'remote' or 'text' if mode = 'local')
49881      */
49882     displayField: undefined,
49883     /**
49884      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49885      * mode = 'remote' or 'value' if mode = 'local'). 
49886      * Note: use of a valueField requires the user make a selection
49887      * in order for a value to be mapped.
49888      */
49889     valueField: undefined,
49890     
49891     
49892     /**
49893      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49894      * field's data value (defaults to the underlying DOM element's name)
49895      */
49896     hiddenName: undefined,
49897     /**
49898      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49899      */
49900     listClass: '',
49901     /**
49902      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49903      */
49904     selectedClass: 'x-combo-selected',
49905     /**
49906      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49907      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49908      * which displays a downward arrow icon).
49909      */
49910     triggerClass : 'x-form-arrow-trigger',
49911     /**
49912      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49913      */
49914     shadow:'sides',
49915     /**
49916      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49917      * anchor positions (defaults to 'tl-bl')
49918      */
49919     listAlign: 'tl-bl?',
49920     /**
49921      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49922      */
49923     maxHeight: 300,
49924     /**
49925      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49926      * query specified by the allQuery config option (defaults to 'query')
49927      */
49928     triggerAction: 'query',
49929     /**
49930      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49931      * (defaults to 4, does not apply if editable = false)
49932      */
49933     minChars : 4,
49934     /**
49935      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49936      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49937      */
49938     typeAhead: false,
49939     /**
49940      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49941      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49942      */
49943     queryDelay: 500,
49944     /**
49945      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49946      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49947      */
49948     pageSize: 0,
49949     /**
49950      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49951      * when editable = true (defaults to false)
49952      */
49953     selectOnFocus:false,
49954     /**
49955      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49956      */
49957     queryParam: 'query',
49958     /**
49959      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49960      * when mode = 'remote' (defaults to 'Loading...')
49961      */
49962     loadingText: 'Loading...',
49963     /**
49964      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49965      */
49966     resizable: false,
49967     /**
49968      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49969      */
49970     handleHeight : 8,
49971     /**
49972      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49973      * traditional select (defaults to true)
49974      */
49975     editable: true,
49976     /**
49977      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49978      */
49979     allQuery: '',
49980     /**
49981      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49982      */
49983     mode: 'remote',
49984     /**
49985      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49986      * listWidth has a higher value)
49987      */
49988     minListWidth : 70,
49989     /**
49990      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49991      * allow the user to set arbitrary text into the field (defaults to false)
49992      */
49993     forceSelection:false,
49994     /**
49995      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49996      * if typeAhead = true (defaults to 250)
49997      */
49998     typeAheadDelay : 250,
49999     /**
50000      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50001      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50002      */
50003     valueNotFoundText : undefined,
50004     
50005     /**
50006      * @cfg {String} defaultValue The value displayed after loading the store.
50007      */
50008     defaultValue: '',
50009     
50010     /**
50011      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50012      */
50013     blockFocus : false,
50014     
50015     /**
50016      * @cfg {Boolean} disableClear Disable showing of clear button.
50017      */
50018     disableClear : false,
50019     /**
50020      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50021      */
50022     alwaysQuery : false,
50023     
50024     //private
50025     addicon : false,
50026     editicon: false,
50027     
50028     // element that contains real text value.. (when hidden is used..)
50029      
50030     // private
50031     onRender : function(ct, position){
50032         Roo.form.Field.prototype.onRender.call(this, ct, position);
50033         
50034         if(this.store){
50035             this.store.on('beforeload', this.onBeforeLoad, this);
50036             this.store.on('load', this.onLoad, this);
50037             this.store.on('loadexception', this.onLoadException, this);
50038             this.store.load({});
50039         }
50040         
50041         
50042         
50043     },
50044
50045     // private
50046     initEvents : function(){
50047         //Roo.form.ComboBox.superclass.initEvents.call(this);
50048  
50049     },
50050
50051     onDestroy : function(){
50052        
50053         if(this.store){
50054             this.store.un('beforeload', this.onBeforeLoad, this);
50055             this.store.un('load', this.onLoad, this);
50056             this.store.un('loadexception', this.onLoadException, this);
50057         }
50058         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50059     },
50060
50061     // private
50062     fireKey : function(e){
50063         if(e.isNavKeyPress() && !this.list.isVisible()){
50064             this.fireEvent("specialkey", this, e);
50065         }
50066     },
50067
50068     // private
50069     onResize: function(w, h){
50070         
50071         return; 
50072     
50073         
50074     },
50075
50076     /**
50077      * Allow or prevent the user from directly editing the field text.  If false is passed,
50078      * the user will only be able to select from the items defined in the dropdown list.  This method
50079      * is the runtime equivalent of setting the 'editable' config option at config time.
50080      * @param {Boolean} value True to allow the user to directly edit the field text
50081      */
50082     setEditable : function(value){
50083          
50084     },
50085
50086     // private
50087     onBeforeLoad : function(){
50088         
50089         Roo.log("Select before load");
50090         return;
50091     
50092         this.innerList.update(this.loadingText ?
50093                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50094         //this.restrictHeight();
50095         this.selectedIndex = -1;
50096     },
50097
50098     // private
50099     onLoad : function(){
50100
50101     
50102         var dom = this.el.dom;
50103         dom.innerHTML = '';
50104          var od = dom.ownerDocument;
50105          
50106         if (this.emptyText) {
50107             var op = od.createElement('option');
50108             op.setAttribute('value', '');
50109             op.innerHTML = String.format('{0}', this.emptyText);
50110             dom.appendChild(op);
50111         }
50112         if(this.store.getCount() > 0){
50113            
50114             var vf = this.valueField;
50115             var df = this.displayField;
50116             this.store.data.each(function(r) {
50117                 // which colmsn to use... testing - cdoe / title..
50118                 var op = od.createElement('option');
50119                 op.setAttribute('value', r.data[vf]);
50120                 op.innerHTML = String.format('{0}', r.data[df]);
50121                 dom.appendChild(op);
50122             });
50123             if (typeof(this.defaultValue != 'undefined')) {
50124                 this.setValue(this.defaultValue);
50125             }
50126             
50127              
50128         }else{
50129             //this.onEmptyResults();
50130         }
50131         //this.el.focus();
50132     },
50133     // private
50134     onLoadException : function()
50135     {
50136         dom.innerHTML = '';
50137             
50138         Roo.log("Select on load exception");
50139         return;
50140     
50141         this.collapse();
50142         Roo.log(this.store.reader.jsonData);
50143         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50144             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50145         }
50146         
50147         
50148     },
50149     // private
50150     onTypeAhead : function(){
50151          
50152     },
50153
50154     // private
50155     onSelect : function(record, index){
50156         Roo.log('on select?');
50157         return;
50158         if(this.fireEvent('beforeselect', this, record, index) !== false){
50159             this.setFromData(index > -1 ? record.data : false);
50160             this.collapse();
50161             this.fireEvent('select', this, record, index);
50162         }
50163     },
50164
50165     /**
50166      * Returns the currently selected field value or empty string if no value is set.
50167      * @return {String} value The selected value
50168      */
50169     getValue : function(){
50170         var dom = this.el.dom;
50171         this.value = dom.options[dom.selectedIndex].value;
50172         return this.value;
50173         
50174     },
50175
50176     /**
50177      * Clears any text/value currently set in the field
50178      */
50179     clearValue : function(){
50180         this.value = '';
50181         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50182         
50183     },
50184
50185     /**
50186      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50187      * will be displayed in the field.  If the value does not match the data value of an existing item,
50188      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50189      * Otherwise the field will be blank (although the value will still be set).
50190      * @param {String} value The value to match
50191      */
50192     setValue : function(v){
50193         var d = this.el.dom;
50194         for (var i =0; i < d.options.length;i++) {
50195             if (v == d.options[i].value) {
50196                 d.selectedIndex = i;
50197                 this.value = v;
50198                 return;
50199             }
50200         }
50201         this.clearValue();
50202     },
50203     /**
50204      * @property {Object} the last set data for the element
50205      */
50206     
50207     lastData : false,
50208     /**
50209      * Sets the value of the field based on a object which is related to the record format for the store.
50210      * @param {Object} value the value to set as. or false on reset?
50211      */
50212     setFromData : function(o){
50213         Roo.log('setfrom data?');
50214          
50215         
50216         
50217     },
50218     // private
50219     reset : function(){
50220         this.clearValue();
50221     },
50222     // private
50223     findRecord : function(prop, value){
50224         
50225         return false;
50226     
50227         var record;
50228         if(this.store.getCount() > 0){
50229             this.store.each(function(r){
50230                 if(r.data[prop] == value){
50231                     record = r;
50232                     return false;
50233                 }
50234                 return true;
50235             });
50236         }
50237         return record;
50238     },
50239     
50240     getName: function()
50241     {
50242         // returns hidden if it's set..
50243         if (!this.rendered) {return ''};
50244         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50245         
50246     },
50247      
50248
50249     
50250
50251     // private
50252     onEmptyResults : function(){
50253         Roo.log('empty results');
50254         //this.collapse();
50255     },
50256
50257     /**
50258      * Returns true if the dropdown list is expanded, else false.
50259      */
50260     isExpanded : function(){
50261         return false;
50262     },
50263
50264     /**
50265      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50266      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50267      * @param {String} value The data value of the item to select
50268      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50269      * selected item if it is not currently in view (defaults to true)
50270      * @return {Boolean} True if the value matched an item in the list, else false
50271      */
50272     selectByValue : function(v, scrollIntoView){
50273         Roo.log('select By Value');
50274         return false;
50275     
50276         if(v !== undefined && v !== null){
50277             var r = this.findRecord(this.valueField || this.displayField, v);
50278             if(r){
50279                 this.select(this.store.indexOf(r), scrollIntoView);
50280                 return true;
50281             }
50282         }
50283         return false;
50284     },
50285
50286     /**
50287      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50288      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50289      * @param {Number} index The zero-based index of the list item to select
50290      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50291      * selected item if it is not currently in view (defaults to true)
50292      */
50293     select : function(index, scrollIntoView){
50294         Roo.log('select ');
50295         return  ;
50296         
50297         this.selectedIndex = index;
50298         this.view.select(index);
50299         if(scrollIntoView !== false){
50300             var el = this.view.getNode(index);
50301             if(el){
50302                 this.innerList.scrollChildIntoView(el, false);
50303             }
50304         }
50305     },
50306
50307       
50308
50309     // private
50310     validateBlur : function(){
50311         
50312         return;
50313         
50314     },
50315
50316     // private
50317     initQuery : function(){
50318         this.doQuery(this.getRawValue());
50319     },
50320
50321     // private
50322     doForce : function(){
50323         if(this.el.dom.value.length > 0){
50324             this.el.dom.value =
50325                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50326              
50327         }
50328     },
50329
50330     /**
50331      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50332      * query allowing the query action to be canceled if needed.
50333      * @param {String} query The SQL query to execute
50334      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50335      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50336      * saved in the current store (defaults to false)
50337      */
50338     doQuery : function(q, forceAll){
50339         
50340         Roo.log('doQuery?');
50341         if(q === undefined || q === null){
50342             q = '';
50343         }
50344         var qe = {
50345             query: q,
50346             forceAll: forceAll,
50347             combo: this,
50348             cancel:false
50349         };
50350         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50351             return false;
50352         }
50353         q = qe.query;
50354         forceAll = qe.forceAll;
50355         if(forceAll === true || (q.length >= this.minChars)){
50356             if(this.lastQuery != q || this.alwaysQuery){
50357                 this.lastQuery = q;
50358                 if(this.mode == 'local'){
50359                     this.selectedIndex = -1;
50360                     if(forceAll){
50361                         this.store.clearFilter();
50362                     }else{
50363                         this.store.filter(this.displayField, q);
50364                     }
50365                     this.onLoad();
50366                 }else{
50367                     this.store.baseParams[this.queryParam] = q;
50368                     this.store.load({
50369                         params: this.getParams(q)
50370                     });
50371                     this.expand();
50372                 }
50373             }else{
50374                 this.selectedIndex = -1;
50375                 this.onLoad();   
50376             }
50377         }
50378     },
50379
50380     // private
50381     getParams : function(q){
50382         var p = {};
50383         //p[this.queryParam] = q;
50384         if(this.pageSize){
50385             p.start = 0;
50386             p.limit = this.pageSize;
50387         }
50388         return p;
50389     },
50390
50391     /**
50392      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50393      */
50394     collapse : function(){
50395         
50396     },
50397
50398     // private
50399     collapseIf : function(e){
50400         
50401     },
50402
50403     /**
50404      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50405      */
50406     expand : function(){
50407         
50408     } ,
50409
50410     // private
50411      
50412
50413     /** 
50414     * @cfg {Boolean} grow 
50415     * @hide 
50416     */
50417     /** 
50418     * @cfg {Number} growMin 
50419     * @hide 
50420     */
50421     /** 
50422     * @cfg {Number} growMax 
50423     * @hide 
50424     */
50425     /**
50426      * @hide
50427      * @method autoSize
50428      */
50429     
50430     setWidth : function()
50431     {
50432         
50433     },
50434     getResizeEl : function(){
50435         return this.el;
50436     }
50437 });//<script type="text/javasscript">
50438  
50439
50440 /**
50441  * @class Roo.DDView
50442  * A DnD enabled version of Roo.View.
50443  * @param {Element/String} container The Element in which to create the View.
50444  * @param {String} tpl The template string used to create the markup for each element of the View
50445  * @param {Object} config The configuration properties. These include all the config options of
50446  * {@link Roo.View} plus some specific to this class.<br>
50447  * <p>
50448  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50449  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50450  * <p>
50451  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50452 .x-view-drag-insert-above {
50453         border-top:1px dotted #3366cc;
50454 }
50455 .x-view-drag-insert-below {
50456         border-bottom:1px dotted #3366cc;
50457 }
50458 </code></pre>
50459  * 
50460  */
50461  
50462 Roo.DDView = function(container, tpl, config) {
50463     Roo.DDView.superclass.constructor.apply(this, arguments);
50464     this.getEl().setStyle("outline", "0px none");
50465     this.getEl().unselectable();
50466     if (this.dragGroup) {
50467                 this.setDraggable(this.dragGroup.split(","));
50468     }
50469     if (this.dropGroup) {
50470                 this.setDroppable(this.dropGroup.split(","));
50471     }
50472     if (this.deletable) {
50473         this.setDeletable();
50474     }
50475     this.isDirtyFlag = false;
50476         this.addEvents({
50477                 "drop" : true
50478         });
50479 };
50480
50481 Roo.extend(Roo.DDView, Roo.View, {
50482 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50483 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50484 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50485 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50486
50487         isFormField: true,
50488
50489         reset: Roo.emptyFn,
50490         
50491         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50492
50493         validate: function() {
50494                 return true;
50495         },
50496         
50497         destroy: function() {
50498                 this.purgeListeners();
50499                 this.getEl.removeAllListeners();
50500                 this.getEl().remove();
50501                 if (this.dragZone) {
50502                         if (this.dragZone.destroy) {
50503                                 this.dragZone.destroy();
50504                         }
50505                 }
50506                 if (this.dropZone) {
50507                         if (this.dropZone.destroy) {
50508                                 this.dropZone.destroy();
50509                         }
50510                 }
50511         },
50512
50513 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50514         getName: function() {
50515                 return this.name;
50516         },
50517
50518 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50519         setValue: function(v) {
50520                 if (!this.store) {
50521                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50522                 }
50523                 var data = {};
50524                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50525                 this.store.proxy = new Roo.data.MemoryProxy(data);
50526                 this.store.load();
50527         },
50528
50529 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50530         getValue: function() {
50531                 var result = '(';
50532                 this.store.each(function(rec) {
50533                         result += rec.id + ',';
50534                 });
50535                 return result.substr(0, result.length - 1) + ')';
50536         },
50537         
50538         getIds: function() {
50539                 var i = 0, result = new Array(this.store.getCount());
50540                 this.store.each(function(rec) {
50541                         result[i++] = rec.id;
50542                 });
50543                 return result;
50544         },
50545         
50546         isDirty: function() {
50547                 return this.isDirtyFlag;
50548         },
50549
50550 /**
50551  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50552  *      whole Element becomes the target, and this causes the drop gesture to append.
50553  */
50554     getTargetFromEvent : function(e) {
50555                 var target = e.getTarget();
50556                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50557                 target = target.parentNode;
50558                 }
50559                 if (!target) {
50560                         target = this.el.dom.lastChild || this.el.dom;
50561                 }
50562                 return target;
50563     },
50564
50565 /**
50566  *      Create the drag data which consists of an object which has the property "ddel" as
50567  *      the drag proxy element. 
50568  */
50569     getDragData : function(e) {
50570         var target = this.findItemFromChild(e.getTarget());
50571                 if(target) {
50572                         this.handleSelection(e);
50573                         var selNodes = this.getSelectedNodes();
50574             var dragData = {
50575                 source: this,
50576                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50577                 nodes: selNodes,
50578                 records: []
50579                         };
50580                         var selectedIndices = this.getSelectedIndexes();
50581                         for (var i = 0; i < selectedIndices.length; i++) {
50582                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50583                         }
50584                         if (selNodes.length == 1) {
50585                                 dragData.ddel = target.cloneNode(true); // the div element
50586                         } else {
50587                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50588                                 div.className = 'multi-proxy';
50589                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50590                                         div.appendChild(selNodes[i].cloneNode(true));
50591                                 }
50592                                 dragData.ddel = div;
50593                         }
50594             //console.log(dragData)
50595             //console.log(dragData.ddel.innerHTML)
50596                         return dragData;
50597                 }
50598         //console.log('nodragData')
50599                 return false;
50600     },
50601     
50602 /**     Specify to which ddGroup items in this DDView may be dragged. */
50603     setDraggable: function(ddGroup) {
50604         if (ddGroup instanceof Array) {
50605                 Roo.each(ddGroup, this.setDraggable, this);
50606                 return;
50607         }
50608         if (this.dragZone) {
50609                 this.dragZone.addToGroup(ddGroup);
50610         } else {
50611                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50612                                 containerScroll: true,
50613                                 ddGroup: ddGroup 
50614
50615                         });
50616 //                      Draggability implies selection. DragZone's mousedown selects the element.
50617                         if (!this.multiSelect) { this.singleSelect = true; }
50618
50619 //                      Wire the DragZone's handlers up to methods in *this*
50620                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50621                 }
50622     },
50623
50624 /**     Specify from which ddGroup this DDView accepts drops. */
50625     setDroppable: function(ddGroup) {
50626         if (ddGroup instanceof Array) {
50627                 Roo.each(ddGroup, this.setDroppable, this);
50628                 return;
50629         }
50630         if (this.dropZone) {
50631                 this.dropZone.addToGroup(ddGroup);
50632         } else {
50633                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50634                                 containerScroll: true,
50635                                 ddGroup: ddGroup
50636                         });
50637
50638 //                      Wire the DropZone's handlers up to methods in *this*
50639                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50640                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50641                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50642                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50643                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50644                 }
50645     },
50646
50647 /**     Decide whether to drop above or below a View node. */
50648     getDropPoint : function(e, n, dd){
50649         if (n == this.el.dom) { return "above"; }
50650                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50651                 var c = t + (b - t) / 2;
50652                 var y = Roo.lib.Event.getPageY(e);
50653                 if(y <= c) {
50654                         return "above";
50655                 }else{
50656                         return "below";
50657                 }
50658     },
50659
50660     onNodeEnter : function(n, dd, e, data){
50661                 return false;
50662     },
50663     
50664     onNodeOver : function(n, dd, e, data){
50665                 var pt = this.getDropPoint(e, n, dd);
50666                 // set the insert point style on the target node
50667                 var dragElClass = this.dropNotAllowed;
50668                 if (pt) {
50669                         var targetElClass;
50670                         if (pt == "above"){
50671                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50672                                 targetElClass = "x-view-drag-insert-above";
50673                         } else {
50674                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50675                                 targetElClass = "x-view-drag-insert-below";
50676                         }
50677                         if (this.lastInsertClass != targetElClass){
50678                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50679                                 this.lastInsertClass = targetElClass;
50680                         }
50681                 }
50682                 return dragElClass;
50683         },
50684
50685     onNodeOut : function(n, dd, e, data){
50686                 this.removeDropIndicators(n);
50687     },
50688
50689     onNodeDrop : function(n, dd, e, data){
50690         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50691                 return false;
50692         }
50693         var pt = this.getDropPoint(e, n, dd);
50694                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50695                 if (pt == "below") { insertAt++; }
50696                 for (var i = 0; i < data.records.length; i++) {
50697                         var r = data.records[i];
50698                         var dup = this.store.getById(r.id);
50699                         if (dup && (dd != this.dragZone)) {
50700                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50701                         } else {
50702                                 if (data.copy) {
50703                                         this.store.insert(insertAt++, r.copy());
50704                                 } else {
50705                                         data.source.isDirtyFlag = true;
50706                                         r.store.remove(r);
50707                                         this.store.insert(insertAt++, r);
50708                                 }
50709                                 this.isDirtyFlag = true;
50710                         }
50711                 }
50712                 this.dragZone.cachedTarget = null;
50713                 return true;
50714     },
50715
50716     removeDropIndicators : function(n){
50717                 if(n){
50718                         Roo.fly(n).removeClass([
50719                                 "x-view-drag-insert-above",
50720                                 "x-view-drag-insert-below"]);
50721                         this.lastInsertClass = "_noclass";
50722                 }
50723     },
50724
50725 /**
50726  *      Utility method. Add a delete option to the DDView's context menu.
50727  *      @param {String} imageUrl The URL of the "delete" icon image.
50728  */
50729         setDeletable: function(imageUrl) {
50730                 if (!this.singleSelect && !this.multiSelect) {
50731                         this.singleSelect = true;
50732                 }
50733                 var c = this.getContextMenu();
50734                 this.contextMenu.on("itemclick", function(item) {
50735                         switch (item.id) {
50736                                 case "delete":
50737                                         this.remove(this.getSelectedIndexes());
50738                                         break;
50739                         }
50740                 }, this);
50741                 this.contextMenu.add({
50742                         icon: imageUrl,
50743                         id: "delete",
50744                         text: 'Delete'
50745                 });
50746         },
50747         
50748 /**     Return the context menu for this DDView. */
50749         getContextMenu: function() {
50750                 if (!this.contextMenu) {
50751 //                      Create the View's context menu
50752                         this.contextMenu = new Roo.menu.Menu({
50753                                 id: this.id + "-contextmenu"
50754                         });
50755                         this.el.on("contextmenu", this.showContextMenu, this);
50756                 }
50757                 return this.contextMenu;
50758         },
50759         
50760         disableContextMenu: function() {
50761                 if (this.contextMenu) {
50762                         this.el.un("contextmenu", this.showContextMenu, this);
50763                 }
50764         },
50765
50766         showContextMenu: function(e, item) {
50767         item = this.findItemFromChild(e.getTarget());
50768                 if (item) {
50769                         e.stopEvent();
50770                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50771                         this.contextMenu.showAt(e.getXY());
50772             }
50773     },
50774
50775 /**
50776  *      Remove {@link Roo.data.Record}s at the specified indices.
50777  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50778  */
50779     remove: function(selectedIndices) {
50780                 selectedIndices = [].concat(selectedIndices);
50781                 for (var i = 0; i < selectedIndices.length; i++) {
50782                         var rec = this.store.getAt(selectedIndices[i]);
50783                         this.store.remove(rec);
50784                 }
50785     },
50786
50787 /**
50788  *      Double click fires the event, but also, if this is draggable, and there is only one other
50789  *      related DropZone, it transfers the selected node.
50790  */
50791     onDblClick : function(e){
50792         var item = this.findItemFromChild(e.getTarget());
50793         if(item){
50794             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50795                 return false;
50796             }
50797             if (this.dragGroup) {
50798                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50799                     while (targets.indexOf(this.dropZone) > -1) {
50800                             targets.remove(this.dropZone);
50801                                 }
50802                     if (targets.length == 1) {
50803                                         this.dragZone.cachedTarget = null;
50804                         var el = Roo.get(targets[0].getEl());
50805                         var box = el.getBox(true);
50806                         targets[0].onNodeDrop(el.dom, {
50807                                 target: el.dom,
50808                                 xy: [box.x, box.y + box.height - 1]
50809                         }, null, this.getDragData(e));
50810                     }
50811                 }
50812         }
50813     },
50814     
50815     handleSelection: function(e) {
50816                 this.dragZone.cachedTarget = null;
50817         var item = this.findItemFromChild(e.getTarget());
50818         if (!item) {
50819                 this.clearSelections(true);
50820                 return;
50821         }
50822                 if (item && (this.multiSelect || this.singleSelect)){
50823                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50824                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50825                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50826                                 this.unselect(item);
50827                         } else {
50828                                 this.select(item, this.multiSelect && e.ctrlKey);
50829                                 this.lastSelection = item;
50830                         }
50831                 }
50832     },
50833
50834     onItemClick : function(item, index, e){
50835                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50836                         return false;
50837                 }
50838                 return true;
50839     },
50840
50841     unselect : function(nodeInfo, suppressEvent){
50842                 var node = this.getNode(nodeInfo);
50843                 if(node && this.isSelected(node)){
50844                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50845                                 Roo.fly(node).removeClass(this.selectedClass);
50846                                 this.selections.remove(node);
50847                                 if(!suppressEvent){
50848                                         this.fireEvent("selectionchange", this, this.selections);
50849                                 }
50850                         }
50851                 }
50852     }
50853 });
50854 /*
50855  * Based on:
50856  * Ext JS Library 1.1.1
50857  * Copyright(c) 2006-2007, Ext JS, LLC.
50858  *
50859  * Originally Released Under LGPL - original licence link has changed is not relivant.
50860  *
50861  * Fork - LGPL
50862  * <script type="text/javascript">
50863  */
50864  
50865 /**
50866  * @class Roo.LayoutManager
50867  * @extends Roo.util.Observable
50868  * Base class for layout managers.
50869  */
50870 Roo.LayoutManager = function(container, config){
50871     Roo.LayoutManager.superclass.constructor.call(this);
50872     this.el = Roo.get(container);
50873     // ie scrollbar fix
50874     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50875         document.body.scroll = "no";
50876     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50877         this.el.position('relative');
50878     }
50879     this.id = this.el.id;
50880     this.el.addClass("x-layout-container");
50881     /** false to disable window resize monitoring @type Boolean */
50882     this.monitorWindowResize = true;
50883     this.regions = {};
50884     this.addEvents({
50885         /**
50886          * @event layout
50887          * Fires when a layout is performed. 
50888          * @param {Roo.LayoutManager} this
50889          */
50890         "layout" : true,
50891         /**
50892          * @event regionresized
50893          * Fires when the user resizes a region. 
50894          * @param {Roo.LayoutRegion} region The resized region
50895          * @param {Number} newSize The new size (width for east/west, height for north/south)
50896          */
50897         "regionresized" : true,
50898         /**
50899          * @event regioncollapsed
50900          * Fires when a region is collapsed. 
50901          * @param {Roo.LayoutRegion} region The collapsed region
50902          */
50903         "regioncollapsed" : true,
50904         /**
50905          * @event regionexpanded
50906          * Fires when a region is expanded.  
50907          * @param {Roo.LayoutRegion} region The expanded region
50908          */
50909         "regionexpanded" : true
50910     });
50911     this.updating = false;
50912     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50913 };
50914
50915 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50916     /**
50917      * Returns true if this layout is currently being updated
50918      * @return {Boolean}
50919      */
50920     isUpdating : function(){
50921         return this.updating; 
50922     },
50923     
50924     /**
50925      * Suspend the LayoutManager from doing auto-layouts while
50926      * making multiple add or remove calls
50927      */
50928     beginUpdate : function(){
50929         this.updating = true;    
50930     },
50931     
50932     /**
50933      * Restore auto-layouts and optionally disable the manager from performing a layout
50934      * @param {Boolean} noLayout true to disable a layout update 
50935      */
50936     endUpdate : function(noLayout){
50937         this.updating = false;
50938         if(!noLayout){
50939             this.layout();
50940         }    
50941     },
50942     
50943     layout: function(){
50944         
50945     },
50946     
50947     onRegionResized : function(region, newSize){
50948         this.fireEvent("regionresized", region, newSize);
50949         this.layout();
50950     },
50951     
50952     onRegionCollapsed : function(region){
50953         this.fireEvent("regioncollapsed", region);
50954     },
50955     
50956     onRegionExpanded : function(region){
50957         this.fireEvent("regionexpanded", region);
50958     },
50959         
50960     /**
50961      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50962      * performs box-model adjustments.
50963      * @return {Object} The size as an object {width: (the width), height: (the height)}
50964      */
50965     getViewSize : function(){
50966         var size;
50967         if(this.el.dom != document.body){
50968             size = this.el.getSize();
50969         }else{
50970             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50971         }
50972         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50973         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50974         return size;
50975     },
50976     
50977     /**
50978      * Returns the Element this layout is bound to.
50979      * @return {Roo.Element}
50980      */
50981     getEl : function(){
50982         return this.el;
50983     },
50984     
50985     /**
50986      * Returns the specified region.
50987      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50988      * @return {Roo.LayoutRegion}
50989      */
50990     getRegion : function(target){
50991         return this.regions[target.toLowerCase()];
50992     },
50993     
50994     onWindowResize : function(){
50995         if(this.monitorWindowResize){
50996             this.layout();
50997         }
50998     }
50999 });/*
51000  * Based on:
51001  * Ext JS Library 1.1.1
51002  * Copyright(c) 2006-2007, Ext JS, LLC.
51003  *
51004  * Originally Released Under LGPL - original licence link has changed is not relivant.
51005  *
51006  * Fork - LGPL
51007  * <script type="text/javascript">
51008  */
51009 /**
51010  * @class Roo.BorderLayout
51011  * @extends Roo.LayoutManager
51012  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51013  * please see: <br><br>
51014  * <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>
51015  * <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>
51016  * Example:
51017  <pre><code>
51018  var layout = new Roo.BorderLayout(document.body, {
51019     north: {
51020         initialSize: 25,
51021         titlebar: false
51022     },
51023     west: {
51024         split:true,
51025         initialSize: 200,
51026         minSize: 175,
51027         maxSize: 400,
51028         titlebar: true,
51029         collapsible: true
51030     },
51031     east: {
51032         split:true,
51033         initialSize: 202,
51034         minSize: 175,
51035         maxSize: 400,
51036         titlebar: true,
51037         collapsible: true
51038     },
51039     south: {
51040         split:true,
51041         initialSize: 100,
51042         minSize: 100,
51043         maxSize: 200,
51044         titlebar: true,
51045         collapsible: true
51046     },
51047     center: {
51048         titlebar: true,
51049         autoScroll:true,
51050         resizeTabs: true,
51051         minTabWidth: 50,
51052         preferredTabWidth: 150
51053     }
51054 });
51055
51056 // shorthand
51057 var CP = Roo.ContentPanel;
51058
51059 layout.beginUpdate();
51060 layout.add("north", new CP("north", "North"));
51061 layout.add("south", new CP("south", {title: "South", closable: true}));
51062 layout.add("west", new CP("west", {title: "West"}));
51063 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51064 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51065 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51066 layout.getRegion("center").showPanel("center1");
51067 layout.endUpdate();
51068 </code></pre>
51069
51070 <b>The container the layout is rendered into can be either the body element or any other element.
51071 If it is not the body element, the container needs to either be an absolute positioned element,
51072 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51073 the container size if it is not the body element.</b>
51074
51075 * @constructor
51076 * Create a new BorderLayout
51077 * @param {String/HTMLElement/Element} container The container this layout is bound to
51078 * @param {Object} config Configuration options
51079  */
51080 Roo.BorderLayout = function(container, config){
51081     config = config || {};
51082     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51083     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51084     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51085         var target = this.factory.validRegions[i];
51086         if(config[target]){
51087             this.addRegion(target, config[target]);
51088         }
51089     }
51090 };
51091
51092 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51093     /**
51094      * Creates and adds a new region if it doesn't already exist.
51095      * @param {String} target The target region key (north, south, east, west or center).
51096      * @param {Object} config The regions config object
51097      * @return {BorderLayoutRegion} The new region
51098      */
51099     addRegion : function(target, config){
51100         if(!this.regions[target]){
51101             var r = this.factory.create(target, this, config);
51102             this.bindRegion(target, r);
51103         }
51104         return this.regions[target];
51105     },
51106
51107     // private (kinda)
51108     bindRegion : function(name, r){
51109         this.regions[name] = r;
51110         r.on("visibilitychange", this.layout, this);
51111         r.on("paneladded", this.layout, this);
51112         r.on("panelremoved", this.layout, this);
51113         r.on("invalidated", this.layout, this);
51114         r.on("resized", this.onRegionResized, this);
51115         r.on("collapsed", this.onRegionCollapsed, this);
51116         r.on("expanded", this.onRegionExpanded, this);
51117     },
51118
51119     /**
51120      * Performs a layout update.
51121      */
51122     layout : function(){
51123         if(this.updating) {
51124             return;
51125         }
51126         var size = this.getViewSize();
51127         var w = size.width;
51128         var h = size.height;
51129         var centerW = w;
51130         var centerH = h;
51131         var centerY = 0;
51132         var centerX = 0;
51133         //var x = 0, y = 0;
51134
51135         var rs = this.regions;
51136         var north = rs["north"];
51137         var south = rs["south"]; 
51138         var west = rs["west"];
51139         var east = rs["east"];
51140         var center = rs["center"];
51141         //if(this.hideOnLayout){ // not supported anymore
51142             //c.el.setStyle("display", "none");
51143         //}
51144         if(north && north.isVisible()){
51145             var b = north.getBox();
51146             var m = north.getMargins();
51147             b.width = w - (m.left+m.right);
51148             b.x = m.left;
51149             b.y = m.top;
51150             centerY = b.height + b.y + m.bottom;
51151             centerH -= centerY;
51152             north.updateBox(this.safeBox(b));
51153         }
51154         if(south && south.isVisible()){
51155             var b = south.getBox();
51156             var m = south.getMargins();
51157             b.width = w - (m.left+m.right);
51158             b.x = m.left;
51159             var totalHeight = (b.height + m.top + m.bottom);
51160             b.y = h - totalHeight + m.top;
51161             centerH -= totalHeight;
51162             south.updateBox(this.safeBox(b));
51163         }
51164         if(west && west.isVisible()){
51165             var b = west.getBox();
51166             var m = west.getMargins();
51167             b.height = centerH - (m.top+m.bottom);
51168             b.x = m.left;
51169             b.y = centerY + m.top;
51170             var totalWidth = (b.width + m.left + m.right);
51171             centerX += totalWidth;
51172             centerW -= totalWidth;
51173             west.updateBox(this.safeBox(b));
51174         }
51175         if(east && east.isVisible()){
51176             var b = east.getBox();
51177             var m = east.getMargins();
51178             b.height = centerH - (m.top+m.bottom);
51179             var totalWidth = (b.width + m.left + m.right);
51180             b.x = w - totalWidth + m.left;
51181             b.y = centerY + m.top;
51182             centerW -= totalWidth;
51183             east.updateBox(this.safeBox(b));
51184         }
51185         if(center){
51186             var m = center.getMargins();
51187             var centerBox = {
51188                 x: centerX + m.left,
51189                 y: centerY + m.top,
51190                 width: centerW - (m.left+m.right),
51191                 height: centerH - (m.top+m.bottom)
51192             };
51193             //if(this.hideOnLayout){
51194                 //center.el.setStyle("display", "block");
51195             //}
51196             center.updateBox(this.safeBox(centerBox));
51197         }
51198         this.el.repaint();
51199         this.fireEvent("layout", this);
51200     },
51201
51202     // private
51203     safeBox : function(box){
51204         box.width = Math.max(0, box.width);
51205         box.height = Math.max(0, box.height);
51206         return box;
51207     },
51208
51209     /**
51210      * Adds a ContentPanel (or subclass) to this layout.
51211      * @param {String} target The target region key (north, south, east, west or center).
51212      * @param {Roo.ContentPanel} panel The panel to add
51213      * @return {Roo.ContentPanel} The added panel
51214      */
51215     add : function(target, panel){
51216          
51217         target = target.toLowerCase();
51218         return this.regions[target].add(panel);
51219     },
51220
51221     /**
51222      * Remove a ContentPanel (or subclass) to this layout.
51223      * @param {String} target The target region key (north, south, east, west or center).
51224      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51225      * @return {Roo.ContentPanel} The removed panel
51226      */
51227     remove : function(target, panel){
51228         target = target.toLowerCase();
51229         return this.regions[target].remove(panel);
51230     },
51231
51232     /**
51233      * Searches all regions for a panel with the specified id
51234      * @param {String} panelId
51235      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51236      */
51237     findPanel : function(panelId){
51238         var rs = this.regions;
51239         for(var target in rs){
51240             if(typeof rs[target] != "function"){
51241                 var p = rs[target].getPanel(panelId);
51242                 if(p){
51243                     return p;
51244                 }
51245             }
51246         }
51247         return null;
51248     },
51249
51250     /**
51251      * Searches all regions for a panel with the specified id and activates (shows) it.
51252      * @param {String/ContentPanel} panelId The panels id or the panel itself
51253      * @return {Roo.ContentPanel} The shown panel or null
51254      */
51255     showPanel : function(panelId) {
51256       var rs = this.regions;
51257       for(var target in rs){
51258          var r = rs[target];
51259          if(typeof r != "function"){
51260             if(r.hasPanel(panelId)){
51261                return r.showPanel(panelId);
51262             }
51263          }
51264       }
51265       return null;
51266    },
51267
51268    /**
51269      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51270      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51271      */
51272     restoreState : function(provider){
51273         if(!provider){
51274             provider = Roo.state.Manager;
51275         }
51276         var sm = new Roo.LayoutStateManager();
51277         sm.init(this, provider);
51278     },
51279
51280     /**
51281      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51282      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51283      * a valid ContentPanel config object.  Example:
51284      * <pre><code>
51285 // Create the main layout
51286 var layout = new Roo.BorderLayout('main-ct', {
51287     west: {
51288         split:true,
51289         minSize: 175,
51290         titlebar: true
51291     },
51292     center: {
51293         title:'Components'
51294     }
51295 }, 'main-ct');
51296
51297 // Create and add multiple ContentPanels at once via configs
51298 layout.batchAdd({
51299    west: {
51300        id: 'source-files',
51301        autoCreate:true,
51302        title:'Ext Source Files',
51303        autoScroll:true,
51304        fitToFrame:true
51305    },
51306    center : {
51307        el: cview,
51308        autoScroll:true,
51309        fitToFrame:true,
51310        toolbar: tb,
51311        resizeEl:'cbody'
51312    }
51313 });
51314 </code></pre>
51315      * @param {Object} regions An object containing ContentPanel configs by region name
51316      */
51317     batchAdd : function(regions){
51318         this.beginUpdate();
51319         for(var rname in regions){
51320             var lr = this.regions[rname];
51321             if(lr){
51322                 this.addTypedPanels(lr, regions[rname]);
51323             }
51324         }
51325         this.endUpdate();
51326     },
51327
51328     // private
51329     addTypedPanels : function(lr, ps){
51330         if(typeof ps == 'string'){
51331             lr.add(new Roo.ContentPanel(ps));
51332         }
51333         else if(ps instanceof Array){
51334             for(var i =0, len = ps.length; i < len; i++){
51335                 this.addTypedPanels(lr, ps[i]);
51336             }
51337         }
51338         else if(!ps.events){ // raw config?
51339             var el = ps.el;
51340             delete ps.el; // prevent conflict
51341             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51342         }
51343         else {  // panel object assumed!
51344             lr.add(ps);
51345         }
51346     },
51347     /**
51348      * Adds a xtype elements to the layout.
51349      * <pre><code>
51350
51351 layout.addxtype({
51352        xtype : 'ContentPanel',
51353        region: 'west',
51354        items: [ .... ]
51355    }
51356 );
51357
51358 layout.addxtype({
51359         xtype : 'NestedLayoutPanel',
51360         region: 'west',
51361         layout: {
51362            center: { },
51363            west: { }   
51364         },
51365         items : [ ... list of content panels or nested layout panels.. ]
51366    }
51367 );
51368 </code></pre>
51369      * @param {Object} cfg Xtype definition of item to add.
51370      */
51371     addxtype : function(cfg)
51372     {
51373         // basically accepts a pannel...
51374         // can accept a layout region..!?!?
51375         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51376         
51377         if (!cfg.xtype.match(/Panel$/)) {
51378             return false;
51379         }
51380         var ret = false;
51381         
51382         if (typeof(cfg.region) == 'undefined') {
51383             Roo.log("Failed to add Panel, region was not set");
51384             Roo.log(cfg);
51385             return false;
51386         }
51387         var region = cfg.region;
51388         delete cfg.region;
51389         
51390           
51391         var xitems = [];
51392         if (cfg.items) {
51393             xitems = cfg.items;
51394             delete cfg.items;
51395         }
51396         var nb = false;
51397         
51398         switch(cfg.xtype) 
51399         {
51400             case 'ContentPanel':  // ContentPanel (el, cfg)
51401             case 'ScrollPanel':  // ContentPanel (el, cfg)
51402             case 'ViewPanel': 
51403                 if(cfg.autoCreate) {
51404                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51405                 } else {
51406                     var el = this.el.createChild();
51407                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51408                 }
51409                 
51410                 this.add(region, ret);
51411                 break;
51412             
51413             
51414             case 'TreePanel': // our new panel!
51415                 cfg.el = this.el.createChild();
51416                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51417                 this.add(region, ret);
51418                 break;
51419             
51420             case 'NestedLayoutPanel': 
51421                 // create a new Layout (which is  a Border Layout...
51422                 var el = this.el.createChild();
51423                 var clayout = cfg.layout;
51424                 delete cfg.layout;
51425                 clayout.items   = clayout.items  || [];
51426                 // replace this exitems with the clayout ones..
51427                 xitems = clayout.items;
51428                  
51429                 
51430                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51431                     cfg.background = false;
51432                 }
51433                 var layout = new Roo.BorderLayout(el, clayout);
51434                 
51435                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51436                 //console.log('adding nested layout panel '  + cfg.toSource());
51437                 this.add(region, ret);
51438                 nb = {}; /// find first...
51439                 break;
51440                 
51441             case 'GridPanel': 
51442             
51443                 // needs grid and region
51444                 
51445                 //var el = this.getRegion(region).el.createChild();
51446                 var el = this.el.createChild();
51447                 // create the grid first...
51448                 
51449                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51450                 delete cfg.grid;
51451                 if (region == 'center' && this.active ) {
51452                     cfg.background = false;
51453                 }
51454                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51455                 
51456                 this.add(region, ret);
51457                 if (cfg.background) {
51458                     ret.on('activate', function(gp) {
51459                         if (!gp.grid.rendered) {
51460                             gp.grid.render();
51461                         }
51462                     });
51463                 } else {
51464                     grid.render();
51465                 }
51466                 break;
51467            
51468            
51469            
51470                 
51471                 
51472                 
51473             default:
51474                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51475                     
51476                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51477                     this.add(region, ret);
51478                 } else {
51479                 
51480                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51481                     return null;
51482                 }
51483                 
51484              // GridPanel (grid, cfg)
51485             
51486         }
51487         this.beginUpdate();
51488         // add children..
51489         var region = '';
51490         var abn = {};
51491         Roo.each(xitems, function(i)  {
51492             region = nb && i.region ? i.region : false;
51493             
51494             var add = ret.addxtype(i);
51495            
51496             if (region) {
51497                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51498                 if (!i.background) {
51499                     abn[region] = nb[region] ;
51500                 }
51501             }
51502             
51503         });
51504         this.endUpdate();
51505
51506         // make the last non-background panel active..
51507         //if (nb) { Roo.log(abn); }
51508         if (nb) {
51509             
51510             for(var r in abn) {
51511                 region = this.getRegion(r);
51512                 if (region) {
51513                     // tried using nb[r], but it does not work..
51514                      
51515                     region.showPanel(abn[r]);
51516                    
51517                 }
51518             }
51519         }
51520         return ret;
51521         
51522     }
51523 });
51524
51525 /**
51526  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51527  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51528  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51529  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51530  * <pre><code>
51531 // shorthand
51532 var CP = Roo.ContentPanel;
51533
51534 var layout = Roo.BorderLayout.create({
51535     north: {
51536         initialSize: 25,
51537         titlebar: false,
51538         panels: [new CP("north", "North")]
51539     },
51540     west: {
51541         split:true,
51542         initialSize: 200,
51543         minSize: 175,
51544         maxSize: 400,
51545         titlebar: true,
51546         collapsible: true,
51547         panels: [new CP("west", {title: "West"})]
51548     },
51549     east: {
51550         split:true,
51551         initialSize: 202,
51552         minSize: 175,
51553         maxSize: 400,
51554         titlebar: true,
51555         collapsible: true,
51556         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51557     },
51558     south: {
51559         split:true,
51560         initialSize: 100,
51561         minSize: 100,
51562         maxSize: 200,
51563         titlebar: true,
51564         collapsible: true,
51565         panels: [new CP("south", {title: "South", closable: true})]
51566     },
51567     center: {
51568         titlebar: true,
51569         autoScroll:true,
51570         resizeTabs: true,
51571         minTabWidth: 50,
51572         preferredTabWidth: 150,
51573         panels: [
51574             new CP("center1", {title: "Close Me", closable: true}),
51575             new CP("center2", {title: "Center Panel", closable: false})
51576         ]
51577     }
51578 }, document.body);
51579
51580 layout.getRegion("center").showPanel("center1");
51581 </code></pre>
51582  * @param config
51583  * @param targetEl
51584  */
51585 Roo.BorderLayout.create = function(config, targetEl){
51586     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51587     layout.beginUpdate();
51588     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51589     for(var j = 0, jlen = regions.length; j < jlen; j++){
51590         var lr = regions[j];
51591         if(layout.regions[lr] && config[lr].panels){
51592             var r = layout.regions[lr];
51593             var ps = config[lr].panels;
51594             layout.addTypedPanels(r, ps);
51595         }
51596     }
51597     layout.endUpdate();
51598     return layout;
51599 };
51600
51601 // private
51602 Roo.BorderLayout.RegionFactory = {
51603     // private
51604     validRegions : ["north","south","east","west","center"],
51605
51606     // private
51607     create : function(target, mgr, config){
51608         target = target.toLowerCase();
51609         if(config.lightweight || config.basic){
51610             return new Roo.BasicLayoutRegion(mgr, config, target);
51611         }
51612         switch(target){
51613             case "north":
51614                 return new Roo.NorthLayoutRegion(mgr, config);
51615             case "south":
51616                 return new Roo.SouthLayoutRegion(mgr, config);
51617             case "east":
51618                 return new Roo.EastLayoutRegion(mgr, config);
51619             case "west":
51620                 return new Roo.WestLayoutRegion(mgr, config);
51621             case "center":
51622                 return new Roo.CenterLayoutRegion(mgr, config);
51623         }
51624         throw 'Layout region "'+target+'" not supported.';
51625     }
51626 };/*
51627  * Based on:
51628  * Ext JS Library 1.1.1
51629  * Copyright(c) 2006-2007, Ext JS, LLC.
51630  *
51631  * Originally Released Under LGPL - original licence link has changed is not relivant.
51632  *
51633  * Fork - LGPL
51634  * <script type="text/javascript">
51635  */
51636  
51637 /**
51638  * @class Roo.BasicLayoutRegion
51639  * @extends Roo.util.Observable
51640  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51641  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51642  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51643  */
51644 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51645     this.mgr = mgr;
51646     this.position  = pos;
51647     this.events = {
51648         /**
51649          * @scope Roo.BasicLayoutRegion
51650          */
51651         
51652         /**
51653          * @event beforeremove
51654          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51655          * @param {Roo.LayoutRegion} this
51656          * @param {Roo.ContentPanel} panel The panel
51657          * @param {Object} e The cancel event object
51658          */
51659         "beforeremove" : true,
51660         /**
51661          * @event invalidated
51662          * Fires when the layout for this region is changed.
51663          * @param {Roo.LayoutRegion} this
51664          */
51665         "invalidated" : true,
51666         /**
51667          * @event visibilitychange
51668          * Fires when this region is shown or hidden 
51669          * @param {Roo.LayoutRegion} this
51670          * @param {Boolean} visibility true or false
51671          */
51672         "visibilitychange" : true,
51673         /**
51674          * @event paneladded
51675          * Fires when a panel is added. 
51676          * @param {Roo.LayoutRegion} this
51677          * @param {Roo.ContentPanel} panel The panel
51678          */
51679         "paneladded" : true,
51680         /**
51681          * @event panelremoved
51682          * Fires when a panel is removed. 
51683          * @param {Roo.LayoutRegion} this
51684          * @param {Roo.ContentPanel} panel The panel
51685          */
51686         "panelremoved" : true,
51687         /**
51688          * @event beforecollapse
51689          * Fires when this region before collapse.
51690          * @param {Roo.LayoutRegion} this
51691          */
51692         "beforecollapse" : true,
51693         /**
51694          * @event collapsed
51695          * Fires when this region is collapsed.
51696          * @param {Roo.LayoutRegion} this
51697          */
51698         "collapsed" : true,
51699         /**
51700          * @event expanded
51701          * Fires when this region is expanded.
51702          * @param {Roo.LayoutRegion} this
51703          */
51704         "expanded" : true,
51705         /**
51706          * @event slideshow
51707          * Fires when this region is slid into view.
51708          * @param {Roo.LayoutRegion} this
51709          */
51710         "slideshow" : true,
51711         /**
51712          * @event slidehide
51713          * Fires when this region slides out of view. 
51714          * @param {Roo.LayoutRegion} this
51715          */
51716         "slidehide" : true,
51717         /**
51718          * @event panelactivated
51719          * Fires when a panel is activated. 
51720          * @param {Roo.LayoutRegion} this
51721          * @param {Roo.ContentPanel} panel The activated panel
51722          */
51723         "panelactivated" : true,
51724         /**
51725          * @event resized
51726          * Fires when the user resizes this region. 
51727          * @param {Roo.LayoutRegion} this
51728          * @param {Number} newSize The new size (width for east/west, height for north/south)
51729          */
51730         "resized" : true
51731     };
51732     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51733     this.panels = new Roo.util.MixedCollection();
51734     this.panels.getKey = this.getPanelId.createDelegate(this);
51735     this.box = null;
51736     this.activePanel = null;
51737     // ensure listeners are added...
51738     
51739     if (config.listeners || config.events) {
51740         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51741             listeners : config.listeners || {},
51742             events : config.events || {}
51743         });
51744     }
51745     
51746     if(skipConfig !== true){
51747         this.applyConfig(config);
51748     }
51749 };
51750
51751 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51752     getPanelId : function(p){
51753         return p.getId();
51754     },
51755     
51756     applyConfig : function(config){
51757         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51758         this.config = config;
51759         
51760     },
51761     
51762     /**
51763      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51764      * the width, for horizontal (north, south) the height.
51765      * @param {Number} newSize The new width or height
51766      */
51767     resizeTo : function(newSize){
51768         var el = this.el ? this.el :
51769                  (this.activePanel ? this.activePanel.getEl() : null);
51770         if(el){
51771             switch(this.position){
51772                 case "east":
51773                 case "west":
51774                     el.setWidth(newSize);
51775                     this.fireEvent("resized", this, newSize);
51776                 break;
51777                 case "north":
51778                 case "south":
51779                     el.setHeight(newSize);
51780                     this.fireEvent("resized", this, newSize);
51781                 break;                
51782             }
51783         }
51784     },
51785     
51786     getBox : function(){
51787         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51788     },
51789     
51790     getMargins : function(){
51791         return this.margins;
51792     },
51793     
51794     updateBox : function(box){
51795         this.box = box;
51796         var el = this.activePanel.getEl();
51797         el.dom.style.left = box.x + "px";
51798         el.dom.style.top = box.y + "px";
51799         this.activePanel.setSize(box.width, box.height);
51800     },
51801     
51802     /**
51803      * Returns the container element for this region.
51804      * @return {Roo.Element}
51805      */
51806     getEl : function(){
51807         return this.activePanel;
51808     },
51809     
51810     /**
51811      * Returns true if this region is currently visible.
51812      * @return {Boolean}
51813      */
51814     isVisible : function(){
51815         return this.activePanel ? true : false;
51816     },
51817     
51818     setActivePanel : function(panel){
51819         panel = this.getPanel(panel);
51820         if(this.activePanel && this.activePanel != panel){
51821             this.activePanel.setActiveState(false);
51822             this.activePanel.getEl().setLeftTop(-10000,-10000);
51823         }
51824         this.activePanel = panel;
51825         panel.setActiveState(true);
51826         if(this.box){
51827             panel.setSize(this.box.width, this.box.height);
51828         }
51829         this.fireEvent("panelactivated", this, panel);
51830         this.fireEvent("invalidated");
51831     },
51832     
51833     /**
51834      * Show the specified panel.
51835      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51836      * @return {Roo.ContentPanel} The shown panel or null
51837      */
51838     showPanel : function(panel){
51839         if(panel = this.getPanel(panel)){
51840             this.setActivePanel(panel);
51841         }
51842         return panel;
51843     },
51844     
51845     /**
51846      * Get the active panel for this region.
51847      * @return {Roo.ContentPanel} The active panel or null
51848      */
51849     getActivePanel : function(){
51850         return this.activePanel;
51851     },
51852     
51853     /**
51854      * Add the passed ContentPanel(s)
51855      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51856      * @return {Roo.ContentPanel} The panel added (if only one was added)
51857      */
51858     add : function(panel){
51859         if(arguments.length > 1){
51860             for(var i = 0, len = arguments.length; i < len; i++) {
51861                 this.add(arguments[i]);
51862             }
51863             return null;
51864         }
51865         if(this.hasPanel(panel)){
51866             this.showPanel(panel);
51867             return panel;
51868         }
51869         var el = panel.getEl();
51870         if(el.dom.parentNode != this.mgr.el.dom){
51871             this.mgr.el.dom.appendChild(el.dom);
51872         }
51873         if(panel.setRegion){
51874             panel.setRegion(this);
51875         }
51876         this.panels.add(panel);
51877         el.setStyle("position", "absolute");
51878         if(!panel.background){
51879             this.setActivePanel(panel);
51880             if(this.config.initialSize && this.panels.getCount()==1){
51881                 this.resizeTo(this.config.initialSize);
51882             }
51883         }
51884         this.fireEvent("paneladded", this, panel);
51885         return panel;
51886     },
51887     
51888     /**
51889      * Returns true if the panel is in this region.
51890      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51891      * @return {Boolean}
51892      */
51893     hasPanel : function(panel){
51894         if(typeof panel == "object"){ // must be panel obj
51895             panel = panel.getId();
51896         }
51897         return this.getPanel(panel) ? true : false;
51898     },
51899     
51900     /**
51901      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51902      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51903      * @param {Boolean} preservePanel Overrides the config preservePanel option
51904      * @return {Roo.ContentPanel} The panel that was removed
51905      */
51906     remove : function(panel, preservePanel){
51907         panel = this.getPanel(panel);
51908         if(!panel){
51909             return null;
51910         }
51911         var e = {};
51912         this.fireEvent("beforeremove", this, panel, e);
51913         if(e.cancel === true){
51914             return null;
51915         }
51916         var panelId = panel.getId();
51917         this.panels.removeKey(panelId);
51918         return panel;
51919     },
51920     
51921     /**
51922      * Returns the panel specified or null if it's not in this region.
51923      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51924      * @return {Roo.ContentPanel}
51925      */
51926     getPanel : function(id){
51927         if(typeof id == "object"){ // must be panel obj
51928             return id;
51929         }
51930         return this.panels.get(id);
51931     },
51932     
51933     /**
51934      * Returns this regions position (north/south/east/west/center).
51935      * @return {String} 
51936      */
51937     getPosition: function(){
51938         return this.position;    
51939     }
51940 });/*
51941  * Based on:
51942  * Ext JS Library 1.1.1
51943  * Copyright(c) 2006-2007, Ext JS, LLC.
51944  *
51945  * Originally Released Under LGPL - original licence link has changed is not relivant.
51946  *
51947  * Fork - LGPL
51948  * <script type="text/javascript">
51949  */
51950  
51951 /**
51952  * @class Roo.LayoutRegion
51953  * @extends Roo.BasicLayoutRegion
51954  * This class represents a region in a layout manager.
51955  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51956  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51957  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51958  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51959  * @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})
51960  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51961  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51962  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51963  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51964  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51965  * @cfg {String}    title           The title for the region (overrides panel titles)
51966  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51967  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51968  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51969  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51970  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51971  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51972  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51973  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51974  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51975  * @cfg {Boolean}   showPin         True to show a pin button
51976  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51977  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51978  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51979  * @cfg {Number}    width           For East/West panels
51980  * @cfg {Number}    height          For North/South panels
51981  * @cfg {Boolean}   split           To show the splitter
51982  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51983  */
51984 Roo.LayoutRegion = function(mgr, config, pos){
51985     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51986     var dh = Roo.DomHelper;
51987     /** This region's container element 
51988     * @type Roo.Element */
51989     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51990     /** This region's title element 
51991     * @type Roo.Element */
51992
51993     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51994         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51995         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51996     ]}, true);
51997     this.titleEl.enableDisplayMode();
51998     /** This region's title text element 
51999     * @type HTMLElement */
52000     this.titleTextEl = this.titleEl.dom.firstChild;
52001     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52002     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52003     this.closeBtn.enableDisplayMode();
52004     this.closeBtn.on("click", this.closeClicked, this);
52005     this.closeBtn.hide();
52006
52007     this.createBody(config);
52008     this.visible = true;
52009     this.collapsed = false;
52010
52011     if(config.hideWhenEmpty){
52012         this.hide();
52013         this.on("paneladded", this.validateVisibility, this);
52014         this.on("panelremoved", this.validateVisibility, this);
52015     }
52016     this.applyConfig(config);
52017 };
52018
52019 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52020
52021     createBody : function(){
52022         /** This region's body element 
52023         * @type Roo.Element */
52024         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52025     },
52026
52027     applyConfig : function(c){
52028         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52029             var dh = Roo.DomHelper;
52030             if(c.titlebar !== false){
52031                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52032                 this.collapseBtn.on("click", this.collapse, this);
52033                 this.collapseBtn.enableDisplayMode();
52034
52035                 if(c.showPin === true || this.showPin){
52036                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52037                     this.stickBtn.enableDisplayMode();
52038                     this.stickBtn.on("click", this.expand, this);
52039                     this.stickBtn.hide();
52040                 }
52041             }
52042             /** This region's collapsed element
52043             * @type Roo.Element */
52044             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52045                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52046             ]}, true);
52047             if(c.floatable !== false){
52048                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52049                this.collapsedEl.on("click", this.collapseClick, this);
52050             }
52051
52052             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52053                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52054                    id: "message", unselectable: "on", style:{"float":"left"}});
52055                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52056              }
52057             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52058             this.expandBtn.on("click", this.expand, this);
52059         }
52060         if(this.collapseBtn){
52061             this.collapseBtn.setVisible(c.collapsible == true);
52062         }
52063         this.cmargins = c.cmargins || this.cmargins ||
52064                          (this.position == "west" || this.position == "east" ?
52065                              {top: 0, left: 2, right:2, bottom: 0} :
52066                              {top: 2, left: 0, right:0, bottom: 2});
52067         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52068         this.bottomTabs = c.tabPosition != "top";
52069         this.autoScroll = c.autoScroll || false;
52070         if(this.autoScroll){
52071             this.bodyEl.setStyle("overflow", "auto");
52072         }else{
52073             this.bodyEl.setStyle("overflow", "hidden");
52074         }
52075         //if(c.titlebar !== false){
52076             if((!c.titlebar && !c.title) || c.titlebar === false){
52077                 this.titleEl.hide();
52078             }else{
52079                 this.titleEl.show();
52080                 if(c.title){
52081                     this.titleTextEl.innerHTML = c.title;
52082                 }
52083             }
52084         //}
52085         this.duration = c.duration || .30;
52086         this.slideDuration = c.slideDuration || .45;
52087         this.config = c;
52088         if(c.collapsed){
52089             this.collapse(true);
52090         }
52091         if(c.hidden){
52092             this.hide();
52093         }
52094     },
52095     /**
52096      * Returns true if this region is currently visible.
52097      * @return {Boolean}
52098      */
52099     isVisible : function(){
52100         return this.visible;
52101     },
52102
52103     /**
52104      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52105      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52106      */
52107     setCollapsedTitle : function(title){
52108         title = title || "&#160;";
52109         if(this.collapsedTitleTextEl){
52110             this.collapsedTitleTextEl.innerHTML = title;
52111         }
52112     },
52113
52114     getBox : function(){
52115         var b;
52116         if(!this.collapsed){
52117             b = this.el.getBox(false, true);
52118         }else{
52119             b = this.collapsedEl.getBox(false, true);
52120         }
52121         return b;
52122     },
52123
52124     getMargins : function(){
52125         return this.collapsed ? this.cmargins : this.margins;
52126     },
52127
52128     highlight : function(){
52129         this.el.addClass("x-layout-panel-dragover");
52130     },
52131
52132     unhighlight : function(){
52133         this.el.removeClass("x-layout-panel-dragover");
52134     },
52135
52136     updateBox : function(box){
52137         this.box = box;
52138         if(!this.collapsed){
52139             this.el.dom.style.left = box.x + "px";
52140             this.el.dom.style.top = box.y + "px";
52141             this.updateBody(box.width, box.height);
52142         }else{
52143             this.collapsedEl.dom.style.left = box.x + "px";
52144             this.collapsedEl.dom.style.top = box.y + "px";
52145             this.collapsedEl.setSize(box.width, box.height);
52146         }
52147         if(this.tabs){
52148             this.tabs.autoSizeTabs();
52149         }
52150     },
52151
52152     updateBody : function(w, h){
52153         if(w !== null){
52154             this.el.setWidth(w);
52155             w -= this.el.getBorderWidth("rl");
52156             if(this.config.adjustments){
52157                 w += this.config.adjustments[0];
52158             }
52159         }
52160         if(h !== null){
52161             this.el.setHeight(h);
52162             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52163             h -= this.el.getBorderWidth("tb");
52164             if(this.config.adjustments){
52165                 h += this.config.adjustments[1];
52166             }
52167             this.bodyEl.setHeight(h);
52168             if(this.tabs){
52169                 h = this.tabs.syncHeight(h);
52170             }
52171         }
52172         if(this.panelSize){
52173             w = w !== null ? w : this.panelSize.width;
52174             h = h !== null ? h : this.panelSize.height;
52175         }
52176         if(this.activePanel){
52177             var el = this.activePanel.getEl();
52178             w = w !== null ? w : el.getWidth();
52179             h = h !== null ? h : el.getHeight();
52180             this.panelSize = {width: w, height: h};
52181             this.activePanel.setSize(w, h);
52182         }
52183         if(Roo.isIE && this.tabs){
52184             this.tabs.el.repaint();
52185         }
52186     },
52187
52188     /**
52189      * Returns the container element for this region.
52190      * @return {Roo.Element}
52191      */
52192     getEl : function(){
52193         return this.el;
52194     },
52195
52196     /**
52197      * Hides this region.
52198      */
52199     hide : function(){
52200         if(!this.collapsed){
52201             this.el.dom.style.left = "-2000px";
52202             this.el.hide();
52203         }else{
52204             this.collapsedEl.dom.style.left = "-2000px";
52205             this.collapsedEl.hide();
52206         }
52207         this.visible = false;
52208         this.fireEvent("visibilitychange", this, false);
52209     },
52210
52211     /**
52212      * Shows this region if it was previously hidden.
52213      */
52214     show : function(){
52215         if(!this.collapsed){
52216             this.el.show();
52217         }else{
52218             this.collapsedEl.show();
52219         }
52220         this.visible = true;
52221         this.fireEvent("visibilitychange", this, true);
52222     },
52223
52224     closeClicked : function(){
52225         if(this.activePanel){
52226             this.remove(this.activePanel);
52227         }
52228     },
52229
52230     collapseClick : function(e){
52231         if(this.isSlid){
52232            e.stopPropagation();
52233            this.slideIn();
52234         }else{
52235            e.stopPropagation();
52236            this.slideOut();
52237         }
52238     },
52239
52240     /**
52241      * Collapses this region.
52242      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52243      */
52244     collapse : function(skipAnim, skipCheck = false){
52245         if(this.collapsed) {
52246             return;
52247         }
52248         
52249         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52250             
52251             this.collapsed = true;
52252             if(this.split){
52253                 this.split.el.hide();
52254             }
52255             if(this.config.animate && skipAnim !== true){
52256                 this.fireEvent("invalidated", this);
52257                 this.animateCollapse();
52258             }else{
52259                 this.el.setLocation(-20000,-20000);
52260                 this.el.hide();
52261                 this.collapsedEl.show();
52262                 this.fireEvent("collapsed", this);
52263                 this.fireEvent("invalidated", this);
52264             }
52265         }
52266         
52267     },
52268
52269     animateCollapse : function(){
52270         // overridden
52271     },
52272
52273     /**
52274      * Expands this region if it was previously collapsed.
52275      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52276      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52277      */
52278     expand : function(e, skipAnim){
52279         if(e) {
52280             e.stopPropagation();
52281         }
52282         if(!this.collapsed || this.el.hasActiveFx()) {
52283             return;
52284         }
52285         if(this.isSlid){
52286             this.afterSlideIn();
52287             skipAnim = true;
52288         }
52289         this.collapsed = false;
52290         if(this.config.animate && skipAnim !== true){
52291             this.animateExpand();
52292         }else{
52293             this.el.show();
52294             if(this.split){
52295                 this.split.el.show();
52296             }
52297             this.collapsedEl.setLocation(-2000,-2000);
52298             this.collapsedEl.hide();
52299             this.fireEvent("invalidated", this);
52300             this.fireEvent("expanded", this);
52301         }
52302     },
52303
52304     animateExpand : function(){
52305         // overridden
52306     },
52307
52308     initTabs : function()
52309     {
52310         this.bodyEl.setStyle("overflow", "hidden");
52311         var ts = new Roo.TabPanel(
52312                 this.bodyEl.dom,
52313                 {
52314                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52315                     disableTooltips: this.config.disableTabTips,
52316                     toolbar : this.config.toolbar
52317                 }
52318         );
52319         if(this.config.hideTabs){
52320             ts.stripWrap.setDisplayed(false);
52321         }
52322         this.tabs = ts;
52323         ts.resizeTabs = this.config.resizeTabs === true;
52324         ts.minTabWidth = this.config.minTabWidth || 40;
52325         ts.maxTabWidth = this.config.maxTabWidth || 250;
52326         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52327         ts.monitorResize = false;
52328         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52329         ts.bodyEl.addClass('x-layout-tabs-body');
52330         this.panels.each(this.initPanelAsTab, this);
52331     },
52332
52333     initPanelAsTab : function(panel){
52334         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52335                     this.config.closeOnTab && panel.isClosable());
52336         if(panel.tabTip !== undefined){
52337             ti.setTooltip(panel.tabTip);
52338         }
52339         ti.on("activate", function(){
52340               this.setActivePanel(panel);
52341         }, this);
52342         if(this.config.closeOnTab){
52343             ti.on("beforeclose", function(t, e){
52344                 e.cancel = true;
52345                 this.remove(panel);
52346             }, this);
52347         }
52348         return ti;
52349     },
52350
52351     updatePanelTitle : function(panel, title){
52352         if(this.activePanel == panel){
52353             this.updateTitle(title);
52354         }
52355         if(this.tabs){
52356             var ti = this.tabs.getTab(panel.getEl().id);
52357             ti.setText(title);
52358             if(panel.tabTip !== undefined){
52359                 ti.setTooltip(panel.tabTip);
52360             }
52361         }
52362     },
52363
52364     updateTitle : function(title){
52365         if(this.titleTextEl && !this.config.title){
52366             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52367         }
52368     },
52369
52370     setActivePanel : function(panel){
52371         panel = this.getPanel(panel);
52372         if(this.activePanel && this.activePanel != panel){
52373             this.activePanel.setActiveState(false);
52374         }
52375         this.activePanel = panel;
52376         panel.setActiveState(true);
52377         if(this.panelSize){
52378             panel.setSize(this.panelSize.width, this.panelSize.height);
52379         }
52380         if(this.closeBtn){
52381             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52382         }
52383         this.updateTitle(panel.getTitle());
52384         if(this.tabs){
52385             this.fireEvent("invalidated", this);
52386         }
52387         this.fireEvent("panelactivated", this, panel);
52388     },
52389
52390     /**
52391      * Shows the specified panel.
52392      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52393      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52394      */
52395     showPanel : function(panel)
52396     {
52397         panel = this.getPanel(panel);
52398         if(panel){
52399             if(this.tabs){
52400                 var tab = this.tabs.getTab(panel.getEl().id);
52401                 if(tab.isHidden()){
52402                     this.tabs.unhideTab(tab.id);
52403                 }
52404                 tab.activate();
52405             }else{
52406                 this.setActivePanel(panel);
52407             }
52408         }
52409         return panel;
52410     },
52411
52412     /**
52413      * Get the active panel for this region.
52414      * @return {Roo.ContentPanel} The active panel or null
52415      */
52416     getActivePanel : function(){
52417         return this.activePanel;
52418     },
52419
52420     validateVisibility : function(){
52421         if(this.panels.getCount() < 1){
52422             this.updateTitle("&#160;");
52423             this.closeBtn.hide();
52424             this.hide();
52425         }else{
52426             if(!this.isVisible()){
52427                 this.show();
52428             }
52429         }
52430     },
52431
52432     /**
52433      * Adds the passed ContentPanel(s) to this region.
52434      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52435      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52436      */
52437     add : function(panel){
52438         if(arguments.length > 1){
52439             for(var i = 0, len = arguments.length; i < len; i++) {
52440                 this.add(arguments[i]);
52441             }
52442             return null;
52443         }
52444         if(this.hasPanel(panel)){
52445             this.showPanel(panel);
52446             return panel;
52447         }
52448         panel.setRegion(this);
52449         this.panels.add(panel);
52450         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52451             this.bodyEl.dom.appendChild(panel.getEl().dom);
52452             if(panel.background !== true){
52453                 this.setActivePanel(panel);
52454             }
52455             this.fireEvent("paneladded", this, panel);
52456             return panel;
52457         }
52458         if(!this.tabs){
52459             this.initTabs();
52460         }else{
52461             this.initPanelAsTab(panel);
52462         }
52463         if(panel.background !== true){
52464             this.tabs.activate(panel.getEl().id);
52465         }
52466         this.fireEvent("paneladded", this, panel);
52467         return panel;
52468     },
52469
52470     /**
52471      * Hides the tab for the specified panel.
52472      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52473      */
52474     hidePanel : function(panel){
52475         if(this.tabs && (panel = this.getPanel(panel))){
52476             this.tabs.hideTab(panel.getEl().id);
52477         }
52478     },
52479
52480     /**
52481      * Unhides the tab for a previously hidden panel.
52482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52483      */
52484     unhidePanel : function(panel){
52485         if(this.tabs && (panel = this.getPanel(panel))){
52486             this.tabs.unhideTab(panel.getEl().id);
52487         }
52488     },
52489
52490     clearPanels : function(){
52491         while(this.panels.getCount() > 0){
52492              this.remove(this.panels.first());
52493         }
52494     },
52495
52496     /**
52497      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52498      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52499      * @param {Boolean} preservePanel Overrides the config preservePanel option
52500      * @return {Roo.ContentPanel} The panel that was removed
52501      */
52502     remove : function(panel, preservePanel){
52503         panel = this.getPanel(panel);
52504         if(!panel){
52505             return null;
52506         }
52507         var e = {};
52508         this.fireEvent("beforeremove", this, panel, e);
52509         if(e.cancel === true){
52510             return null;
52511         }
52512         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52513         var panelId = panel.getId();
52514         this.panels.removeKey(panelId);
52515         if(preservePanel){
52516             document.body.appendChild(panel.getEl().dom);
52517         }
52518         if(this.tabs){
52519             this.tabs.removeTab(panel.getEl().id);
52520         }else if (!preservePanel){
52521             this.bodyEl.dom.removeChild(panel.getEl().dom);
52522         }
52523         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52524             var p = this.panels.first();
52525             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52526             tempEl.appendChild(p.getEl().dom);
52527             this.bodyEl.update("");
52528             this.bodyEl.dom.appendChild(p.getEl().dom);
52529             tempEl = null;
52530             this.updateTitle(p.getTitle());
52531             this.tabs = null;
52532             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52533             this.setActivePanel(p);
52534         }
52535         panel.setRegion(null);
52536         if(this.activePanel == panel){
52537             this.activePanel = null;
52538         }
52539         if(this.config.autoDestroy !== false && preservePanel !== true){
52540             try{panel.destroy();}catch(e){}
52541         }
52542         this.fireEvent("panelremoved", this, panel);
52543         return panel;
52544     },
52545
52546     /**
52547      * Returns the TabPanel component used by this region
52548      * @return {Roo.TabPanel}
52549      */
52550     getTabs : function(){
52551         return this.tabs;
52552     },
52553
52554     createTool : function(parentEl, className){
52555         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52556             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52557         btn.addClassOnOver("x-layout-tools-button-over");
52558         return btn;
52559     }
52560 });/*
52561  * Based on:
52562  * Ext JS Library 1.1.1
52563  * Copyright(c) 2006-2007, Ext JS, LLC.
52564  *
52565  * Originally Released Under LGPL - original licence link has changed is not relivant.
52566  *
52567  * Fork - LGPL
52568  * <script type="text/javascript">
52569  */
52570  
52571
52572
52573 /**
52574  * @class Roo.SplitLayoutRegion
52575  * @extends Roo.LayoutRegion
52576  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52577  */
52578 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52579     this.cursor = cursor;
52580     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52581 };
52582
52583 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52584     splitTip : "Drag to resize.",
52585     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52586     useSplitTips : false,
52587
52588     applyConfig : function(config){
52589         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52590         if(config.split){
52591             if(!this.split){
52592                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52593                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52594                 /** The SplitBar for this region 
52595                 * @type Roo.SplitBar */
52596                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52597                 this.split.on("moved", this.onSplitMove, this);
52598                 this.split.useShim = config.useShim === true;
52599                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52600                 if(this.useSplitTips){
52601                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52602                 }
52603                 if(config.collapsible){
52604                     this.split.el.on("dblclick", this.collapse,  this);
52605                 }
52606             }
52607             if(typeof config.minSize != "undefined"){
52608                 this.split.minSize = config.minSize;
52609             }
52610             if(typeof config.maxSize != "undefined"){
52611                 this.split.maxSize = config.maxSize;
52612             }
52613             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52614                 this.hideSplitter();
52615             }
52616         }
52617     },
52618
52619     getHMaxSize : function(){
52620          var cmax = this.config.maxSize || 10000;
52621          var center = this.mgr.getRegion("center");
52622          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52623     },
52624
52625     getVMaxSize : function(){
52626          var cmax = this.config.maxSize || 10000;
52627          var center = this.mgr.getRegion("center");
52628          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52629     },
52630
52631     onSplitMove : function(split, newSize){
52632         this.fireEvent("resized", this, newSize);
52633     },
52634     
52635     /** 
52636      * Returns the {@link Roo.SplitBar} for this region.
52637      * @return {Roo.SplitBar}
52638      */
52639     getSplitBar : function(){
52640         return this.split;
52641     },
52642     
52643     hide : function(){
52644         this.hideSplitter();
52645         Roo.SplitLayoutRegion.superclass.hide.call(this);
52646     },
52647
52648     hideSplitter : function(){
52649         if(this.split){
52650             this.split.el.setLocation(-2000,-2000);
52651             this.split.el.hide();
52652         }
52653     },
52654
52655     show : function(){
52656         if(this.split){
52657             this.split.el.show();
52658         }
52659         Roo.SplitLayoutRegion.superclass.show.call(this);
52660     },
52661     
52662     beforeSlide: function(){
52663         if(Roo.isGecko){// firefox overflow auto bug workaround
52664             this.bodyEl.clip();
52665             if(this.tabs) {
52666                 this.tabs.bodyEl.clip();
52667             }
52668             if(this.activePanel){
52669                 this.activePanel.getEl().clip();
52670                 
52671                 if(this.activePanel.beforeSlide){
52672                     this.activePanel.beforeSlide();
52673                 }
52674             }
52675         }
52676     },
52677     
52678     afterSlide : function(){
52679         if(Roo.isGecko){// firefox overflow auto bug workaround
52680             this.bodyEl.unclip();
52681             if(this.tabs) {
52682                 this.tabs.bodyEl.unclip();
52683             }
52684             if(this.activePanel){
52685                 this.activePanel.getEl().unclip();
52686                 if(this.activePanel.afterSlide){
52687                     this.activePanel.afterSlide();
52688                 }
52689             }
52690         }
52691     },
52692
52693     initAutoHide : function(){
52694         if(this.autoHide !== false){
52695             if(!this.autoHideHd){
52696                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52697                 this.autoHideHd = {
52698                     "mouseout": function(e){
52699                         if(!e.within(this.el, true)){
52700                             st.delay(500);
52701                         }
52702                     },
52703                     "mouseover" : function(e){
52704                         st.cancel();
52705                     },
52706                     scope : this
52707                 };
52708             }
52709             this.el.on(this.autoHideHd);
52710         }
52711     },
52712
52713     clearAutoHide : function(){
52714         if(this.autoHide !== false){
52715             this.el.un("mouseout", this.autoHideHd.mouseout);
52716             this.el.un("mouseover", this.autoHideHd.mouseover);
52717         }
52718     },
52719
52720     clearMonitor : function(){
52721         Roo.get(document).un("click", this.slideInIf, this);
52722     },
52723
52724     // these names are backwards but not changed for compat
52725     slideOut : function(){
52726         if(this.isSlid || this.el.hasActiveFx()){
52727             return;
52728         }
52729         this.isSlid = true;
52730         if(this.collapseBtn){
52731             this.collapseBtn.hide();
52732         }
52733         this.closeBtnState = this.closeBtn.getStyle('display');
52734         this.closeBtn.hide();
52735         if(this.stickBtn){
52736             this.stickBtn.show();
52737         }
52738         this.el.show();
52739         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52740         this.beforeSlide();
52741         this.el.setStyle("z-index", 10001);
52742         this.el.slideIn(this.getSlideAnchor(), {
52743             callback: function(){
52744                 this.afterSlide();
52745                 this.initAutoHide();
52746                 Roo.get(document).on("click", this.slideInIf, this);
52747                 this.fireEvent("slideshow", this);
52748             },
52749             scope: this,
52750             block: true
52751         });
52752     },
52753
52754     afterSlideIn : function(){
52755         this.clearAutoHide();
52756         this.isSlid = false;
52757         this.clearMonitor();
52758         this.el.setStyle("z-index", "");
52759         if(this.collapseBtn){
52760             this.collapseBtn.show();
52761         }
52762         this.closeBtn.setStyle('display', this.closeBtnState);
52763         if(this.stickBtn){
52764             this.stickBtn.hide();
52765         }
52766         this.fireEvent("slidehide", this);
52767     },
52768
52769     slideIn : function(cb){
52770         if(!this.isSlid || this.el.hasActiveFx()){
52771             Roo.callback(cb);
52772             return;
52773         }
52774         this.isSlid = false;
52775         this.beforeSlide();
52776         this.el.slideOut(this.getSlideAnchor(), {
52777             callback: function(){
52778                 this.el.setLeftTop(-10000, -10000);
52779                 this.afterSlide();
52780                 this.afterSlideIn();
52781                 Roo.callback(cb);
52782             },
52783             scope: this,
52784             block: true
52785         });
52786     },
52787     
52788     slideInIf : function(e){
52789         if(!e.within(this.el)){
52790             this.slideIn();
52791         }
52792     },
52793
52794     animateCollapse : function(){
52795         this.beforeSlide();
52796         this.el.setStyle("z-index", 20000);
52797         var anchor = this.getSlideAnchor();
52798         this.el.slideOut(anchor, {
52799             callback : function(){
52800                 this.el.setStyle("z-index", "");
52801                 this.collapsedEl.slideIn(anchor, {duration:.3});
52802                 this.afterSlide();
52803                 this.el.setLocation(-10000,-10000);
52804                 this.el.hide();
52805                 this.fireEvent("collapsed", this);
52806             },
52807             scope: this,
52808             block: true
52809         });
52810     },
52811
52812     animateExpand : function(){
52813         this.beforeSlide();
52814         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52815         this.el.setStyle("z-index", 20000);
52816         this.collapsedEl.hide({
52817             duration:.1
52818         });
52819         this.el.slideIn(this.getSlideAnchor(), {
52820             callback : function(){
52821                 this.el.setStyle("z-index", "");
52822                 this.afterSlide();
52823                 if(this.split){
52824                     this.split.el.show();
52825                 }
52826                 this.fireEvent("invalidated", this);
52827                 this.fireEvent("expanded", this);
52828             },
52829             scope: this,
52830             block: true
52831         });
52832     },
52833
52834     anchors : {
52835         "west" : "left",
52836         "east" : "right",
52837         "north" : "top",
52838         "south" : "bottom"
52839     },
52840
52841     sanchors : {
52842         "west" : "l",
52843         "east" : "r",
52844         "north" : "t",
52845         "south" : "b"
52846     },
52847
52848     canchors : {
52849         "west" : "tl-tr",
52850         "east" : "tr-tl",
52851         "north" : "tl-bl",
52852         "south" : "bl-tl"
52853     },
52854
52855     getAnchor : function(){
52856         return this.anchors[this.position];
52857     },
52858
52859     getCollapseAnchor : function(){
52860         return this.canchors[this.position];
52861     },
52862
52863     getSlideAnchor : function(){
52864         return this.sanchors[this.position];
52865     },
52866
52867     getAlignAdj : function(){
52868         var cm = this.cmargins;
52869         switch(this.position){
52870             case "west":
52871                 return [0, 0];
52872             break;
52873             case "east":
52874                 return [0, 0];
52875             break;
52876             case "north":
52877                 return [0, 0];
52878             break;
52879             case "south":
52880                 return [0, 0];
52881             break;
52882         }
52883     },
52884
52885     getExpandAdj : function(){
52886         var c = this.collapsedEl, cm = this.cmargins;
52887         switch(this.position){
52888             case "west":
52889                 return [-(cm.right+c.getWidth()+cm.left), 0];
52890             break;
52891             case "east":
52892                 return [cm.right+c.getWidth()+cm.left, 0];
52893             break;
52894             case "north":
52895                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52896             break;
52897             case "south":
52898                 return [0, cm.top+cm.bottom+c.getHeight()];
52899             break;
52900         }
52901     }
52902 });/*
52903  * Based on:
52904  * Ext JS Library 1.1.1
52905  * Copyright(c) 2006-2007, Ext JS, LLC.
52906  *
52907  * Originally Released Under LGPL - original licence link has changed is not relivant.
52908  *
52909  * Fork - LGPL
52910  * <script type="text/javascript">
52911  */
52912 /*
52913  * These classes are private internal classes
52914  */
52915 Roo.CenterLayoutRegion = function(mgr, config){
52916     Roo.LayoutRegion.call(this, mgr, config, "center");
52917     this.visible = true;
52918     this.minWidth = config.minWidth || 20;
52919     this.minHeight = config.minHeight || 20;
52920 };
52921
52922 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52923     hide : function(){
52924         // center panel can't be hidden
52925     },
52926     
52927     show : function(){
52928         // center panel can't be hidden
52929     },
52930     
52931     getMinWidth: function(){
52932         return this.minWidth;
52933     },
52934     
52935     getMinHeight: function(){
52936         return this.minHeight;
52937     }
52938 });
52939
52940
52941 Roo.NorthLayoutRegion = function(mgr, config){
52942     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52943     if(this.split){
52944         this.split.placement = Roo.SplitBar.TOP;
52945         this.split.orientation = Roo.SplitBar.VERTICAL;
52946         this.split.el.addClass("x-layout-split-v");
52947     }
52948     var size = config.initialSize || config.height;
52949     if(typeof size != "undefined"){
52950         this.el.setHeight(size);
52951     }
52952 };
52953 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52954     orientation: Roo.SplitBar.VERTICAL,
52955     getBox : function(){
52956         if(this.collapsed){
52957             return this.collapsedEl.getBox();
52958         }
52959         var box = this.el.getBox();
52960         if(this.split){
52961             box.height += this.split.el.getHeight();
52962         }
52963         return box;
52964     },
52965     
52966     updateBox : function(box){
52967         if(this.split && !this.collapsed){
52968             box.height -= this.split.el.getHeight();
52969             this.split.el.setLeft(box.x);
52970             this.split.el.setTop(box.y+box.height);
52971             this.split.el.setWidth(box.width);
52972         }
52973         if(this.collapsed){
52974             this.updateBody(box.width, null);
52975         }
52976         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52977     }
52978 });
52979
52980 Roo.SouthLayoutRegion = function(mgr, config){
52981     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52982     if(this.split){
52983         this.split.placement = Roo.SplitBar.BOTTOM;
52984         this.split.orientation = Roo.SplitBar.VERTICAL;
52985         this.split.el.addClass("x-layout-split-v");
52986     }
52987     var size = config.initialSize || config.height;
52988     if(typeof size != "undefined"){
52989         this.el.setHeight(size);
52990     }
52991 };
52992 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52993     orientation: Roo.SplitBar.VERTICAL,
52994     getBox : function(){
52995         if(this.collapsed){
52996             return this.collapsedEl.getBox();
52997         }
52998         var box = this.el.getBox();
52999         if(this.split){
53000             var sh = this.split.el.getHeight();
53001             box.height += sh;
53002             box.y -= sh;
53003         }
53004         return box;
53005     },
53006     
53007     updateBox : function(box){
53008         if(this.split && !this.collapsed){
53009             var sh = this.split.el.getHeight();
53010             box.height -= sh;
53011             box.y += sh;
53012             this.split.el.setLeft(box.x);
53013             this.split.el.setTop(box.y-sh);
53014             this.split.el.setWidth(box.width);
53015         }
53016         if(this.collapsed){
53017             this.updateBody(box.width, null);
53018         }
53019         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53020     }
53021 });
53022
53023 Roo.EastLayoutRegion = function(mgr, config){
53024     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53025     if(this.split){
53026         this.split.placement = Roo.SplitBar.RIGHT;
53027         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53028         this.split.el.addClass("x-layout-split-h");
53029     }
53030     var size = config.initialSize || config.width;
53031     if(typeof size != "undefined"){
53032         this.el.setWidth(size);
53033     }
53034 };
53035 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53036     orientation: Roo.SplitBar.HORIZONTAL,
53037     getBox : function(){
53038         if(this.collapsed){
53039             return this.collapsedEl.getBox();
53040         }
53041         var box = this.el.getBox();
53042         if(this.split){
53043             var sw = this.split.el.getWidth();
53044             box.width += sw;
53045             box.x -= sw;
53046         }
53047         return box;
53048     },
53049
53050     updateBox : function(box){
53051         if(this.split && !this.collapsed){
53052             var sw = this.split.el.getWidth();
53053             box.width -= sw;
53054             this.split.el.setLeft(box.x);
53055             this.split.el.setTop(box.y);
53056             this.split.el.setHeight(box.height);
53057             box.x += sw;
53058         }
53059         if(this.collapsed){
53060             this.updateBody(null, box.height);
53061         }
53062         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53063     }
53064 });
53065
53066 Roo.WestLayoutRegion = function(mgr, config){
53067     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53068     if(this.split){
53069         this.split.placement = Roo.SplitBar.LEFT;
53070         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53071         this.split.el.addClass("x-layout-split-h");
53072     }
53073     var size = config.initialSize || config.width;
53074     if(typeof size != "undefined"){
53075         this.el.setWidth(size);
53076     }
53077 };
53078 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53079     orientation: Roo.SplitBar.HORIZONTAL,
53080     getBox : function(){
53081         if(this.collapsed){
53082             return this.collapsedEl.getBox();
53083         }
53084         var box = this.el.getBox();
53085         if(this.split){
53086             box.width += this.split.el.getWidth();
53087         }
53088         return box;
53089     },
53090     
53091     updateBox : function(box){
53092         if(this.split && !this.collapsed){
53093             var sw = this.split.el.getWidth();
53094             box.width -= sw;
53095             this.split.el.setLeft(box.x+box.width);
53096             this.split.el.setTop(box.y);
53097             this.split.el.setHeight(box.height);
53098         }
53099         if(this.collapsed){
53100             this.updateBody(null, box.height);
53101         }
53102         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53103     }
53104 });
53105 /*
53106  * Based on:
53107  * Ext JS Library 1.1.1
53108  * Copyright(c) 2006-2007, Ext JS, LLC.
53109  *
53110  * Originally Released Under LGPL - original licence link has changed is not relivant.
53111  *
53112  * Fork - LGPL
53113  * <script type="text/javascript">
53114  */
53115  
53116  
53117 /*
53118  * Private internal class for reading and applying state
53119  */
53120 Roo.LayoutStateManager = function(layout){
53121      // default empty state
53122      this.state = {
53123         north: {},
53124         south: {},
53125         east: {},
53126         west: {}       
53127     };
53128 };
53129
53130 Roo.LayoutStateManager.prototype = {
53131     init : function(layout, provider){
53132         this.provider = provider;
53133         var state = provider.get(layout.id+"-layout-state");
53134         if(state){
53135             var wasUpdating = layout.isUpdating();
53136             if(!wasUpdating){
53137                 layout.beginUpdate();
53138             }
53139             for(var key in state){
53140                 if(typeof state[key] != "function"){
53141                     var rstate = state[key];
53142                     var r = layout.getRegion(key);
53143                     if(r && rstate){
53144                         if(rstate.size){
53145                             r.resizeTo(rstate.size);
53146                         }
53147                         if(rstate.collapsed == true){
53148                             r.collapse(true);
53149                         }else{
53150                             r.expand(null, true);
53151                         }
53152                     }
53153                 }
53154             }
53155             if(!wasUpdating){
53156                 layout.endUpdate();
53157             }
53158             this.state = state; 
53159         }
53160         this.layout = layout;
53161         layout.on("regionresized", this.onRegionResized, this);
53162         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53163         layout.on("regionexpanded", this.onRegionExpanded, this);
53164     },
53165     
53166     storeState : function(){
53167         this.provider.set(this.layout.id+"-layout-state", this.state);
53168     },
53169     
53170     onRegionResized : function(region, newSize){
53171         this.state[region.getPosition()].size = newSize;
53172         this.storeState();
53173     },
53174     
53175     onRegionCollapsed : function(region){
53176         this.state[region.getPosition()].collapsed = true;
53177         this.storeState();
53178     },
53179     
53180     onRegionExpanded : function(region){
53181         this.state[region.getPosition()].collapsed = false;
53182         this.storeState();
53183     }
53184 };/*
53185  * Based on:
53186  * Ext JS Library 1.1.1
53187  * Copyright(c) 2006-2007, Ext JS, LLC.
53188  *
53189  * Originally Released Under LGPL - original licence link has changed is not relivant.
53190  *
53191  * Fork - LGPL
53192  * <script type="text/javascript">
53193  */
53194 /**
53195  * @class Roo.ContentPanel
53196  * @extends Roo.util.Observable
53197  * A basic ContentPanel element.
53198  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53199  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53200  * @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
53201  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53202  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53203  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53204  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53205  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53206  * @cfg {String} title          The title for this panel
53207  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53208  * @cfg {String} url            Calls {@link #setUrl} with this value
53209  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53210  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53211  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53212  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53213
53214  * @constructor
53215  * Create a new ContentPanel.
53216  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53217  * @param {String/Object} config A string to set only the title or a config object
53218  * @param {String} content (optional) Set the HTML content for this panel
53219  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53220  */
53221 Roo.ContentPanel = function(el, config, content){
53222     
53223      
53224     /*
53225     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53226         config = el;
53227         el = Roo.id();
53228     }
53229     if (config && config.parentLayout) { 
53230         el = config.parentLayout.el.createChild(); 
53231     }
53232     */
53233     if(el.autoCreate){ // xtype is available if this is called from factory
53234         config = el;
53235         el = Roo.id();
53236     }
53237     this.el = Roo.get(el);
53238     if(!this.el && config && config.autoCreate){
53239         if(typeof config.autoCreate == "object"){
53240             if(!config.autoCreate.id){
53241                 config.autoCreate.id = config.id||el;
53242             }
53243             this.el = Roo.DomHelper.append(document.body,
53244                         config.autoCreate, true);
53245         }else{
53246             this.el = Roo.DomHelper.append(document.body,
53247                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53248         }
53249     }
53250     this.closable = false;
53251     this.loaded = false;
53252     this.active = false;
53253     if(typeof config == "string"){
53254         this.title = config;
53255     }else{
53256         Roo.apply(this, config);
53257     }
53258     
53259     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53260         this.wrapEl = this.el.wrap();
53261         this.toolbar.container = this.el.insertSibling(false, 'before');
53262         this.toolbar = new Roo.Toolbar(this.toolbar);
53263     }
53264     
53265     // xtype created footer. - not sure if will work as we normally have to render first..
53266     if (this.footer && !this.footer.el && this.footer.xtype) {
53267         if (!this.wrapEl) {
53268             this.wrapEl = this.el.wrap();
53269         }
53270     
53271         this.footer.container = this.wrapEl.createChild();
53272          
53273         this.footer = Roo.factory(this.footer, Roo);
53274         
53275     }
53276     
53277     if(this.resizeEl){
53278         this.resizeEl = Roo.get(this.resizeEl, true);
53279     }else{
53280         this.resizeEl = this.el;
53281     }
53282     // handle view.xtype
53283     
53284  
53285     
53286     
53287     this.addEvents({
53288         /**
53289          * @event activate
53290          * Fires when this panel is activated. 
53291          * @param {Roo.ContentPanel} this
53292          */
53293         "activate" : true,
53294         /**
53295          * @event deactivate
53296          * Fires when this panel is activated. 
53297          * @param {Roo.ContentPanel} this
53298          */
53299         "deactivate" : true,
53300
53301         /**
53302          * @event resize
53303          * Fires when this panel is resized if fitToFrame is true.
53304          * @param {Roo.ContentPanel} this
53305          * @param {Number} width The width after any component adjustments
53306          * @param {Number} height The height after any component adjustments
53307          */
53308         "resize" : true,
53309         
53310          /**
53311          * @event render
53312          * Fires when this tab is created
53313          * @param {Roo.ContentPanel} this
53314          */
53315         "render" : true
53316         
53317         
53318         
53319     });
53320     
53321
53322     
53323     
53324     if(this.autoScroll){
53325         this.resizeEl.setStyle("overflow", "auto");
53326     } else {
53327         // fix randome scrolling
53328         this.el.on('scroll', function() {
53329             Roo.log('fix random scolling');
53330             this.scrollTo('top',0); 
53331         });
53332     }
53333     content = content || this.content;
53334     if(content){
53335         this.setContent(content);
53336     }
53337     if(config && config.url){
53338         this.setUrl(this.url, this.params, this.loadOnce);
53339     }
53340     
53341     
53342     
53343     Roo.ContentPanel.superclass.constructor.call(this);
53344     
53345     if (this.view && typeof(this.view.xtype) != 'undefined') {
53346         this.view.el = this.el.appendChild(document.createElement("div"));
53347         this.view = Roo.factory(this.view); 
53348         this.view.render  &&  this.view.render(false, '');  
53349     }
53350     
53351     
53352     this.fireEvent('render', this);
53353 };
53354
53355 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53356     tabTip:'',
53357     setRegion : function(region){
53358         this.region = region;
53359         if(region){
53360            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53361         }else{
53362            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53363         } 
53364     },
53365     
53366     /**
53367      * Returns the toolbar for this Panel if one was configured. 
53368      * @return {Roo.Toolbar} 
53369      */
53370     getToolbar : function(){
53371         return this.toolbar;
53372     },
53373     
53374     setActiveState : function(active){
53375         this.active = active;
53376         if(!active){
53377             this.fireEvent("deactivate", this);
53378         }else{
53379             this.fireEvent("activate", this);
53380         }
53381     },
53382     /**
53383      * Updates this panel's element
53384      * @param {String} content The new content
53385      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53386     */
53387     setContent : function(content, loadScripts){
53388         this.el.update(content, loadScripts);
53389     },
53390
53391     ignoreResize : function(w, h){
53392         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53393             return true;
53394         }else{
53395             this.lastSize = {width: w, height: h};
53396             return false;
53397         }
53398     },
53399     /**
53400      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53401      * @return {Roo.UpdateManager} The UpdateManager
53402      */
53403     getUpdateManager : function(){
53404         return this.el.getUpdateManager();
53405     },
53406      /**
53407      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53408      * @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:
53409 <pre><code>
53410 panel.load({
53411     url: "your-url.php",
53412     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53413     callback: yourFunction,
53414     scope: yourObject, //(optional scope)
53415     discardUrl: false,
53416     nocache: false,
53417     text: "Loading...",
53418     timeout: 30,
53419     scripts: false
53420 });
53421 </code></pre>
53422      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53423      * 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.
53424      * @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}
53425      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53426      * @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.
53427      * @return {Roo.ContentPanel} this
53428      */
53429     load : function(){
53430         var um = this.el.getUpdateManager();
53431         um.update.apply(um, arguments);
53432         return this;
53433     },
53434
53435
53436     /**
53437      * 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.
53438      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53439      * @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)
53440      * @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)
53441      * @return {Roo.UpdateManager} The UpdateManager
53442      */
53443     setUrl : function(url, params, loadOnce){
53444         if(this.refreshDelegate){
53445             this.removeListener("activate", this.refreshDelegate);
53446         }
53447         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53448         this.on("activate", this.refreshDelegate);
53449         return this.el.getUpdateManager();
53450     },
53451     
53452     _handleRefresh : function(url, params, loadOnce){
53453         if(!loadOnce || !this.loaded){
53454             var updater = this.el.getUpdateManager();
53455             updater.update(url, params, this._setLoaded.createDelegate(this));
53456         }
53457     },
53458     
53459     _setLoaded : function(){
53460         this.loaded = true;
53461     }, 
53462     
53463     /**
53464      * Returns this panel's id
53465      * @return {String} 
53466      */
53467     getId : function(){
53468         return this.el.id;
53469     },
53470     
53471     /** 
53472      * Returns this panel's element - used by regiosn to add.
53473      * @return {Roo.Element} 
53474      */
53475     getEl : function(){
53476         return this.wrapEl || this.el;
53477     },
53478     
53479     adjustForComponents : function(width, height)
53480     {
53481         //Roo.log('adjustForComponents ');
53482         if(this.resizeEl != this.el){
53483             width -= this.el.getFrameWidth('lr');
53484             height -= this.el.getFrameWidth('tb');
53485         }
53486         if(this.toolbar){
53487             var te = this.toolbar.getEl();
53488             height -= te.getHeight();
53489             te.setWidth(width);
53490         }
53491         if(this.footer){
53492             var te = this.footer.getEl();
53493             Roo.log("footer:" + te.getHeight());
53494             
53495             height -= te.getHeight();
53496             te.setWidth(width);
53497         }
53498         
53499         
53500         if(this.adjustments){
53501             width += this.adjustments[0];
53502             height += this.adjustments[1];
53503         }
53504         return {"width": width, "height": height};
53505     },
53506     
53507     setSize : function(width, height){
53508         if(this.fitToFrame && !this.ignoreResize(width, height)){
53509             if(this.fitContainer && this.resizeEl != this.el){
53510                 this.el.setSize(width, height);
53511             }
53512             var size = this.adjustForComponents(width, height);
53513             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53514             this.fireEvent('resize', this, size.width, size.height);
53515         }
53516     },
53517     
53518     /**
53519      * Returns this panel's title
53520      * @return {String} 
53521      */
53522     getTitle : function(){
53523         return this.title;
53524     },
53525     
53526     /**
53527      * Set this panel's title
53528      * @param {String} title
53529      */
53530     setTitle : function(title){
53531         this.title = title;
53532         if(this.region){
53533             this.region.updatePanelTitle(this, title);
53534         }
53535     },
53536     
53537     /**
53538      * Returns true is this panel was configured to be closable
53539      * @return {Boolean} 
53540      */
53541     isClosable : function(){
53542         return this.closable;
53543     },
53544     
53545     beforeSlide : function(){
53546         this.el.clip();
53547         this.resizeEl.clip();
53548     },
53549     
53550     afterSlide : function(){
53551         this.el.unclip();
53552         this.resizeEl.unclip();
53553     },
53554     
53555     /**
53556      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53557      *   Will fail silently if the {@link #setUrl} method has not been called.
53558      *   This does not activate the panel, just updates its content.
53559      */
53560     refresh : function(){
53561         if(this.refreshDelegate){
53562            this.loaded = false;
53563            this.refreshDelegate();
53564         }
53565     },
53566     
53567     /**
53568      * Destroys this panel
53569      */
53570     destroy : function(){
53571         this.el.removeAllListeners();
53572         var tempEl = document.createElement("span");
53573         tempEl.appendChild(this.el.dom);
53574         tempEl.innerHTML = "";
53575         this.el.remove();
53576         this.el = null;
53577     },
53578     
53579     /**
53580      * form - if the content panel contains a form - this is a reference to it.
53581      * @type {Roo.form.Form}
53582      */
53583     form : false,
53584     /**
53585      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53586      *    This contains a reference to it.
53587      * @type {Roo.View}
53588      */
53589     view : false,
53590     
53591       /**
53592      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53593      * <pre><code>
53594
53595 layout.addxtype({
53596        xtype : 'Form',
53597        items: [ .... ]
53598    }
53599 );
53600
53601 </code></pre>
53602      * @param {Object} cfg Xtype definition of item to add.
53603      */
53604     
53605     addxtype : function(cfg) {
53606         // add form..
53607         if (cfg.xtype.match(/^Form$/)) {
53608             
53609             var el;
53610             //if (this.footer) {
53611             //    el = this.footer.container.insertSibling(false, 'before');
53612             //} else {
53613                 el = this.el.createChild();
53614             //}
53615
53616             this.form = new  Roo.form.Form(cfg);
53617             
53618             
53619             if ( this.form.allItems.length) {
53620                 this.form.render(el.dom);
53621             }
53622             return this.form;
53623         }
53624         // should only have one of theses..
53625         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53626             // views.. should not be just added - used named prop 'view''
53627             
53628             cfg.el = this.el.appendChild(document.createElement("div"));
53629             // factory?
53630             
53631             var ret = new Roo.factory(cfg);
53632              
53633              ret.render && ret.render(false, ''); // render blank..
53634             this.view = ret;
53635             return ret;
53636         }
53637         return false;
53638     }
53639 });
53640
53641 /**
53642  * @class Roo.GridPanel
53643  * @extends Roo.ContentPanel
53644  * @constructor
53645  * Create a new GridPanel.
53646  * @param {Roo.grid.Grid} grid The grid for this panel
53647  * @param {String/Object} config A string to set only the panel's title, or a config object
53648  */
53649 Roo.GridPanel = function(grid, config){
53650     
53651   
53652     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53653         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53654         
53655     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53656     
53657     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53658     
53659     if(this.toolbar){
53660         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53661     }
53662     // xtype created footer. - not sure if will work as we normally have to render first..
53663     if (this.footer && !this.footer.el && this.footer.xtype) {
53664         
53665         this.footer.container = this.grid.getView().getFooterPanel(true);
53666         this.footer.dataSource = this.grid.dataSource;
53667         this.footer = Roo.factory(this.footer, Roo);
53668         
53669     }
53670     
53671     grid.monitorWindowResize = false; // turn off autosizing
53672     grid.autoHeight = false;
53673     grid.autoWidth = false;
53674     this.grid = grid;
53675     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53676 };
53677
53678 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53679     getId : function(){
53680         return this.grid.id;
53681     },
53682     
53683     /**
53684      * Returns the grid for this panel
53685      * @return {Roo.grid.Grid} 
53686      */
53687     getGrid : function(){
53688         return this.grid;    
53689     },
53690     
53691     setSize : function(width, height){
53692         if(!this.ignoreResize(width, height)){
53693             var grid = this.grid;
53694             var size = this.adjustForComponents(width, height);
53695             grid.getGridEl().setSize(size.width, size.height);
53696             grid.autoSize();
53697         }
53698     },
53699     
53700     beforeSlide : function(){
53701         this.grid.getView().scroller.clip();
53702     },
53703     
53704     afterSlide : function(){
53705         this.grid.getView().scroller.unclip();
53706     },
53707     
53708     destroy : function(){
53709         this.grid.destroy();
53710         delete this.grid;
53711         Roo.GridPanel.superclass.destroy.call(this); 
53712     }
53713 });
53714
53715
53716 /**
53717  * @class Roo.NestedLayoutPanel
53718  * @extends Roo.ContentPanel
53719  * @constructor
53720  * Create a new NestedLayoutPanel.
53721  * 
53722  * 
53723  * @param {Roo.BorderLayout} layout The layout for this panel
53724  * @param {String/Object} config A string to set only the title or a config object
53725  */
53726 Roo.NestedLayoutPanel = function(layout, config)
53727 {
53728     // construct with only one argument..
53729     /* FIXME - implement nicer consturctors
53730     if (layout.layout) {
53731         config = layout;
53732         layout = config.layout;
53733         delete config.layout;
53734     }
53735     if (layout.xtype && !layout.getEl) {
53736         // then layout needs constructing..
53737         layout = Roo.factory(layout, Roo);
53738     }
53739     */
53740     
53741     
53742     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53743     
53744     layout.monitorWindowResize = false; // turn off autosizing
53745     this.layout = layout;
53746     this.layout.getEl().addClass("x-layout-nested-layout");
53747     
53748     
53749     
53750     
53751 };
53752
53753 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53754
53755     setSize : function(width, height){
53756         if(!this.ignoreResize(width, height)){
53757             var size = this.adjustForComponents(width, height);
53758             var el = this.layout.getEl();
53759             el.setSize(size.width, size.height);
53760             var touch = el.dom.offsetWidth;
53761             this.layout.layout();
53762             // ie requires a double layout on the first pass
53763             if(Roo.isIE && !this.initialized){
53764                 this.initialized = true;
53765                 this.layout.layout();
53766             }
53767         }
53768     },
53769     
53770     // activate all subpanels if not currently active..
53771     
53772     setActiveState : function(active){
53773         this.active = active;
53774         if(!active){
53775             this.fireEvent("deactivate", this);
53776             return;
53777         }
53778         
53779         this.fireEvent("activate", this);
53780         // not sure if this should happen before or after..
53781         if (!this.layout) {
53782             return; // should not happen..
53783         }
53784         var reg = false;
53785         for (var r in this.layout.regions) {
53786             reg = this.layout.getRegion(r);
53787             if (reg.getActivePanel()) {
53788                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53789                 reg.setActivePanel(reg.getActivePanel());
53790                 continue;
53791             }
53792             if (!reg.panels.length) {
53793                 continue;
53794             }
53795             reg.showPanel(reg.getPanel(0));
53796         }
53797         
53798         
53799         
53800         
53801     },
53802     
53803     /**
53804      * Returns the nested BorderLayout for this panel
53805      * @return {Roo.BorderLayout} 
53806      */
53807     getLayout : function(){
53808         return this.layout;
53809     },
53810     
53811      /**
53812      * Adds a xtype elements to the layout of the nested panel
53813      * <pre><code>
53814
53815 panel.addxtype({
53816        xtype : 'ContentPanel',
53817        region: 'west',
53818        items: [ .... ]
53819    }
53820 );
53821
53822 panel.addxtype({
53823         xtype : 'NestedLayoutPanel',
53824         region: 'west',
53825         layout: {
53826            center: { },
53827            west: { }   
53828         },
53829         items : [ ... list of content panels or nested layout panels.. ]
53830    }
53831 );
53832 </code></pre>
53833      * @param {Object} cfg Xtype definition of item to add.
53834      */
53835     addxtype : function(cfg) {
53836         return this.layout.addxtype(cfg);
53837     
53838     }
53839 });
53840
53841 Roo.ScrollPanel = function(el, config, content){
53842     config = config || {};
53843     config.fitToFrame = true;
53844     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53845     
53846     this.el.dom.style.overflow = "hidden";
53847     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53848     this.el.removeClass("x-layout-inactive-content");
53849     this.el.on("mousewheel", this.onWheel, this);
53850
53851     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53852     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53853     up.unselectable(); down.unselectable();
53854     up.on("click", this.scrollUp, this);
53855     down.on("click", this.scrollDown, this);
53856     up.addClassOnOver("x-scroller-btn-over");
53857     down.addClassOnOver("x-scroller-btn-over");
53858     up.addClassOnClick("x-scroller-btn-click");
53859     down.addClassOnClick("x-scroller-btn-click");
53860     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53861
53862     this.resizeEl = this.el;
53863     this.el = wrap; this.up = up; this.down = down;
53864 };
53865
53866 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53867     increment : 100,
53868     wheelIncrement : 5,
53869     scrollUp : function(){
53870         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53871     },
53872
53873     scrollDown : function(){
53874         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53875     },
53876
53877     afterScroll : function(){
53878         var el = this.resizeEl;
53879         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53880         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53881         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53882     },
53883
53884     setSize : function(){
53885         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53886         this.afterScroll();
53887     },
53888
53889     onWheel : function(e){
53890         var d = e.getWheelDelta();
53891         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53892         this.afterScroll();
53893         e.stopEvent();
53894     },
53895
53896     setContent : function(content, loadScripts){
53897         this.resizeEl.update(content, loadScripts);
53898     }
53899
53900 });
53901
53902
53903
53904
53905
53906
53907
53908
53909
53910 /**
53911  * @class Roo.TreePanel
53912  * @extends Roo.ContentPanel
53913  * @constructor
53914  * Create a new TreePanel. - defaults to fit/scoll contents.
53915  * @param {String/Object} config A string to set only the panel's title, or a config object
53916  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53917  */
53918 Roo.TreePanel = function(config){
53919     var el = config.el;
53920     var tree = config.tree;
53921     delete config.tree; 
53922     delete config.el; // hopefull!
53923     
53924     // wrapper for IE7 strict & safari scroll issue
53925     
53926     var treeEl = el.createChild();
53927     config.resizeEl = treeEl;
53928     
53929     
53930     
53931     Roo.TreePanel.superclass.constructor.call(this, el, config);
53932  
53933  
53934     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53935     //console.log(tree);
53936     this.on('activate', function()
53937     {
53938         if (this.tree.rendered) {
53939             return;
53940         }
53941         //console.log('render tree');
53942         this.tree.render();
53943     });
53944     // this should not be needed.. - it's actually the 'el' that resizes?
53945     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53946     
53947     //this.on('resize',  function (cp, w, h) {
53948     //        this.tree.innerCt.setWidth(w);
53949     //        this.tree.innerCt.setHeight(h);
53950     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53951     //});
53952
53953         
53954     
53955 };
53956
53957 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53958     fitToFrame : true,
53959     autoScroll : true
53960 });
53961
53962
53963
53964
53965
53966
53967
53968
53969
53970
53971
53972 /*
53973  * Based on:
53974  * Ext JS Library 1.1.1
53975  * Copyright(c) 2006-2007, Ext JS, LLC.
53976  *
53977  * Originally Released Under LGPL - original licence link has changed is not relivant.
53978  *
53979  * Fork - LGPL
53980  * <script type="text/javascript">
53981  */
53982  
53983
53984 /**
53985  * @class Roo.ReaderLayout
53986  * @extends Roo.BorderLayout
53987  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53988  * center region containing two nested regions (a top one for a list view and one for item preview below),
53989  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53990  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53991  * expedites the setup of the overall layout and regions for this common application style.
53992  * Example:
53993  <pre><code>
53994 var reader = new Roo.ReaderLayout();
53995 var CP = Roo.ContentPanel;  // shortcut for adding
53996
53997 reader.beginUpdate();
53998 reader.add("north", new CP("north", "North"));
53999 reader.add("west", new CP("west", {title: "West"}));
54000 reader.add("east", new CP("east", {title: "East"}));
54001
54002 reader.regions.listView.add(new CP("listView", "List"));
54003 reader.regions.preview.add(new CP("preview", "Preview"));
54004 reader.endUpdate();
54005 </code></pre>
54006 * @constructor
54007 * Create a new ReaderLayout
54008 * @param {Object} config Configuration options
54009 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54010 * document.body if omitted)
54011 */
54012 Roo.ReaderLayout = function(config, renderTo){
54013     var c = config || {size:{}};
54014     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54015         north: c.north !== false ? Roo.apply({
54016             split:false,
54017             initialSize: 32,
54018             titlebar: false
54019         }, c.north) : false,
54020         west: c.west !== false ? Roo.apply({
54021             split:true,
54022             initialSize: 200,
54023             minSize: 175,
54024             maxSize: 400,
54025             titlebar: true,
54026             collapsible: true,
54027             animate: true,
54028             margins:{left:5,right:0,bottom:5,top:5},
54029             cmargins:{left:5,right:5,bottom:5,top:5}
54030         }, c.west) : false,
54031         east: c.east !== false ? Roo.apply({
54032             split:true,
54033             initialSize: 200,
54034             minSize: 175,
54035             maxSize: 400,
54036             titlebar: true,
54037             collapsible: true,
54038             animate: true,
54039             margins:{left:0,right:5,bottom:5,top:5},
54040             cmargins:{left:5,right:5,bottom:5,top:5}
54041         }, c.east) : false,
54042         center: Roo.apply({
54043             tabPosition: 'top',
54044             autoScroll:false,
54045             closeOnTab: true,
54046             titlebar:false,
54047             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54048         }, c.center)
54049     });
54050
54051     this.el.addClass('x-reader');
54052
54053     this.beginUpdate();
54054
54055     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54056         south: c.preview !== false ? Roo.apply({
54057             split:true,
54058             initialSize: 200,
54059             minSize: 100,
54060             autoScroll:true,
54061             collapsible:true,
54062             titlebar: true,
54063             cmargins:{top:5,left:0, right:0, bottom:0}
54064         }, c.preview) : false,
54065         center: Roo.apply({
54066             autoScroll:false,
54067             titlebar:false,
54068             minHeight:200
54069         }, c.listView)
54070     });
54071     this.add('center', new Roo.NestedLayoutPanel(inner,
54072             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54073
54074     this.endUpdate();
54075
54076     this.regions.preview = inner.getRegion('south');
54077     this.regions.listView = inner.getRegion('center');
54078 };
54079
54080 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54081  * Based on:
54082  * Ext JS Library 1.1.1
54083  * Copyright(c) 2006-2007, Ext JS, LLC.
54084  *
54085  * Originally Released Under LGPL - original licence link has changed is not relivant.
54086  *
54087  * Fork - LGPL
54088  * <script type="text/javascript">
54089  */
54090  
54091 /**
54092  * @class Roo.grid.Grid
54093  * @extends Roo.util.Observable
54094  * This class represents the primary interface of a component based grid control.
54095  * <br><br>Usage:<pre><code>
54096  var grid = new Roo.grid.Grid("my-container-id", {
54097      ds: myDataStore,
54098      cm: myColModel,
54099      selModel: mySelectionModel,
54100      autoSizeColumns: true,
54101      monitorWindowResize: false,
54102      trackMouseOver: true
54103  });
54104  // set any options
54105  grid.render();
54106  * </code></pre>
54107  * <b>Common Problems:</b><br/>
54108  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54109  * element will correct this<br/>
54110  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54111  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54112  * are unpredictable.<br/>
54113  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54114  * grid to calculate dimensions/offsets.<br/>
54115   * @constructor
54116  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54117  * The container MUST have some type of size defined for the grid to fill. The container will be
54118  * automatically set to position relative if it isn't already.
54119  * @param {Object} config A config object that sets properties on this grid.
54120  */
54121 Roo.grid.Grid = function(container, config){
54122         // initialize the container
54123         this.container = Roo.get(container);
54124         this.container.update("");
54125         this.container.setStyle("overflow", "hidden");
54126     this.container.addClass('x-grid-container');
54127
54128     this.id = this.container.id;
54129
54130     Roo.apply(this, config);
54131     // check and correct shorthanded configs
54132     if(this.ds){
54133         this.dataSource = this.ds;
54134         delete this.ds;
54135     }
54136     if(this.cm){
54137         this.colModel = this.cm;
54138         delete this.cm;
54139     }
54140     if(this.sm){
54141         this.selModel = this.sm;
54142         delete this.sm;
54143     }
54144
54145     if (this.selModel) {
54146         this.selModel = Roo.factory(this.selModel, Roo.grid);
54147         this.sm = this.selModel;
54148         this.sm.xmodule = this.xmodule || false;
54149     }
54150     if (typeof(this.colModel.config) == 'undefined') {
54151         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54152         this.cm = this.colModel;
54153         this.cm.xmodule = this.xmodule || false;
54154     }
54155     if (this.dataSource) {
54156         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54157         this.ds = this.dataSource;
54158         this.ds.xmodule = this.xmodule || false;
54159          
54160     }
54161     
54162     
54163     
54164     if(this.width){
54165         this.container.setWidth(this.width);
54166     }
54167
54168     if(this.height){
54169         this.container.setHeight(this.height);
54170     }
54171     /** @private */
54172         this.addEvents({
54173         // raw events
54174         /**
54175          * @event click
54176          * The raw click event for the entire grid.
54177          * @param {Roo.EventObject} e
54178          */
54179         "click" : true,
54180         /**
54181          * @event dblclick
54182          * The raw dblclick event for the entire grid.
54183          * @param {Roo.EventObject} e
54184          */
54185         "dblclick" : true,
54186         /**
54187          * @event contextmenu
54188          * The raw contextmenu event for the entire grid.
54189          * @param {Roo.EventObject} e
54190          */
54191         "contextmenu" : true,
54192         /**
54193          * @event mousedown
54194          * The raw mousedown event for the entire grid.
54195          * @param {Roo.EventObject} e
54196          */
54197         "mousedown" : true,
54198         /**
54199          * @event mouseup
54200          * The raw mouseup event for the entire grid.
54201          * @param {Roo.EventObject} e
54202          */
54203         "mouseup" : true,
54204         /**
54205          * @event mouseover
54206          * The raw mouseover event for the entire grid.
54207          * @param {Roo.EventObject} e
54208          */
54209         "mouseover" : true,
54210         /**
54211          * @event mouseout
54212          * The raw mouseout event for the entire grid.
54213          * @param {Roo.EventObject} e
54214          */
54215         "mouseout" : true,
54216         /**
54217          * @event keypress
54218          * The raw keypress event for the entire grid.
54219          * @param {Roo.EventObject} e
54220          */
54221         "keypress" : true,
54222         /**
54223          * @event keydown
54224          * The raw keydown event for the entire grid.
54225          * @param {Roo.EventObject} e
54226          */
54227         "keydown" : true,
54228
54229         // custom events
54230
54231         /**
54232          * @event cellclick
54233          * Fires when a cell is clicked
54234          * @param {Grid} this
54235          * @param {Number} rowIndex
54236          * @param {Number} columnIndex
54237          * @param {Roo.EventObject} e
54238          */
54239         "cellclick" : true,
54240         /**
54241          * @event celldblclick
54242          * Fires when a cell is double clicked
54243          * @param {Grid} this
54244          * @param {Number} rowIndex
54245          * @param {Number} columnIndex
54246          * @param {Roo.EventObject} e
54247          */
54248         "celldblclick" : true,
54249         /**
54250          * @event rowclick
54251          * Fires when a row is clicked
54252          * @param {Grid} this
54253          * @param {Number} rowIndex
54254          * @param {Roo.EventObject} e
54255          */
54256         "rowclick" : true,
54257         /**
54258          * @event rowdblclick
54259          * Fires when a row is double clicked
54260          * @param {Grid} this
54261          * @param {Number} rowIndex
54262          * @param {Roo.EventObject} e
54263          */
54264         "rowdblclick" : true,
54265         /**
54266          * @event headerclick
54267          * Fires when a header is clicked
54268          * @param {Grid} this
54269          * @param {Number} columnIndex
54270          * @param {Roo.EventObject} e
54271          */
54272         "headerclick" : true,
54273         /**
54274          * @event headerdblclick
54275          * Fires when a header cell is double clicked
54276          * @param {Grid} this
54277          * @param {Number} columnIndex
54278          * @param {Roo.EventObject} e
54279          */
54280         "headerdblclick" : true,
54281         /**
54282          * @event rowcontextmenu
54283          * Fires when a row is right clicked
54284          * @param {Grid} this
54285          * @param {Number} rowIndex
54286          * @param {Roo.EventObject} e
54287          */
54288         "rowcontextmenu" : true,
54289         /**
54290          * @event cellcontextmenu
54291          * Fires when a cell is right clicked
54292          * @param {Grid} this
54293          * @param {Number} rowIndex
54294          * @param {Number} cellIndex
54295          * @param {Roo.EventObject} e
54296          */
54297          "cellcontextmenu" : true,
54298         /**
54299          * @event headercontextmenu
54300          * Fires when a header is right clicked
54301          * @param {Grid} this
54302          * @param {Number} columnIndex
54303          * @param {Roo.EventObject} e
54304          */
54305         "headercontextmenu" : true,
54306         /**
54307          * @event bodyscroll
54308          * Fires when the body element is scrolled
54309          * @param {Number} scrollLeft
54310          * @param {Number} scrollTop
54311          */
54312         "bodyscroll" : true,
54313         /**
54314          * @event columnresize
54315          * Fires when the user resizes a column
54316          * @param {Number} columnIndex
54317          * @param {Number} newSize
54318          */
54319         "columnresize" : true,
54320         /**
54321          * @event columnmove
54322          * Fires when the user moves a column
54323          * @param {Number} oldIndex
54324          * @param {Number} newIndex
54325          */
54326         "columnmove" : true,
54327         /**
54328          * @event startdrag
54329          * Fires when row(s) start being dragged
54330          * @param {Grid} this
54331          * @param {Roo.GridDD} dd The drag drop object
54332          * @param {event} e The raw browser event
54333          */
54334         "startdrag" : true,
54335         /**
54336          * @event enddrag
54337          * Fires when a drag operation is complete
54338          * @param {Grid} this
54339          * @param {Roo.GridDD} dd The drag drop object
54340          * @param {event} e The raw browser event
54341          */
54342         "enddrag" : true,
54343         /**
54344          * @event dragdrop
54345          * Fires when dragged row(s) are dropped on a valid DD target
54346          * @param {Grid} this
54347          * @param {Roo.GridDD} dd The drag drop object
54348          * @param {String} targetId The target drag drop object
54349          * @param {event} e The raw browser event
54350          */
54351         "dragdrop" : true,
54352         /**
54353          * @event dragover
54354          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54355          * @param {Grid} this
54356          * @param {Roo.GridDD} dd The drag drop object
54357          * @param {String} targetId The target drag drop object
54358          * @param {event} e The raw browser event
54359          */
54360         "dragover" : true,
54361         /**
54362          * @event dragenter
54363          *  Fires when the dragged row(s) first cross another DD target while being dragged
54364          * @param {Grid} this
54365          * @param {Roo.GridDD} dd The drag drop object
54366          * @param {String} targetId The target drag drop object
54367          * @param {event} e The raw browser event
54368          */
54369         "dragenter" : true,
54370         /**
54371          * @event dragout
54372          * Fires when the dragged row(s) leave another DD target while being dragged
54373          * @param {Grid} this
54374          * @param {Roo.GridDD} dd The drag drop object
54375          * @param {String} targetId The target drag drop object
54376          * @param {event} e The raw browser event
54377          */
54378         "dragout" : true,
54379         /**
54380          * @event rowclass
54381          * Fires when a row is rendered, so you can change add a style to it.
54382          * @param {GridView} gridview   The grid view
54383          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54384          */
54385         'rowclass' : true,
54386
54387         /**
54388          * @event render
54389          * Fires when the grid is rendered
54390          * @param {Grid} grid
54391          */
54392         'render' : true
54393     });
54394
54395     Roo.grid.Grid.superclass.constructor.call(this);
54396 };
54397 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54398     
54399     /**
54400      * @cfg {String} ddGroup - drag drop group.
54401      */
54402
54403     /**
54404      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54405      */
54406     minColumnWidth : 25,
54407
54408     /**
54409      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54410      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54411      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54412      */
54413     autoSizeColumns : false,
54414
54415     /**
54416      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54417      */
54418     autoSizeHeaders : true,
54419
54420     /**
54421      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54422      */
54423     monitorWindowResize : true,
54424
54425     /**
54426      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54427      * rows measured to get a columns size. Default is 0 (all rows).
54428      */
54429     maxRowsToMeasure : 0,
54430
54431     /**
54432      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54433      */
54434     trackMouseOver : true,
54435
54436     /**
54437     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54438     */
54439     
54440     /**
54441     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54442     */
54443     enableDragDrop : false,
54444     
54445     /**
54446     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54447     */
54448     enableColumnMove : true,
54449     
54450     /**
54451     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54452     */
54453     enableColumnHide : true,
54454     
54455     /**
54456     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54457     */
54458     enableRowHeightSync : false,
54459     
54460     /**
54461     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54462     */
54463     stripeRows : true,
54464     
54465     /**
54466     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54467     */
54468     autoHeight : false,
54469
54470     /**
54471      * @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.
54472      */
54473     autoExpandColumn : false,
54474
54475     /**
54476     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54477     * Default is 50.
54478     */
54479     autoExpandMin : 50,
54480
54481     /**
54482     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54483     */
54484     autoExpandMax : 1000,
54485
54486     /**
54487     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54488     */
54489     view : null,
54490
54491     /**
54492     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54493     */
54494     loadMask : false,
54495     /**
54496     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54497     */
54498     dropTarget: false,
54499     
54500    
54501     
54502     // private
54503     rendered : false,
54504
54505     /**
54506     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54507     * of a fixed width. Default is false.
54508     */
54509     /**
54510     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54511     */
54512     /**
54513      * Called once after all setup has been completed and the grid is ready to be rendered.
54514      * @return {Roo.grid.Grid} this
54515      */
54516     render : function()
54517     {
54518         var c = this.container;
54519         // try to detect autoHeight/width mode
54520         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54521             this.autoHeight = true;
54522         }
54523         var view = this.getView();
54524         view.init(this);
54525
54526         c.on("click", this.onClick, this);
54527         c.on("dblclick", this.onDblClick, this);
54528         c.on("contextmenu", this.onContextMenu, this);
54529         c.on("keydown", this.onKeyDown, this);
54530         if (Roo.isTouch) {
54531             c.on("touchstart", this.onTouchStart, this);
54532         }
54533
54534         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54535
54536         this.getSelectionModel().init(this);
54537
54538         view.render();
54539
54540         if(this.loadMask){
54541             this.loadMask = new Roo.LoadMask(this.container,
54542                     Roo.apply({store:this.dataSource}, this.loadMask));
54543         }
54544         
54545         
54546         if (this.toolbar && this.toolbar.xtype) {
54547             this.toolbar.container = this.getView().getHeaderPanel(true);
54548             this.toolbar = new Roo.Toolbar(this.toolbar);
54549         }
54550         if (this.footer && this.footer.xtype) {
54551             this.footer.dataSource = this.getDataSource();
54552             this.footer.container = this.getView().getFooterPanel(true);
54553             this.footer = Roo.factory(this.footer, Roo);
54554         }
54555         if (this.dropTarget && this.dropTarget.xtype) {
54556             delete this.dropTarget.xtype;
54557             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54558         }
54559         
54560         
54561         this.rendered = true;
54562         this.fireEvent('render', this);
54563         return this;
54564     },
54565
54566         /**
54567          * Reconfigures the grid to use a different Store and Column Model.
54568          * The View will be bound to the new objects and refreshed.
54569          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54570          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54571          */
54572     reconfigure : function(dataSource, colModel){
54573         if(this.loadMask){
54574             this.loadMask.destroy();
54575             this.loadMask = new Roo.LoadMask(this.container,
54576                     Roo.apply({store:dataSource}, this.loadMask));
54577         }
54578         this.view.bind(dataSource, colModel);
54579         this.dataSource = dataSource;
54580         this.colModel = colModel;
54581         this.view.refresh(true);
54582     },
54583
54584     // private
54585     onKeyDown : function(e){
54586         this.fireEvent("keydown", e);
54587     },
54588
54589     /**
54590      * Destroy this grid.
54591      * @param {Boolean} removeEl True to remove the element
54592      */
54593     destroy : function(removeEl, keepListeners){
54594         if(this.loadMask){
54595             this.loadMask.destroy();
54596         }
54597         var c = this.container;
54598         c.removeAllListeners();
54599         this.view.destroy();
54600         this.colModel.purgeListeners();
54601         if(!keepListeners){
54602             this.purgeListeners();
54603         }
54604         c.update("");
54605         if(removeEl === true){
54606             c.remove();
54607         }
54608     },
54609
54610     // private
54611     processEvent : function(name, e){
54612         // does this fire select???
54613         //Roo.log('grid:processEvent '  + name);
54614         
54615         if (name != 'touchstart' ) {
54616             this.fireEvent(name, e);    
54617         }
54618         
54619         var t = e.getTarget();
54620         var v = this.view;
54621         var header = v.findHeaderIndex(t);
54622         if(header !== false){
54623             var ename = name == 'touchstart' ? 'click' : name;
54624              
54625             this.fireEvent("header" + ename, this, header, e);
54626         }else{
54627             var row = v.findRowIndex(t);
54628             var cell = v.findCellIndex(t);
54629             if (name == 'touchstart') {
54630                 // first touch is always a click.
54631                 // hopefull this happens after selection is updated.?
54632                 name = false;
54633                 
54634                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54635                     var cs = this.selModel.getSelectedCell();
54636                     if (row == cs[0] && cell == cs[1]){
54637                         name = 'dblclick';
54638                     }
54639                 }
54640                 if (typeof(this.selModel.getSelections) != 'undefined') {
54641                     var cs = this.selModel.getSelections();
54642                     var ds = this.dataSource;
54643                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54644                         name = 'dblclick';
54645                     }
54646                 }
54647                 if (!name) {
54648                     return;
54649                 }
54650             }
54651             
54652             
54653             if(row !== false){
54654                 this.fireEvent("row" + name, this, row, e);
54655                 if(cell !== false){
54656                     this.fireEvent("cell" + name, this, row, cell, e);
54657                 }
54658             }
54659         }
54660     },
54661
54662     // private
54663     onClick : function(e){
54664         this.processEvent("click", e);
54665     },
54666    // private
54667     onTouchStart : function(e){
54668         this.processEvent("touchstart", e);
54669     },
54670
54671     // private
54672     onContextMenu : function(e, t){
54673         this.processEvent("contextmenu", e);
54674     },
54675
54676     // private
54677     onDblClick : function(e){
54678         this.processEvent("dblclick", e);
54679     },
54680
54681     // private
54682     walkCells : function(row, col, step, fn, scope){
54683         var cm = this.colModel, clen = cm.getColumnCount();
54684         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54685         if(step < 0){
54686             if(col < 0){
54687                 row--;
54688                 first = false;
54689             }
54690             while(row >= 0){
54691                 if(!first){
54692                     col = clen-1;
54693                 }
54694                 first = false;
54695                 while(col >= 0){
54696                     if(fn.call(scope || this, row, col, cm) === true){
54697                         return [row, col];
54698                     }
54699                     col--;
54700                 }
54701                 row--;
54702             }
54703         } else {
54704             if(col >= clen){
54705                 row++;
54706                 first = false;
54707             }
54708             while(row < rlen){
54709                 if(!first){
54710                     col = 0;
54711                 }
54712                 first = false;
54713                 while(col < clen){
54714                     if(fn.call(scope || this, row, col, cm) === true){
54715                         return [row, col];
54716                     }
54717                     col++;
54718                 }
54719                 row++;
54720             }
54721         }
54722         return null;
54723     },
54724
54725     // private
54726     getSelections : function(){
54727         return this.selModel.getSelections();
54728     },
54729
54730     /**
54731      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54732      * but if manual update is required this method will initiate it.
54733      */
54734     autoSize : function(){
54735         if(this.rendered){
54736             this.view.layout();
54737             if(this.view.adjustForScroll){
54738                 this.view.adjustForScroll();
54739             }
54740         }
54741     },
54742
54743     /**
54744      * Returns the grid's underlying element.
54745      * @return {Element} The element
54746      */
54747     getGridEl : function(){
54748         return this.container;
54749     },
54750
54751     // private for compatibility, overridden by editor grid
54752     stopEditing : function(){},
54753
54754     /**
54755      * Returns the grid's SelectionModel.
54756      * @return {SelectionModel}
54757      */
54758     getSelectionModel : function(){
54759         if(!this.selModel){
54760             this.selModel = new Roo.grid.RowSelectionModel();
54761         }
54762         return this.selModel;
54763     },
54764
54765     /**
54766      * Returns the grid's DataSource.
54767      * @return {DataSource}
54768      */
54769     getDataSource : function(){
54770         return this.dataSource;
54771     },
54772
54773     /**
54774      * Returns the grid's ColumnModel.
54775      * @return {ColumnModel}
54776      */
54777     getColumnModel : function(){
54778         return this.colModel;
54779     },
54780
54781     /**
54782      * Returns the grid's GridView object.
54783      * @return {GridView}
54784      */
54785     getView : function(){
54786         if(!this.view){
54787             this.view = new Roo.grid.GridView(this.viewConfig);
54788         }
54789         return this.view;
54790     },
54791     /**
54792      * Called to get grid's drag proxy text, by default returns this.ddText.
54793      * @return {String}
54794      */
54795     getDragDropText : function(){
54796         var count = this.selModel.getCount();
54797         return String.format(this.ddText, count, count == 1 ? '' : 's');
54798     }
54799 });
54800 /**
54801  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54802  * %0 is replaced with the number of selected rows.
54803  * @type String
54804  */
54805 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54806  * Based on:
54807  * Ext JS Library 1.1.1
54808  * Copyright(c) 2006-2007, Ext JS, LLC.
54809  *
54810  * Originally Released Under LGPL - original licence link has changed is not relivant.
54811  *
54812  * Fork - LGPL
54813  * <script type="text/javascript">
54814  */
54815  
54816 Roo.grid.AbstractGridView = function(){
54817         this.grid = null;
54818         
54819         this.events = {
54820             "beforerowremoved" : true,
54821             "beforerowsinserted" : true,
54822             "beforerefresh" : true,
54823             "rowremoved" : true,
54824             "rowsinserted" : true,
54825             "rowupdated" : true,
54826             "refresh" : true
54827         };
54828     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54829 };
54830
54831 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54832     rowClass : "x-grid-row",
54833     cellClass : "x-grid-cell",
54834     tdClass : "x-grid-td",
54835     hdClass : "x-grid-hd",
54836     splitClass : "x-grid-hd-split",
54837     
54838     init: function(grid){
54839         this.grid = grid;
54840                 var cid = this.grid.getGridEl().id;
54841         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54842         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54843         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54844         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54845         },
54846         
54847     getColumnRenderers : function(){
54848         var renderers = [];
54849         var cm = this.grid.colModel;
54850         var colCount = cm.getColumnCount();
54851         for(var i = 0; i < colCount; i++){
54852             renderers[i] = cm.getRenderer(i);
54853         }
54854         return renderers;
54855     },
54856     
54857     getColumnIds : function(){
54858         var ids = [];
54859         var cm = this.grid.colModel;
54860         var colCount = cm.getColumnCount();
54861         for(var i = 0; i < colCount; i++){
54862             ids[i] = cm.getColumnId(i);
54863         }
54864         return ids;
54865     },
54866     
54867     getDataIndexes : function(){
54868         if(!this.indexMap){
54869             this.indexMap = this.buildIndexMap();
54870         }
54871         return this.indexMap.colToData;
54872     },
54873     
54874     getColumnIndexByDataIndex : function(dataIndex){
54875         if(!this.indexMap){
54876             this.indexMap = this.buildIndexMap();
54877         }
54878         return this.indexMap.dataToCol[dataIndex];
54879     },
54880     
54881     /**
54882      * Set a css style for a column dynamically. 
54883      * @param {Number} colIndex The index of the column
54884      * @param {String} name The css property name
54885      * @param {String} value The css value
54886      */
54887     setCSSStyle : function(colIndex, name, value){
54888         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54889         Roo.util.CSS.updateRule(selector, name, value);
54890     },
54891     
54892     generateRules : function(cm){
54893         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54894         Roo.util.CSS.removeStyleSheet(rulesId);
54895         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54896             var cid = cm.getColumnId(i);
54897             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54898                          this.tdSelector, cid, " {\n}\n",
54899                          this.hdSelector, cid, " {\n}\n",
54900                          this.splitSelector, cid, " {\n}\n");
54901         }
54902         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54903     }
54904 });/*
54905  * Based on:
54906  * Ext JS Library 1.1.1
54907  * Copyright(c) 2006-2007, Ext JS, LLC.
54908  *
54909  * Originally Released Under LGPL - original licence link has changed is not relivant.
54910  *
54911  * Fork - LGPL
54912  * <script type="text/javascript">
54913  */
54914
54915 // private
54916 // This is a support class used internally by the Grid components
54917 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54918     this.grid = grid;
54919     this.view = grid.getView();
54920     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54921     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54922     if(hd2){
54923         this.setHandleElId(Roo.id(hd));
54924         this.setOuterHandleElId(Roo.id(hd2));
54925     }
54926     this.scroll = false;
54927 };
54928 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54929     maxDragWidth: 120,
54930     getDragData : function(e){
54931         var t = Roo.lib.Event.getTarget(e);
54932         var h = this.view.findHeaderCell(t);
54933         if(h){
54934             return {ddel: h.firstChild, header:h};
54935         }
54936         return false;
54937     },
54938
54939     onInitDrag : function(e){
54940         this.view.headersDisabled = true;
54941         var clone = this.dragData.ddel.cloneNode(true);
54942         clone.id = Roo.id();
54943         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54944         this.proxy.update(clone);
54945         return true;
54946     },
54947
54948     afterValidDrop : function(){
54949         var v = this.view;
54950         setTimeout(function(){
54951             v.headersDisabled = false;
54952         }, 50);
54953     },
54954
54955     afterInvalidDrop : function(){
54956         var v = this.view;
54957         setTimeout(function(){
54958             v.headersDisabled = false;
54959         }, 50);
54960     }
54961 });
54962 /*
54963  * Based on:
54964  * Ext JS Library 1.1.1
54965  * Copyright(c) 2006-2007, Ext JS, LLC.
54966  *
54967  * Originally Released Under LGPL - original licence link has changed is not relivant.
54968  *
54969  * Fork - LGPL
54970  * <script type="text/javascript">
54971  */
54972 // private
54973 // This is a support class used internally by the Grid components
54974 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54975     this.grid = grid;
54976     this.view = grid.getView();
54977     // split the proxies so they don't interfere with mouse events
54978     this.proxyTop = Roo.DomHelper.append(document.body, {
54979         cls:"col-move-top", html:"&#160;"
54980     }, true);
54981     this.proxyBottom = Roo.DomHelper.append(document.body, {
54982         cls:"col-move-bottom", html:"&#160;"
54983     }, true);
54984     this.proxyTop.hide = this.proxyBottom.hide = function(){
54985         this.setLeftTop(-100,-100);
54986         this.setStyle("visibility", "hidden");
54987     };
54988     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54989     // temporarily disabled
54990     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54991     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54992 };
54993 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54994     proxyOffsets : [-4, -9],
54995     fly: Roo.Element.fly,
54996
54997     getTargetFromEvent : function(e){
54998         var t = Roo.lib.Event.getTarget(e);
54999         var cindex = this.view.findCellIndex(t);
55000         if(cindex !== false){
55001             return this.view.getHeaderCell(cindex);
55002         }
55003         return null;
55004     },
55005
55006     nextVisible : function(h){
55007         var v = this.view, cm = this.grid.colModel;
55008         h = h.nextSibling;
55009         while(h){
55010             if(!cm.isHidden(v.getCellIndex(h))){
55011                 return h;
55012             }
55013             h = h.nextSibling;
55014         }
55015         return null;
55016     },
55017
55018     prevVisible : function(h){
55019         var v = this.view, cm = this.grid.colModel;
55020         h = h.prevSibling;
55021         while(h){
55022             if(!cm.isHidden(v.getCellIndex(h))){
55023                 return h;
55024             }
55025             h = h.prevSibling;
55026         }
55027         return null;
55028     },
55029
55030     positionIndicator : function(h, n, e){
55031         var x = Roo.lib.Event.getPageX(e);
55032         var r = Roo.lib.Dom.getRegion(n.firstChild);
55033         var px, pt, py = r.top + this.proxyOffsets[1];
55034         if((r.right - x) <= (r.right-r.left)/2){
55035             px = r.right+this.view.borderWidth;
55036             pt = "after";
55037         }else{
55038             px = r.left;
55039             pt = "before";
55040         }
55041         var oldIndex = this.view.getCellIndex(h);
55042         var newIndex = this.view.getCellIndex(n);
55043
55044         if(this.grid.colModel.isFixed(newIndex)){
55045             return false;
55046         }
55047
55048         var locked = this.grid.colModel.isLocked(newIndex);
55049
55050         if(pt == "after"){
55051             newIndex++;
55052         }
55053         if(oldIndex < newIndex){
55054             newIndex--;
55055         }
55056         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55057             return false;
55058         }
55059         px +=  this.proxyOffsets[0];
55060         this.proxyTop.setLeftTop(px, py);
55061         this.proxyTop.show();
55062         if(!this.bottomOffset){
55063             this.bottomOffset = this.view.mainHd.getHeight();
55064         }
55065         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55066         this.proxyBottom.show();
55067         return pt;
55068     },
55069
55070     onNodeEnter : function(n, dd, e, data){
55071         if(data.header != n){
55072             this.positionIndicator(data.header, n, e);
55073         }
55074     },
55075
55076     onNodeOver : function(n, dd, e, data){
55077         var result = false;
55078         if(data.header != n){
55079             result = this.positionIndicator(data.header, n, e);
55080         }
55081         if(!result){
55082             this.proxyTop.hide();
55083             this.proxyBottom.hide();
55084         }
55085         return result ? this.dropAllowed : this.dropNotAllowed;
55086     },
55087
55088     onNodeOut : function(n, dd, e, data){
55089         this.proxyTop.hide();
55090         this.proxyBottom.hide();
55091     },
55092
55093     onNodeDrop : function(n, dd, e, data){
55094         var h = data.header;
55095         if(h != n){
55096             var cm = this.grid.colModel;
55097             var x = Roo.lib.Event.getPageX(e);
55098             var r = Roo.lib.Dom.getRegion(n.firstChild);
55099             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55100             var oldIndex = this.view.getCellIndex(h);
55101             var newIndex = this.view.getCellIndex(n);
55102             var locked = cm.isLocked(newIndex);
55103             if(pt == "after"){
55104                 newIndex++;
55105             }
55106             if(oldIndex < newIndex){
55107                 newIndex--;
55108             }
55109             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55110                 return false;
55111             }
55112             cm.setLocked(oldIndex, locked, true);
55113             cm.moveColumn(oldIndex, newIndex);
55114             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55115             return true;
55116         }
55117         return false;
55118     }
55119 });
55120 /*
55121  * Based on:
55122  * Ext JS Library 1.1.1
55123  * Copyright(c) 2006-2007, Ext JS, LLC.
55124  *
55125  * Originally Released Under LGPL - original licence link has changed is not relivant.
55126  *
55127  * Fork - LGPL
55128  * <script type="text/javascript">
55129  */
55130   
55131 /**
55132  * @class Roo.grid.GridView
55133  * @extends Roo.util.Observable
55134  *
55135  * @constructor
55136  * @param {Object} config
55137  */
55138 Roo.grid.GridView = function(config){
55139     Roo.grid.GridView.superclass.constructor.call(this);
55140     this.el = null;
55141
55142     Roo.apply(this, config);
55143 };
55144
55145 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55146
55147     unselectable :  'unselectable="on"',
55148     unselectableCls :  'x-unselectable',
55149     
55150     
55151     rowClass : "x-grid-row",
55152
55153     cellClass : "x-grid-col",
55154
55155     tdClass : "x-grid-td",
55156
55157     hdClass : "x-grid-hd",
55158
55159     splitClass : "x-grid-split",
55160
55161     sortClasses : ["sort-asc", "sort-desc"],
55162
55163     enableMoveAnim : false,
55164
55165     hlColor: "C3DAF9",
55166
55167     dh : Roo.DomHelper,
55168
55169     fly : Roo.Element.fly,
55170
55171     css : Roo.util.CSS,
55172
55173     borderWidth: 1,
55174
55175     splitOffset: 3,
55176
55177     scrollIncrement : 22,
55178
55179     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55180
55181     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55182
55183     bind : function(ds, cm){
55184         if(this.ds){
55185             this.ds.un("load", this.onLoad, this);
55186             this.ds.un("datachanged", this.onDataChange, this);
55187             this.ds.un("add", this.onAdd, this);
55188             this.ds.un("remove", this.onRemove, this);
55189             this.ds.un("update", this.onUpdate, this);
55190             this.ds.un("clear", this.onClear, this);
55191         }
55192         if(ds){
55193             ds.on("load", this.onLoad, this);
55194             ds.on("datachanged", this.onDataChange, this);
55195             ds.on("add", this.onAdd, this);
55196             ds.on("remove", this.onRemove, this);
55197             ds.on("update", this.onUpdate, this);
55198             ds.on("clear", this.onClear, this);
55199         }
55200         this.ds = ds;
55201
55202         if(this.cm){
55203             this.cm.un("widthchange", this.onColWidthChange, this);
55204             this.cm.un("headerchange", this.onHeaderChange, this);
55205             this.cm.un("hiddenchange", this.onHiddenChange, this);
55206             this.cm.un("columnmoved", this.onColumnMove, this);
55207             this.cm.un("columnlockchange", this.onColumnLock, this);
55208         }
55209         if(cm){
55210             this.generateRules(cm);
55211             cm.on("widthchange", this.onColWidthChange, this);
55212             cm.on("headerchange", this.onHeaderChange, this);
55213             cm.on("hiddenchange", this.onHiddenChange, this);
55214             cm.on("columnmoved", this.onColumnMove, this);
55215             cm.on("columnlockchange", this.onColumnLock, this);
55216         }
55217         this.cm = cm;
55218     },
55219
55220     init: function(grid){
55221         Roo.grid.GridView.superclass.init.call(this, grid);
55222
55223         this.bind(grid.dataSource, grid.colModel);
55224
55225         grid.on("headerclick", this.handleHeaderClick, this);
55226
55227         if(grid.trackMouseOver){
55228             grid.on("mouseover", this.onRowOver, this);
55229             grid.on("mouseout", this.onRowOut, this);
55230         }
55231         grid.cancelTextSelection = function(){};
55232         this.gridId = grid.id;
55233
55234         var tpls = this.templates || {};
55235
55236         if(!tpls.master){
55237             tpls.master = new Roo.Template(
55238                '<div class="x-grid" hidefocus="true">',
55239                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55240                   '<div class="x-grid-topbar"></div>',
55241                   '<div class="x-grid-scroller"><div></div></div>',
55242                   '<div class="x-grid-locked">',
55243                       '<div class="x-grid-header">{lockedHeader}</div>',
55244                       '<div class="x-grid-body">{lockedBody}</div>',
55245                   "</div>",
55246                   '<div class="x-grid-viewport">',
55247                       '<div class="x-grid-header">{header}</div>',
55248                       '<div class="x-grid-body">{body}</div>',
55249                   "</div>",
55250                   '<div class="x-grid-bottombar"></div>',
55251                  
55252                   '<div class="x-grid-resize-proxy">&#160;</div>',
55253                "</div>"
55254             );
55255             tpls.master.disableformats = true;
55256         }
55257
55258         if(!tpls.header){
55259             tpls.header = new Roo.Template(
55260                '<table border="0" cellspacing="0" cellpadding="0">',
55261                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55262                "</table>{splits}"
55263             );
55264             tpls.header.disableformats = true;
55265         }
55266         tpls.header.compile();
55267
55268         if(!tpls.hcell){
55269             tpls.hcell = new Roo.Template(
55270                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55271                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55272                 "</div></td>"
55273              );
55274              tpls.hcell.disableFormats = true;
55275         }
55276         tpls.hcell.compile();
55277
55278         if(!tpls.hsplit){
55279             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55280                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55281             tpls.hsplit.disableFormats = true;
55282         }
55283         tpls.hsplit.compile();
55284
55285         if(!tpls.body){
55286             tpls.body = new Roo.Template(
55287                '<table border="0" cellspacing="0" cellpadding="0">',
55288                "<tbody>{rows}</tbody>",
55289                "</table>"
55290             );
55291             tpls.body.disableFormats = true;
55292         }
55293         tpls.body.compile();
55294
55295         if(!tpls.row){
55296             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55297             tpls.row.disableFormats = true;
55298         }
55299         tpls.row.compile();
55300
55301         if(!tpls.cell){
55302             tpls.cell = new Roo.Template(
55303                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55304                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55305                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55306                 "</td>"
55307             );
55308             tpls.cell.disableFormats = true;
55309         }
55310         tpls.cell.compile();
55311
55312         this.templates = tpls;
55313     },
55314
55315     // remap these for backwards compat
55316     onColWidthChange : function(){
55317         this.updateColumns.apply(this, arguments);
55318     },
55319     onHeaderChange : function(){
55320         this.updateHeaders.apply(this, arguments);
55321     }, 
55322     onHiddenChange : function(){
55323         this.handleHiddenChange.apply(this, arguments);
55324     },
55325     onColumnMove : function(){
55326         this.handleColumnMove.apply(this, arguments);
55327     },
55328     onColumnLock : function(){
55329         this.handleLockChange.apply(this, arguments);
55330     },
55331
55332     onDataChange : function(){
55333         this.refresh();
55334         this.updateHeaderSortState();
55335     },
55336
55337     onClear : function(){
55338         this.refresh();
55339     },
55340
55341     onUpdate : function(ds, record){
55342         this.refreshRow(record);
55343     },
55344
55345     refreshRow : function(record){
55346         var ds = this.ds, index;
55347         if(typeof record == 'number'){
55348             index = record;
55349             record = ds.getAt(index);
55350         }else{
55351             index = ds.indexOf(record);
55352         }
55353         this.insertRows(ds, index, index, true);
55354         this.onRemove(ds, record, index+1, true);
55355         this.syncRowHeights(index, index);
55356         this.layout();
55357         this.fireEvent("rowupdated", this, index, record);
55358     },
55359
55360     onAdd : function(ds, records, index){
55361         this.insertRows(ds, index, index + (records.length-1));
55362     },
55363
55364     onRemove : function(ds, record, index, isUpdate){
55365         if(isUpdate !== true){
55366             this.fireEvent("beforerowremoved", this, index, record);
55367         }
55368         var bt = this.getBodyTable(), lt = this.getLockedTable();
55369         if(bt.rows[index]){
55370             bt.firstChild.removeChild(bt.rows[index]);
55371         }
55372         if(lt.rows[index]){
55373             lt.firstChild.removeChild(lt.rows[index]);
55374         }
55375         if(isUpdate !== true){
55376             this.stripeRows(index);
55377             this.syncRowHeights(index, index);
55378             this.layout();
55379             this.fireEvent("rowremoved", this, index, record);
55380         }
55381     },
55382
55383     onLoad : function(){
55384         this.scrollToTop();
55385     },
55386
55387     /**
55388      * Scrolls the grid to the top
55389      */
55390     scrollToTop : function(){
55391         if(this.scroller){
55392             this.scroller.dom.scrollTop = 0;
55393             this.syncScroll();
55394         }
55395     },
55396
55397     /**
55398      * Gets a panel in the header of the grid that can be used for toolbars etc.
55399      * After modifying the contents of this panel a call to grid.autoSize() may be
55400      * required to register any changes in size.
55401      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55402      * @return Roo.Element
55403      */
55404     getHeaderPanel : function(doShow){
55405         if(doShow){
55406             this.headerPanel.show();
55407         }
55408         return this.headerPanel;
55409     },
55410
55411     /**
55412      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55413      * After modifying the contents of this panel a call to grid.autoSize() may be
55414      * required to register any changes in size.
55415      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55416      * @return Roo.Element
55417      */
55418     getFooterPanel : function(doShow){
55419         if(doShow){
55420             this.footerPanel.show();
55421         }
55422         return this.footerPanel;
55423     },
55424
55425     initElements : function(){
55426         var E = Roo.Element;
55427         var el = this.grid.getGridEl().dom.firstChild;
55428         var cs = el.childNodes;
55429
55430         this.el = new E(el);
55431         
55432          this.focusEl = new E(el.firstChild);
55433         this.focusEl.swallowEvent("click", true);
55434         
55435         this.headerPanel = new E(cs[1]);
55436         this.headerPanel.enableDisplayMode("block");
55437
55438         this.scroller = new E(cs[2]);
55439         this.scrollSizer = new E(this.scroller.dom.firstChild);
55440
55441         this.lockedWrap = new E(cs[3]);
55442         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55443         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55444
55445         this.mainWrap = new E(cs[4]);
55446         this.mainHd = new E(this.mainWrap.dom.firstChild);
55447         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55448
55449         this.footerPanel = new E(cs[5]);
55450         this.footerPanel.enableDisplayMode("block");
55451
55452         this.resizeProxy = new E(cs[6]);
55453
55454         this.headerSelector = String.format(
55455            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55456            this.lockedHd.id, this.mainHd.id
55457         );
55458
55459         this.splitterSelector = String.format(
55460            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55461            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55462         );
55463     },
55464     idToCssName : function(s)
55465     {
55466         return s.replace(/[^a-z0-9]+/ig, '-');
55467     },
55468
55469     getHeaderCell : function(index){
55470         return Roo.DomQuery.select(this.headerSelector)[index];
55471     },
55472
55473     getHeaderCellMeasure : function(index){
55474         return this.getHeaderCell(index).firstChild;
55475     },
55476
55477     getHeaderCellText : function(index){
55478         return this.getHeaderCell(index).firstChild.firstChild;
55479     },
55480
55481     getLockedTable : function(){
55482         return this.lockedBody.dom.firstChild;
55483     },
55484
55485     getBodyTable : function(){
55486         return this.mainBody.dom.firstChild;
55487     },
55488
55489     getLockedRow : function(index){
55490         return this.getLockedTable().rows[index];
55491     },
55492
55493     getRow : function(index){
55494         return this.getBodyTable().rows[index];
55495     },
55496
55497     getRowComposite : function(index){
55498         if(!this.rowEl){
55499             this.rowEl = new Roo.CompositeElementLite();
55500         }
55501         var els = [], lrow, mrow;
55502         if(lrow = this.getLockedRow(index)){
55503             els.push(lrow);
55504         }
55505         if(mrow = this.getRow(index)){
55506             els.push(mrow);
55507         }
55508         this.rowEl.elements = els;
55509         return this.rowEl;
55510     },
55511     /**
55512      * Gets the 'td' of the cell
55513      * 
55514      * @param {Integer} rowIndex row to select
55515      * @param {Integer} colIndex column to select
55516      * 
55517      * @return {Object} 
55518      */
55519     getCell : function(rowIndex, colIndex){
55520         var locked = this.cm.getLockedCount();
55521         var source;
55522         if(colIndex < locked){
55523             source = this.lockedBody.dom.firstChild;
55524         }else{
55525             source = this.mainBody.dom.firstChild;
55526             colIndex -= locked;
55527         }
55528         return source.rows[rowIndex].childNodes[colIndex];
55529     },
55530
55531     getCellText : function(rowIndex, colIndex){
55532         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55533     },
55534
55535     getCellBox : function(cell){
55536         var b = this.fly(cell).getBox();
55537         if(Roo.isOpera){ // opera fails to report the Y
55538             b.y = cell.offsetTop + this.mainBody.getY();
55539         }
55540         return b;
55541     },
55542
55543     getCellIndex : function(cell){
55544         var id = String(cell.className).match(this.cellRE);
55545         if(id){
55546             return parseInt(id[1], 10);
55547         }
55548         return 0;
55549     },
55550
55551     findHeaderIndex : function(n){
55552         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55553         return r ? this.getCellIndex(r) : false;
55554     },
55555
55556     findHeaderCell : function(n){
55557         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55558         return r ? r : false;
55559     },
55560
55561     findRowIndex : function(n){
55562         if(!n){
55563             return false;
55564         }
55565         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55566         return r ? r.rowIndex : false;
55567     },
55568
55569     findCellIndex : function(node){
55570         var stop = this.el.dom;
55571         while(node && node != stop){
55572             if(this.findRE.test(node.className)){
55573                 return this.getCellIndex(node);
55574             }
55575             node = node.parentNode;
55576         }
55577         return false;
55578     },
55579
55580     getColumnId : function(index){
55581         return this.cm.getColumnId(index);
55582     },
55583
55584     getSplitters : function()
55585     {
55586         if(this.splitterSelector){
55587            return Roo.DomQuery.select(this.splitterSelector);
55588         }else{
55589             return null;
55590       }
55591     },
55592
55593     getSplitter : function(index){
55594         return this.getSplitters()[index];
55595     },
55596
55597     onRowOver : function(e, t){
55598         var row;
55599         if((row = this.findRowIndex(t)) !== false){
55600             this.getRowComposite(row).addClass("x-grid-row-over");
55601         }
55602     },
55603
55604     onRowOut : function(e, t){
55605         var row;
55606         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55607             this.getRowComposite(row).removeClass("x-grid-row-over");
55608         }
55609     },
55610
55611     renderHeaders : function(){
55612         var cm = this.cm;
55613         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55614         var cb = [], lb = [], sb = [], lsb = [], p = {};
55615         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55616             p.cellId = "x-grid-hd-0-" + i;
55617             p.splitId = "x-grid-csplit-0-" + i;
55618             p.id = cm.getColumnId(i);
55619             p.value = cm.getColumnHeader(i) || "";
55620             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55621             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55622             if(!cm.isLocked(i)){
55623                 cb[cb.length] = ct.apply(p);
55624                 sb[sb.length] = st.apply(p);
55625             }else{
55626                 lb[lb.length] = ct.apply(p);
55627                 lsb[lsb.length] = st.apply(p);
55628             }
55629         }
55630         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55631                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55632     },
55633
55634     updateHeaders : function(){
55635         var html = this.renderHeaders();
55636         this.lockedHd.update(html[0]);
55637         this.mainHd.update(html[1]);
55638     },
55639
55640     /**
55641      * Focuses the specified row.
55642      * @param {Number} row The row index
55643      */
55644     focusRow : function(row)
55645     {
55646         //Roo.log('GridView.focusRow');
55647         var x = this.scroller.dom.scrollLeft;
55648         this.focusCell(row, 0, false);
55649         this.scroller.dom.scrollLeft = x;
55650     },
55651
55652     /**
55653      * Focuses the specified cell.
55654      * @param {Number} row The row index
55655      * @param {Number} col The column index
55656      * @param {Boolean} hscroll false to disable horizontal scrolling
55657      */
55658     focusCell : function(row, col, hscroll)
55659     {
55660         //Roo.log('GridView.focusCell');
55661         var el = this.ensureVisible(row, col, hscroll);
55662         this.focusEl.alignTo(el, "tl-tl");
55663         if(Roo.isGecko){
55664             this.focusEl.focus();
55665         }else{
55666             this.focusEl.focus.defer(1, this.focusEl);
55667         }
55668     },
55669
55670     /**
55671      * Scrolls the specified cell into view
55672      * @param {Number} row The row index
55673      * @param {Number} col The column index
55674      * @param {Boolean} hscroll false to disable horizontal scrolling
55675      */
55676     ensureVisible : function(row, col, hscroll)
55677     {
55678         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55679         //return null; //disable for testing.
55680         if(typeof row != "number"){
55681             row = row.rowIndex;
55682         }
55683         if(row < 0 && row >= this.ds.getCount()){
55684             return  null;
55685         }
55686         col = (col !== undefined ? col : 0);
55687         var cm = this.grid.colModel;
55688         while(cm.isHidden(col)){
55689             col++;
55690         }
55691
55692         var el = this.getCell(row, col);
55693         if(!el){
55694             return null;
55695         }
55696         var c = this.scroller.dom;
55697
55698         var ctop = parseInt(el.offsetTop, 10);
55699         var cleft = parseInt(el.offsetLeft, 10);
55700         var cbot = ctop + el.offsetHeight;
55701         var cright = cleft + el.offsetWidth;
55702         
55703         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55704         var stop = parseInt(c.scrollTop, 10);
55705         var sleft = parseInt(c.scrollLeft, 10);
55706         var sbot = stop + ch;
55707         var sright = sleft + c.clientWidth;
55708         /*
55709         Roo.log('GridView.ensureVisible:' +
55710                 ' ctop:' + ctop +
55711                 ' c.clientHeight:' + c.clientHeight +
55712                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55713                 ' stop:' + stop +
55714                 ' cbot:' + cbot +
55715                 ' sbot:' + sbot +
55716                 ' ch:' + ch  
55717                 );
55718         */
55719         if(ctop < stop){
55720              c.scrollTop = ctop;
55721             //Roo.log("set scrolltop to ctop DISABLE?");
55722         }else if(cbot > sbot){
55723             //Roo.log("set scrolltop to cbot-ch");
55724             c.scrollTop = cbot-ch;
55725         }
55726         
55727         if(hscroll !== false){
55728             if(cleft < sleft){
55729                 c.scrollLeft = cleft;
55730             }else if(cright > sright){
55731                 c.scrollLeft = cright-c.clientWidth;
55732             }
55733         }
55734          
55735         return el;
55736     },
55737
55738     updateColumns : function(){
55739         this.grid.stopEditing();
55740         var cm = this.grid.colModel, colIds = this.getColumnIds();
55741         //var totalWidth = cm.getTotalWidth();
55742         var pos = 0;
55743         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55744             //if(cm.isHidden(i)) continue;
55745             var w = cm.getColumnWidth(i);
55746             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55747             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55748         }
55749         this.updateSplitters();
55750     },
55751
55752     generateRules : function(cm){
55753         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55754         Roo.util.CSS.removeStyleSheet(rulesId);
55755         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55756             var cid = cm.getColumnId(i);
55757             var align = '';
55758             if(cm.config[i].align){
55759                 align = 'text-align:'+cm.config[i].align+';';
55760             }
55761             var hidden = '';
55762             if(cm.isHidden(i)){
55763                 hidden = 'display:none;';
55764             }
55765             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55766             ruleBuf.push(
55767                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55768                     this.hdSelector, cid, " {\n", align, width, "}\n",
55769                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55770                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55771         }
55772         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55773     },
55774
55775     updateSplitters : function(){
55776         var cm = this.cm, s = this.getSplitters();
55777         if(s){ // splitters not created yet
55778             var pos = 0, locked = true;
55779             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55780                 if(cm.isHidden(i)) {
55781                     continue;
55782                 }
55783                 var w = cm.getColumnWidth(i); // make sure it's a number
55784                 if(!cm.isLocked(i) && locked){
55785                     pos = 0;
55786                     locked = false;
55787                 }
55788                 pos += w;
55789                 s[i].style.left = (pos-this.splitOffset) + "px";
55790             }
55791         }
55792     },
55793
55794     handleHiddenChange : function(colModel, colIndex, hidden){
55795         if(hidden){
55796             this.hideColumn(colIndex);
55797         }else{
55798             this.unhideColumn(colIndex);
55799         }
55800     },
55801
55802     hideColumn : function(colIndex){
55803         var cid = this.getColumnId(colIndex);
55804         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55805         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55806         if(Roo.isSafari){
55807             this.updateHeaders();
55808         }
55809         this.updateSplitters();
55810         this.layout();
55811     },
55812
55813     unhideColumn : function(colIndex){
55814         var cid = this.getColumnId(colIndex);
55815         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55816         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55817
55818         if(Roo.isSafari){
55819             this.updateHeaders();
55820         }
55821         this.updateSplitters();
55822         this.layout();
55823     },
55824
55825     insertRows : function(dm, firstRow, lastRow, isUpdate){
55826         if(firstRow == 0 && lastRow == dm.getCount()-1){
55827             this.refresh();
55828         }else{
55829             if(!isUpdate){
55830                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55831             }
55832             var s = this.getScrollState();
55833             var markup = this.renderRows(firstRow, lastRow);
55834             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55835             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55836             this.restoreScroll(s);
55837             if(!isUpdate){
55838                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55839                 this.syncRowHeights(firstRow, lastRow);
55840                 this.stripeRows(firstRow);
55841                 this.layout();
55842             }
55843         }
55844     },
55845
55846     bufferRows : function(markup, target, index){
55847         var before = null, trows = target.rows, tbody = target.tBodies[0];
55848         if(index < trows.length){
55849             before = trows[index];
55850         }
55851         var b = document.createElement("div");
55852         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55853         var rows = b.firstChild.rows;
55854         for(var i = 0, len = rows.length; i < len; i++){
55855             if(before){
55856                 tbody.insertBefore(rows[0], before);
55857             }else{
55858                 tbody.appendChild(rows[0]);
55859             }
55860         }
55861         b.innerHTML = "";
55862         b = null;
55863     },
55864
55865     deleteRows : function(dm, firstRow, lastRow){
55866         if(dm.getRowCount()<1){
55867             this.fireEvent("beforerefresh", this);
55868             this.mainBody.update("");
55869             this.lockedBody.update("");
55870             this.fireEvent("refresh", this);
55871         }else{
55872             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55873             var bt = this.getBodyTable();
55874             var tbody = bt.firstChild;
55875             var rows = bt.rows;
55876             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55877                 tbody.removeChild(rows[firstRow]);
55878             }
55879             this.stripeRows(firstRow);
55880             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55881         }
55882     },
55883
55884     updateRows : function(dataSource, firstRow, lastRow){
55885         var s = this.getScrollState();
55886         this.refresh();
55887         this.restoreScroll(s);
55888     },
55889
55890     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55891         if(!noRefresh){
55892            this.refresh();
55893         }
55894         this.updateHeaderSortState();
55895     },
55896
55897     getScrollState : function(){
55898         
55899         var sb = this.scroller.dom;
55900         return {left: sb.scrollLeft, top: sb.scrollTop};
55901     },
55902
55903     stripeRows : function(startRow){
55904         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55905             return;
55906         }
55907         startRow = startRow || 0;
55908         var rows = this.getBodyTable().rows;
55909         var lrows = this.getLockedTable().rows;
55910         var cls = ' x-grid-row-alt ';
55911         for(var i = startRow, len = rows.length; i < len; i++){
55912             var row = rows[i], lrow = lrows[i];
55913             var isAlt = ((i+1) % 2 == 0);
55914             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55915             if(isAlt == hasAlt){
55916                 continue;
55917             }
55918             if(isAlt){
55919                 row.className += " x-grid-row-alt";
55920             }else{
55921                 row.className = row.className.replace("x-grid-row-alt", "");
55922             }
55923             if(lrow){
55924                 lrow.className = row.className;
55925             }
55926         }
55927     },
55928
55929     restoreScroll : function(state){
55930         //Roo.log('GridView.restoreScroll');
55931         var sb = this.scroller.dom;
55932         sb.scrollLeft = state.left;
55933         sb.scrollTop = state.top;
55934         this.syncScroll();
55935     },
55936
55937     syncScroll : function(){
55938         //Roo.log('GridView.syncScroll');
55939         var sb = this.scroller.dom;
55940         var sh = this.mainHd.dom;
55941         var bs = this.mainBody.dom;
55942         var lv = this.lockedBody.dom;
55943         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55944         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55945     },
55946
55947     handleScroll : function(e){
55948         this.syncScroll();
55949         var sb = this.scroller.dom;
55950         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55951         e.stopEvent();
55952     },
55953
55954     handleWheel : function(e){
55955         var d = e.getWheelDelta();
55956         this.scroller.dom.scrollTop -= d*22;
55957         // set this here to prevent jumpy scrolling on large tables
55958         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55959         e.stopEvent();
55960     },
55961
55962     renderRows : function(startRow, endRow){
55963         // pull in all the crap needed to render rows
55964         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55965         var colCount = cm.getColumnCount();
55966
55967         if(ds.getCount() < 1){
55968             return ["", ""];
55969         }
55970
55971         // build a map for all the columns
55972         var cs = [];
55973         for(var i = 0; i < colCount; i++){
55974             var name = cm.getDataIndex(i);
55975             cs[i] = {
55976                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55977                 renderer : cm.getRenderer(i),
55978                 id : cm.getColumnId(i),
55979                 locked : cm.isLocked(i),
55980                 has_editor : cm.isCellEditable(i)
55981             };
55982         }
55983
55984         startRow = startRow || 0;
55985         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55986
55987         // records to render
55988         var rs = ds.getRange(startRow, endRow);
55989
55990         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55991     },
55992
55993     // As much as I hate to duplicate code, this was branched because FireFox really hates
55994     // [].join("") on strings. The performance difference was substantial enough to
55995     // branch this function
55996     doRender : Roo.isGecko ?
55997             function(cs, rs, ds, startRow, colCount, stripe){
55998                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55999                 // buffers
56000                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56001                 
56002                 var hasListener = this.grid.hasListener('rowclass');
56003                 var rowcfg = {};
56004                 for(var j = 0, len = rs.length; j < len; j++){
56005                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56006                     for(var i = 0; i < colCount; i++){
56007                         c = cs[i];
56008                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56009                         p.id = c.id;
56010                         p.css = p.attr = "";
56011                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56012                         if(p.value == undefined || p.value === "") {
56013                             p.value = "&#160;";
56014                         }
56015                         if(c.has_editor){
56016                             p.css += ' x-grid-editable-cell';
56017                         }
56018                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56019                             p.css +=  ' x-grid-dirty-cell';
56020                         }
56021                         var markup = ct.apply(p);
56022                         if(!c.locked){
56023                             cb+= markup;
56024                         }else{
56025                             lcb+= markup;
56026                         }
56027                     }
56028                     var alt = [];
56029                     if(stripe && ((rowIndex+1) % 2 == 0)){
56030                         alt.push("x-grid-row-alt")
56031                     }
56032                     if(r.dirty){
56033                         alt.push(  " x-grid-dirty-row");
56034                     }
56035                     rp.cells = lcb;
56036                     if(this.getRowClass){
56037                         alt.push(this.getRowClass(r, rowIndex));
56038                     }
56039                     if (hasListener) {
56040                         rowcfg = {
56041                              
56042                             record: r,
56043                             rowIndex : rowIndex,
56044                             rowClass : ''
56045                         };
56046                         this.grid.fireEvent('rowclass', this, rowcfg);
56047                         alt.push(rowcfg.rowClass);
56048                     }
56049                     rp.alt = alt.join(" ");
56050                     lbuf+= rt.apply(rp);
56051                     rp.cells = cb;
56052                     buf+=  rt.apply(rp);
56053                 }
56054                 return [lbuf, buf];
56055             } :
56056             function(cs, rs, ds, startRow, colCount, stripe){
56057                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56058                 // buffers
56059                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56060                 var hasListener = this.grid.hasListener('rowclass');
56061  
56062                 var rowcfg = {};
56063                 for(var j = 0, len = rs.length; j < len; j++){
56064                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56065                     for(var i = 0; i < colCount; i++){
56066                         c = cs[i];
56067                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56068                         p.id = c.id;
56069                         p.css = p.attr = "";
56070                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56071                         if(p.value == undefined || p.value === "") {
56072                             p.value = "&#160;";
56073                         }
56074                         //Roo.log(c);
56075                          if(c.has_editor){
56076                             p.css += ' x-grid-editable-cell';
56077                         }
56078                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56079                             p.css += ' x-grid-dirty-cell' 
56080                         }
56081                         
56082                         var markup = ct.apply(p);
56083                         if(!c.locked){
56084                             cb[cb.length] = markup;
56085                         }else{
56086                             lcb[lcb.length] = markup;
56087                         }
56088                     }
56089                     var alt = [];
56090                     if(stripe && ((rowIndex+1) % 2 == 0)){
56091                         alt.push( "x-grid-row-alt");
56092                     }
56093                     if(r.dirty){
56094                         alt.push(" x-grid-dirty-row");
56095                     }
56096                     rp.cells = lcb;
56097                     if(this.getRowClass){
56098                         alt.push( this.getRowClass(r, rowIndex));
56099                     }
56100                     if (hasListener) {
56101                         rowcfg = {
56102                              
56103                             record: r,
56104                             rowIndex : rowIndex,
56105                             rowClass : ''
56106                         };
56107                         this.grid.fireEvent('rowclass', this, rowcfg);
56108                         alt.push(rowcfg.rowClass);
56109                     }
56110                     
56111                     rp.alt = alt.join(" ");
56112                     rp.cells = lcb.join("");
56113                     lbuf[lbuf.length] = rt.apply(rp);
56114                     rp.cells = cb.join("");
56115                     buf[buf.length] =  rt.apply(rp);
56116                 }
56117                 return [lbuf.join(""), buf.join("")];
56118             },
56119
56120     renderBody : function(){
56121         var markup = this.renderRows();
56122         var bt = this.templates.body;
56123         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56124     },
56125
56126     /**
56127      * Refreshes the grid
56128      * @param {Boolean} headersToo
56129      */
56130     refresh : function(headersToo){
56131         this.fireEvent("beforerefresh", this);
56132         this.grid.stopEditing();
56133         var result = this.renderBody();
56134         this.lockedBody.update(result[0]);
56135         this.mainBody.update(result[1]);
56136         if(headersToo === true){
56137             this.updateHeaders();
56138             this.updateColumns();
56139             this.updateSplitters();
56140             this.updateHeaderSortState();
56141         }
56142         this.syncRowHeights();
56143         this.layout();
56144         this.fireEvent("refresh", this);
56145     },
56146
56147     handleColumnMove : function(cm, oldIndex, newIndex){
56148         this.indexMap = null;
56149         var s = this.getScrollState();
56150         this.refresh(true);
56151         this.restoreScroll(s);
56152         this.afterMove(newIndex);
56153     },
56154
56155     afterMove : function(colIndex){
56156         if(this.enableMoveAnim && Roo.enableFx){
56157             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56158         }
56159         // if multisort - fix sortOrder, and reload..
56160         if (this.grid.dataSource.multiSort) {
56161             // the we can call sort again..
56162             var dm = this.grid.dataSource;
56163             var cm = this.grid.colModel;
56164             var so = [];
56165             for(var i = 0; i < cm.config.length; i++ ) {
56166                 
56167                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56168                     continue; // dont' bother, it's not in sort list or being set.
56169                 }
56170                 
56171                 so.push(cm.config[i].dataIndex);
56172             };
56173             dm.sortOrder = so;
56174             dm.load(dm.lastOptions);
56175             
56176             
56177         }
56178         
56179     },
56180
56181     updateCell : function(dm, rowIndex, dataIndex){
56182         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56183         if(typeof colIndex == "undefined"){ // not present in grid
56184             return;
56185         }
56186         var cm = this.grid.colModel;
56187         var cell = this.getCell(rowIndex, colIndex);
56188         var cellText = this.getCellText(rowIndex, colIndex);
56189
56190         var p = {
56191             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56192             id : cm.getColumnId(colIndex),
56193             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56194         };
56195         var renderer = cm.getRenderer(colIndex);
56196         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56197         if(typeof val == "undefined" || val === "") {
56198             val = "&#160;";
56199         }
56200         cellText.innerHTML = val;
56201         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56202         this.syncRowHeights(rowIndex, rowIndex);
56203     },
56204
56205     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56206         var maxWidth = 0;
56207         if(this.grid.autoSizeHeaders){
56208             var h = this.getHeaderCellMeasure(colIndex);
56209             maxWidth = Math.max(maxWidth, h.scrollWidth);
56210         }
56211         var tb, index;
56212         if(this.cm.isLocked(colIndex)){
56213             tb = this.getLockedTable();
56214             index = colIndex;
56215         }else{
56216             tb = this.getBodyTable();
56217             index = colIndex - this.cm.getLockedCount();
56218         }
56219         if(tb && tb.rows){
56220             var rows = tb.rows;
56221             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56222             for(var i = 0; i < stopIndex; i++){
56223                 var cell = rows[i].childNodes[index].firstChild;
56224                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56225             }
56226         }
56227         return maxWidth + /*margin for error in IE*/ 5;
56228     },
56229     /**
56230      * Autofit a column to its content.
56231      * @param {Number} colIndex
56232      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56233      */
56234      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56235          if(this.cm.isHidden(colIndex)){
56236              return; // can't calc a hidden column
56237          }
56238         if(forceMinSize){
56239             var cid = this.cm.getColumnId(colIndex);
56240             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56241            if(this.grid.autoSizeHeaders){
56242                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56243            }
56244         }
56245         var newWidth = this.calcColumnWidth(colIndex);
56246         this.cm.setColumnWidth(colIndex,
56247             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56248         if(!suppressEvent){
56249             this.grid.fireEvent("columnresize", colIndex, newWidth);
56250         }
56251     },
56252
56253     /**
56254      * Autofits all columns to their content and then expands to fit any extra space in the grid
56255      */
56256      autoSizeColumns : function(){
56257         var cm = this.grid.colModel;
56258         var colCount = cm.getColumnCount();
56259         for(var i = 0; i < colCount; i++){
56260             this.autoSizeColumn(i, true, true);
56261         }
56262         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56263             this.fitColumns();
56264         }else{
56265             this.updateColumns();
56266             this.layout();
56267         }
56268     },
56269
56270     /**
56271      * Autofits all columns to the grid's width proportionate with their current size
56272      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56273      */
56274     fitColumns : function(reserveScrollSpace){
56275         var cm = this.grid.colModel;
56276         var colCount = cm.getColumnCount();
56277         var cols = [];
56278         var width = 0;
56279         var i, w;
56280         for (i = 0; i < colCount; i++){
56281             if(!cm.isHidden(i) && !cm.isFixed(i)){
56282                 w = cm.getColumnWidth(i);
56283                 cols.push(i);
56284                 cols.push(w);
56285                 width += w;
56286             }
56287         }
56288         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56289         if(reserveScrollSpace){
56290             avail -= 17;
56291         }
56292         var frac = (avail - cm.getTotalWidth())/width;
56293         while (cols.length){
56294             w = cols.pop();
56295             i = cols.pop();
56296             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56297         }
56298         this.updateColumns();
56299         this.layout();
56300     },
56301
56302     onRowSelect : function(rowIndex){
56303         var row = this.getRowComposite(rowIndex);
56304         row.addClass("x-grid-row-selected");
56305     },
56306
56307     onRowDeselect : function(rowIndex){
56308         var row = this.getRowComposite(rowIndex);
56309         row.removeClass("x-grid-row-selected");
56310     },
56311
56312     onCellSelect : function(row, col){
56313         var cell = this.getCell(row, col);
56314         if(cell){
56315             Roo.fly(cell).addClass("x-grid-cell-selected");
56316         }
56317     },
56318
56319     onCellDeselect : function(row, col){
56320         var cell = this.getCell(row, col);
56321         if(cell){
56322             Roo.fly(cell).removeClass("x-grid-cell-selected");
56323         }
56324     },
56325
56326     updateHeaderSortState : function(){
56327         
56328         // sort state can be single { field: xxx, direction : yyy}
56329         // or   { xxx=>ASC , yyy : DESC ..... }
56330         
56331         var mstate = {};
56332         if (!this.ds.multiSort) { 
56333             var state = this.ds.getSortState();
56334             if(!state){
56335                 return;
56336             }
56337             mstate[state.field] = state.direction;
56338             // FIXME... - this is not used here.. but might be elsewhere..
56339             this.sortState = state;
56340             
56341         } else {
56342             mstate = this.ds.sortToggle;
56343         }
56344         //remove existing sort classes..
56345         
56346         var sc = this.sortClasses;
56347         var hds = this.el.select(this.headerSelector).removeClass(sc);
56348         
56349         for(var f in mstate) {
56350         
56351             var sortColumn = this.cm.findColumnIndex(f);
56352             
56353             if(sortColumn != -1){
56354                 var sortDir = mstate[f];        
56355                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56356             }
56357         }
56358         
56359          
56360         
56361     },
56362
56363
56364     handleHeaderClick : function(g, index,e){
56365         
56366         Roo.log("header click");
56367         
56368         if (Roo.isTouch) {
56369             // touch events on header are handled by context
56370             this.handleHdCtx(g,index,e);
56371             return;
56372         }
56373         
56374         
56375         if(this.headersDisabled){
56376             return;
56377         }
56378         var dm = g.dataSource, cm = g.colModel;
56379         if(!cm.isSortable(index)){
56380             return;
56381         }
56382         g.stopEditing();
56383         
56384         if (dm.multiSort) {
56385             // update the sortOrder
56386             var so = [];
56387             for(var i = 0; i < cm.config.length; i++ ) {
56388                 
56389                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56390                     continue; // dont' bother, it's not in sort list or being set.
56391                 }
56392                 
56393                 so.push(cm.config[i].dataIndex);
56394             };
56395             dm.sortOrder = so;
56396         }
56397         
56398         
56399         dm.sort(cm.getDataIndex(index));
56400     },
56401
56402
56403     destroy : function(){
56404         if(this.colMenu){
56405             this.colMenu.removeAll();
56406             Roo.menu.MenuMgr.unregister(this.colMenu);
56407             this.colMenu.getEl().remove();
56408             delete this.colMenu;
56409         }
56410         if(this.hmenu){
56411             this.hmenu.removeAll();
56412             Roo.menu.MenuMgr.unregister(this.hmenu);
56413             this.hmenu.getEl().remove();
56414             delete this.hmenu;
56415         }
56416         if(this.grid.enableColumnMove){
56417             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56418             if(dds){
56419                 for(var dd in dds){
56420                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56421                         var elid = dds[dd].dragElId;
56422                         dds[dd].unreg();
56423                         Roo.get(elid).remove();
56424                     } else if(dds[dd].config.isTarget){
56425                         dds[dd].proxyTop.remove();
56426                         dds[dd].proxyBottom.remove();
56427                         dds[dd].unreg();
56428                     }
56429                     if(Roo.dd.DDM.locationCache[dd]){
56430                         delete Roo.dd.DDM.locationCache[dd];
56431                     }
56432                 }
56433                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56434             }
56435         }
56436         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56437         this.bind(null, null);
56438         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56439     },
56440
56441     handleLockChange : function(){
56442         this.refresh(true);
56443     },
56444
56445     onDenyColumnLock : function(){
56446
56447     },
56448
56449     onDenyColumnHide : function(){
56450
56451     },
56452
56453     handleHdMenuClick : function(item){
56454         var index = this.hdCtxIndex;
56455         var cm = this.cm, ds = this.ds;
56456         switch(item.id){
56457             case "asc":
56458                 ds.sort(cm.getDataIndex(index), "ASC");
56459                 break;
56460             case "desc":
56461                 ds.sort(cm.getDataIndex(index), "DESC");
56462                 break;
56463             case "lock":
56464                 var lc = cm.getLockedCount();
56465                 if(cm.getColumnCount(true) <= lc+1){
56466                     this.onDenyColumnLock();
56467                     return;
56468                 }
56469                 if(lc != index){
56470                     cm.setLocked(index, true, true);
56471                     cm.moveColumn(index, lc);
56472                     this.grid.fireEvent("columnmove", index, lc);
56473                 }else{
56474                     cm.setLocked(index, true);
56475                 }
56476             break;
56477             case "unlock":
56478                 var lc = cm.getLockedCount();
56479                 if((lc-1) != index){
56480                     cm.setLocked(index, false, true);
56481                     cm.moveColumn(index, lc-1);
56482                     this.grid.fireEvent("columnmove", index, lc-1);
56483                 }else{
56484                     cm.setLocked(index, false);
56485                 }
56486             break;
56487             case 'wider': // used to expand cols on touch..
56488             case 'narrow':
56489                 var cw = cm.getColumnWidth(index);
56490                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56491                 cw = Math.max(0, cw);
56492                 cw = Math.min(cw,4000);
56493                 cm.setColumnWidth(index, cw);
56494                 break;
56495                 
56496             default:
56497                 index = cm.getIndexById(item.id.substr(4));
56498                 if(index != -1){
56499                     if(item.checked && cm.getColumnCount(true) <= 1){
56500                         this.onDenyColumnHide();
56501                         return false;
56502                     }
56503                     cm.setHidden(index, item.checked);
56504                 }
56505         }
56506         return true;
56507     },
56508
56509     beforeColMenuShow : function(){
56510         var cm = this.cm,  colCount = cm.getColumnCount();
56511         this.colMenu.removeAll();
56512         for(var i = 0; i < colCount; i++){
56513             this.colMenu.add(new Roo.menu.CheckItem({
56514                 id: "col-"+cm.getColumnId(i),
56515                 text: cm.getColumnHeader(i),
56516                 checked: !cm.isHidden(i),
56517                 hideOnClick:false
56518             }));
56519         }
56520     },
56521
56522     handleHdCtx : function(g, index, e){
56523         e.stopEvent();
56524         var hd = this.getHeaderCell(index);
56525         this.hdCtxIndex = index;
56526         var ms = this.hmenu.items, cm = this.cm;
56527         ms.get("asc").setDisabled(!cm.isSortable(index));
56528         ms.get("desc").setDisabled(!cm.isSortable(index));
56529         if(this.grid.enableColLock !== false){
56530             ms.get("lock").setDisabled(cm.isLocked(index));
56531             ms.get("unlock").setDisabled(!cm.isLocked(index));
56532         }
56533         this.hmenu.show(hd, "tl-bl");
56534     },
56535
56536     handleHdOver : function(e){
56537         var hd = this.findHeaderCell(e.getTarget());
56538         if(hd && !this.headersDisabled){
56539             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56540                this.fly(hd).addClass("x-grid-hd-over");
56541             }
56542         }
56543     },
56544
56545     handleHdOut : function(e){
56546         var hd = this.findHeaderCell(e.getTarget());
56547         if(hd){
56548             this.fly(hd).removeClass("x-grid-hd-over");
56549         }
56550     },
56551
56552     handleSplitDblClick : function(e, t){
56553         var i = this.getCellIndex(t);
56554         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56555             this.autoSizeColumn(i, true);
56556             this.layout();
56557         }
56558     },
56559
56560     render : function(){
56561
56562         var cm = this.cm;
56563         var colCount = cm.getColumnCount();
56564
56565         if(this.grid.monitorWindowResize === true){
56566             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56567         }
56568         var header = this.renderHeaders();
56569         var body = this.templates.body.apply({rows:""});
56570         var html = this.templates.master.apply({
56571             lockedBody: body,
56572             body: body,
56573             lockedHeader: header[0],
56574             header: header[1]
56575         });
56576
56577         //this.updateColumns();
56578
56579         this.grid.getGridEl().dom.innerHTML = html;
56580
56581         this.initElements();
56582         
56583         // a kludge to fix the random scolling effect in webkit
56584         this.el.on("scroll", function() {
56585             this.el.dom.scrollTop=0; // hopefully not recursive..
56586         },this);
56587
56588         this.scroller.on("scroll", this.handleScroll, this);
56589         this.lockedBody.on("mousewheel", this.handleWheel, this);
56590         this.mainBody.on("mousewheel", this.handleWheel, this);
56591
56592         this.mainHd.on("mouseover", this.handleHdOver, this);
56593         this.mainHd.on("mouseout", this.handleHdOut, this);
56594         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56595                 {delegate: "."+this.splitClass});
56596
56597         this.lockedHd.on("mouseover", this.handleHdOver, this);
56598         this.lockedHd.on("mouseout", this.handleHdOut, this);
56599         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56600                 {delegate: "."+this.splitClass});
56601
56602         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56603             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56604         }
56605
56606         this.updateSplitters();
56607
56608         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56609             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56610             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56611         }
56612
56613         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56614             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56615             this.hmenu.add(
56616                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56617                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56618             );
56619             if(this.grid.enableColLock !== false){
56620                 this.hmenu.add('-',
56621                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56622                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56623                 );
56624             }
56625             if (Roo.isTouch) {
56626                  this.hmenu.add('-',
56627                     {id:"wider", text: this.columnsWiderText},
56628                     {id:"narrow", text: this.columnsNarrowText }
56629                 );
56630                 
56631                  
56632             }
56633             
56634             if(this.grid.enableColumnHide !== false){
56635
56636                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56637                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56638                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56639
56640                 this.hmenu.add('-',
56641                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56642                 );
56643             }
56644             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56645
56646             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56647         }
56648
56649         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56650             this.dd = new Roo.grid.GridDragZone(this.grid, {
56651                 ddGroup : this.grid.ddGroup || 'GridDD'
56652             });
56653             
56654         }
56655
56656         /*
56657         for(var i = 0; i < colCount; i++){
56658             if(cm.isHidden(i)){
56659                 this.hideColumn(i);
56660             }
56661             if(cm.config[i].align){
56662                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56663                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56664             }
56665         }*/
56666         
56667         this.updateHeaderSortState();
56668
56669         this.beforeInitialResize();
56670         this.layout(true);
56671
56672         // two part rendering gives faster view to the user
56673         this.renderPhase2.defer(1, this);
56674     },
56675
56676     renderPhase2 : function(){
56677         // render the rows now
56678         this.refresh();
56679         if(this.grid.autoSizeColumns){
56680             this.autoSizeColumns();
56681         }
56682     },
56683
56684     beforeInitialResize : function(){
56685
56686     },
56687
56688     onColumnSplitterMoved : function(i, w){
56689         this.userResized = true;
56690         var cm = this.grid.colModel;
56691         cm.setColumnWidth(i, w, true);
56692         var cid = cm.getColumnId(i);
56693         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56694         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56695         this.updateSplitters();
56696         this.layout();
56697         this.grid.fireEvent("columnresize", i, w);
56698     },
56699
56700     syncRowHeights : function(startIndex, endIndex){
56701         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56702             startIndex = startIndex || 0;
56703             var mrows = this.getBodyTable().rows;
56704             var lrows = this.getLockedTable().rows;
56705             var len = mrows.length-1;
56706             endIndex = Math.min(endIndex || len, len);
56707             for(var i = startIndex; i <= endIndex; i++){
56708                 var m = mrows[i], l = lrows[i];
56709                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56710                 m.style.height = l.style.height = h + "px";
56711             }
56712         }
56713     },
56714
56715     layout : function(initialRender, is2ndPass){
56716         var g = this.grid;
56717         var auto = g.autoHeight;
56718         var scrollOffset = 16;
56719         var c = g.getGridEl(), cm = this.cm,
56720                 expandCol = g.autoExpandColumn,
56721                 gv = this;
56722         //c.beginMeasure();
56723
56724         if(!c.dom.offsetWidth){ // display:none?
56725             if(initialRender){
56726                 this.lockedWrap.show();
56727                 this.mainWrap.show();
56728             }
56729             return;
56730         }
56731
56732         var hasLock = this.cm.isLocked(0);
56733
56734         var tbh = this.headerPanel.getHeight();
56735         var bbh = this.footerPanel.getHeight();
56736
56737         if(auto){
56738             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56739             var newHeight = ch + c.getBorderWidth("tb");
56740             if(g.maxHeight){
56741                 newHeight = Math.min(g.maxHeight, newHeight);
56742             }
56743             c.setHeight(newHeight);
56744         }
56745
56746         if(g.autoWidth){
56747             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56748         }
56749
56750         var s = this.scroller;
56751
56752         var csize = c.getSize(true);
56753
56754         this.el.setSize(csize.width, csize.height);
56755
56756         this.headerPanel.setWidth(csize.width);
56757         this.footerPanel.setWidth(csize.width);
56758
56759         var hdHeight = this.mainHd.getHeight();
56760         var vw = csize.width;
56761         var vh = csize.height - (tbh + bbh);
56762
56763         s.setSize(vw, vh);
56764
56765         var bt = this.getBodyTable();
56766         
56767         if(cm.getLockedCount() == cm.config.length){
56768             bt = this.getLockedTable();
56769         }
56770         
56771         var ltWidth = hasLock ?
56772                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56773
56774         var scrollHeight = bt.offsetHeight;
56775         var scrollWidth = ltWidth + bt.offsetWidth;
56776         var vscroll = false, hscroll = false;
56777
56778         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56779
56780         var lw = this.lockedWrap, mw = this.mainWrap;
56781         var lb = this.lockedBody, mb = this.mainBody;
56782
56783         setTimeout(function(){
56784             var t = s.dom.offsetTop;
56785             var w = s.dom.clientWidth,
56786                 h = s.dom.clientHeight;
56787
56788             lw.setTop(t);
56789             lw.setSize(ltWidth, h);
56790
56791             mw.setLeftTop(ltWidth, t);
56792             mw.setSize(w-ltWidth, h);
56793
56794             lb.setHeight(h-hdHeight);
56795             mb.setHeight(h-hdHeight);
56796
56797             if(is2ndPass !== true && !gv.userResized && expandCol){
56798                 // high speed resize without full column calculation
56799                 
56800                 var ci = cm.getIndexById(expandCol);
56801                 if (ci < 0) {
56802                     ci = cm.findColumnIndex(expandCol);
56803                 }
56804                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56805                 var expandId = cm.getColumnId(ci);
56806                 var  tw = cm.getTotalWidth(false);
56807                 var currentWidth = cm.getColumnWidth(ci);
56808                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56809                 if(currentWidth != cw){
56810                     cm.setColumnWidth(ci, cw, true);
56811                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56812                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56813                     gv.updateSplitters();
56814                     gv.layout(false, true);
56815                 }
56816             }
56817
56818             if(initialRender){
56819                 lw.show();
56820                 mw.show();
56821             }
56822             //c.endMeasure();
56823         }, 10);
56824     },
56825
56826     onWindowResize : function(){
56827         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56828             return;
56829         }
56830         this.layout();
56831     },
56832
56833     appendFooter : function(parentEl){
56834         return null;
56835     },
56836
56837     sortAscText : "Sort Ascending",
56838     sortDescText : "Sort Descending",
56839     lockText : "Lock Column",
56840     unlockText : "Unlock Column",
56841     columnsText : "Columns",
56842  
56843     columnsWiderText : "Wider",
56844     columnsNarrowText : "Thinner"
56845 });
56846
56847
56848 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56849     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56850     this.proxy.el.addClass('x-grid3-col-dd');
56851 };
56852
56853 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56854     handleMouseDown : function(e){
56855
56856     },
56857
56858     callHandleMouseDown : function(e){
56859         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56860     }
56861 });
56862 /*
56863  * Based on:
56864  * Ext JS Library 1.1.1
56865  * Copyright(c) 2006-2007, Ext JS, LLC.
56866  *
56867  * Originally Released Under LGPL - original licence link has changed is not relivant.
56868  *
56869  * Fork - LGPL
56870  * <script type="text/javascript">
56871  */
56872  
56873 // private
56874 // This is a support class used internally by the Grid components
56875 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56876     this.grid = grid;
56877     this.view = grid.getView();
56878     this.proxy = this.view.resizeProxy;
56879     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56880         "gridSplitters" + this.grid.getGridEl().id, {
56881         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56882     });
56883     this.setHandleElId(Roo.id(hd));
56884     this.setOuterHandleElId(Roo.id(hd2));
56885     this.scroll = false;
56886 };
56887 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56888     fly: Roo.Element.fly,
56889
56890     b4StartDrag : function(x, y){
56891         this.view.headersDisabled = true;
56892         this.proxy.setHeight(this.view.mainWrap.getHeight());
56893         var w = this.cm.getColumnWidth(this.cellIndex);
56894         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56895         this.resetConstraints();
56896         this.setXConstraint(minw, 1000);
56897         this.setYConstraint(0, 0);
56898         this.minX = x - minw;
56899         this.maxX = x + 1000;
56900         this.startPos = x;
56901         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56902     },
56903
56904
56905     handleMouseDown : function(e){
56906         ev = Roo.EventObject.setEvent(e);
56907         var t = this.fly(ev.getTarget());
56908         if(t.hasClass("x-grid-split")){
56909             this.cellIndex = this.view.getCellIndex(t.dom);
56910             this.split = t.dom;
56911             this.cm = this.grid.colModel;
56912             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56913                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56914             }
56915         }
56916     },
56917
56918     endDrag : function(e){
56919         this.view.headersDisabled = false;
56920         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56921         var diff = endX - this.startPos;
56922         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56923     },
56924
56925     autoOffset : function(){
56926         this.setDelta(0,0);
56927     }
56928 });/*
56929  * Based on:
56930  * Ext JS Library 1.1.1
56931  * Copyright(c) 2006-2007, Ext JS, LLC.
56932  *
56933  * Originally Released Under LGPL - original licence link has changed is not relivant.
56934  *
56935  * Fork - LGPL
56936  * <script type="text/javascript">
56937  */
56938  
56939 // private
56940 // This is a support class used internally by the Grid components
56941 Roo.grid.GridDragZone = function(grid, config){
56942     this.view = grid.getView();
56943     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56944     if(this.view.lockedBody){
56945         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56946         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56947     }
56948     this.scroll = false;
56949     this.grid = grid;
56950     this.ddel = document.createElement('div');
56951     this.ddel.className = 'x-grid-dd-wrap';
56952 };
56953
56954 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56955     ddGroup : "GridDD",
56956
56957     getDragData : function(e){
56958         var t = Roo.lib.Event.getTarget(e);
56959         var rowIndex = this.view.findRowIndex(t);
56960         var sm = this.grid.selModel;
56961             
56962         //Roo.log(rowIndex);
56963         
56964         if (sm.getSelectedCell) {
56965             // cell selection..
56966             if (!sm.getSelectedCell()) {
56967                 return false;
56968             }
56969             if (rowIndex != sm.getSelectedCell()[0]) {
56970                 return false;
56971             }
56972         
56973         }
56974         
56975         if(rowIndex !== false){
56976             
56977             // if editorgrid.. 
56978             
56979             
56980             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56981                
56982             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56983               //  
56984             //}
56985             if (e.hasModifier()){
56986                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56987             }
56988             
56989             Roo.log("getDragData");
56990             
56991             return {
56992                 grid: this.grid,
56993                 ddel: this.ddel,
56994                 rowIndex: rowIndex,
56995                 selections:sm.getSelections ? sm.getSelections() : (
56996                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56997                 )
56998             };
56999         }
57000         return false;
57001     },
57002
57003     onInitDrag : function(e){
57004         var data = this.dragData;
57005         this.ddel.innerHTML = this.grid.getDragDropText();
57006         this.proxy.update(this.ddel);
57007         // fire start drag?
57008     },
57009
57010     afterRepair : function(){
57011         this.dragging = false;
57012     },
57013
57014     getRepairXY : function(e, data){
57015         return false;
57016     },
57017
57018     onEndDrag : function(data, e){
57019         // fire end drag?
57020     },
57021
57022     onValidDrop : function(dd, e, id){
57023         // fire drag drop?
57024         this.hideProxy();
57025     },
57026
57027     beforeInvalidDrop : function(e, id){
57028
57029     }
57030 });/*
57031  * Based on:
57032  * Ext JS Library 1.1.1
57033  * Copyright(c) 2006-2007, Ext JS, LLC.
57034  *
57035  * Originally Released Under LGPL - original licence link has changed is not relivant.
57036  *
57037  * Fork - LGPL
57038  * <script type="text/javascript">
57039  */
57040  
57041
57042 /**
57043  * @class Roo.grid.ColumnModel
57044  * @extends Roo.util.Observable
57045  * This is the default implementation of a ColumnModel used by the Grid. It defines
57046  * the columns in the grid.
57047  * <br>Usage:<br>
57048  <pre><code>
57049  var colModel = new Roo.grid.ColumnModel([
57050         {header: "Ticker", width: 60, sortable: true, locked: true},
57051         {header: "Company Name", width: 150, sortable: true},
57052         {header: "Market Cap.", width: 100, sortable: true},
57053         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57054         {header: "Employees", width: 100, sortable: true, resizable: false}
57055  ]);
57056  </code></pre>
57057  * <p>
57058  
57059  * The config options listed for this class are options which may appear in each
57060  * individual column definition.
57061  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57062  * @constructor
57063  * @param {Object} config An Array of column config objects. See this class's
57064  * config objects for details.
57065 */
57066 Roo.grid.ColumnModel = function(config){
57067         /**
57068      * The config passed into the constructor
57069      */
57070     this.config = config;
57071     this.lookup = {};
57072
57073     // if no id, create one
57074     // if the column does not have a dataIndex mapping,
57075     // map it to the order it is in the config
57076     for(var i = 0, len = config.length; i < len; i++){
57077         var c = config[i];
57078         if(typeof c.dataIndex == "undefined"){
57079             c.dataIndex = i;
57080         }
57081         if(typeof c.renderer == "string"){
57082             c.renderer = Roo.util.Format[c.renderer];
57083         }
57084         if(typeof c.id == "undefined"){
57085             c.id = Roo.id();
57086         }
57087         if(c.editor && c.editor.xtype){
57088             c.editor  = Roo.factory(c.editor, Roo.grid);
57089         }
57090         if(c.editor && c.editor.isFormField){
57091             c.editor = new Roo.grid.GridEditor(c.editor);
57092         }
57093         this.lookup[c.id] = c;
57094     }
57095
57096     /**
57097      * The width of columns which have no width specified (defaults to 100)
57098      * @type Number
57099      */
57100     this.defaultWidth = 100;
57101
57102     /**
57103      * Default sortable of columns which have no sortable specified (defaults to false)
57104      * @type Boolean
57105      */
57106     this.defaultSortable = false;
57107
57108     this.addEvents({
57109         /**
57110              * @event widthchange
57111              * Fires when the width of a column changes.
57112              * @param {ColumnModel} this
57113              * @param {Number} columnIndex The column index
57114              * @param {Number} newWidth The new width
57115              */
57116             "widthchange": true,
57117         /**
57118              * @event headerchange
57119              * Fires when the text of a header changes.
57120              * @param {ColumnModel} this
57121              * @param {Number} columnIndex The column index
57122              * @param {Number} newText The new header text
57123              */
57124             "headerchange": true,
57125         /**
57126              * @event hiddenchange
57127              * Fires when a column is hidden or "unhidden".
57128              * @param {ColumnModel} this
57129              * @param {Number} columnIndex The column index
57130              * @param {Boolean} hidden true if hidden, false otherwise
57131              */
57132             "hiddenchange": true,
57133             /**
57134          * @event columnmoved
57135          * Fires when a column is moved.
57136          * @param {ColumnModel} this
57137          * @param {Number} oldIndex
57138          * @param {Number} newIndex
57139          */
57140         "columnmoved" : true,
57141         /**
57142          * @event columlockchange
57143          * Fires when a column's locked state is changed
57144          * @param {ColumnModel} this
57145          * @param {Number} colIndex
57146          * @param {Boolean} locked true if locked
57147          */
57148         "columnlockchange" : true
57149     });
57150     Roo.grid.ColumnModel.superclass.constructor.call(this);
57151 };
57152 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57153     /**
57154      * @cfg {String} header The header text to display in the Grid view.
57155      */
57156     /**
57157      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57158      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57159      * specified, the column's index is used as an index into the Record's data Array.
57160      */
57161     /**
57162      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57163      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57164      */
57165     /**
57166      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57167      * Defaults to the value of the {@link #defaultSortable} property.
57168      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57169      */
57170     /**
57171      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57172      */
57173     /**
57174      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57175      */
57176     /**
57177      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57178      */
57179     /**
57180      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57181      */
57182     /**
57183      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57184      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57185      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57186      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57187      */
57188        /**
57189      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57190      */
57191     /**
57192      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57193      */
57194     /**
57195      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57196      */
57197     /**
57198      * @cfg {String} cursor (Optional)
57199      */
57200     /**
57201      * @cfg {String} tooltip (Optional)
57202      */
57203     /**
57204      * @cfg {Number} xs (Optional)
57205      */
57206     /**
57207      * @cfg {Number} sm (Optional)
57208      */
57209     /**
57210      * @cfg {Number} md (Optional)
57211      */
57212     /**
57213      * @cfg {Number} lg (Optional)
57214      */
57215     /**
57216      * Returns the id of the column at the specified index.
57217      * @param {Number} index The column index
57218      * @return {String} the id
57219      */
57220     getColumnId : function(index){
57221         return this.config[index].id;
57222     },
57223
57224     /**
57225      * Returns the column for a specified id.
57226      * @param {String} id The column id
57227      * @return {Object} the column
57228      */
57229     getColumnById : function(id){
57230         return this.lookup[id];
57231     },
57232
57233     
57234     /**
57235      * Returns the column for a specified dataIndex.
57236      * @param {String} dataIndex The column dataIndex
57237      * @return {Object|Boolean} the column or false if not found
57238      */
57239     getColumnByDataIndex: function(dataIndex){
57240         var index = this.findColumnIndex(dataIndex);
57241         return index > -1 ? this.config[index] : false;
57242     },
57243     
57244     /**
57245      * Returns the index for a specified column id.
57246      * @param {String} id The column id
57247      * @return {Number} the index, or -1 if not found
57248      */
57249     getIndexById : function(id){
57250         for(var i = 0, len = this.config.length; i < len; i++){
57251             if(this.config[i].id == id){
57252                 return i;
57253             }
57254         }
57255         return -1;
57256     },
57257     
57258     /**
57259      * Returns the index for a specified column dataIndex.
57260      * @param {String} dataIndex The column dataIndex
57261      * @return {Number} the index, or -1 if not found
57262      */
57263     
57264     findColumnIndex : function(dataIndex){
57265         for(var i = 0, len = this.config.length; i < len; i++){
57266             if(this.config[i].dataIndex == dataIndex){
57267                 return i;
57268             }
57269         }
57270         return -1;
57271     },
57272     
57273     
57274     moveColumn : function(oldIndex, newIndex){
57275         var c = this.config[oldIndex];
57276         this.config.splice(oldIndex, 1);
57277         this.config.splice(newIndex, 0, c);
57278         this.dataMap = null;
57279         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57280     },
57281
57282     isLocked : function(colIndex){
57283         return this.config[colIndex].locked === true;
57284     },
57285
57286     setLocked : function(colIndex, value, suppressEvent){
57287         if(this.isLocked(colIndex) == value){
57288             return;
57289         }
57290         this.config[colIndex].locked = value;
57291         if(!suppressEvent){
57292             this.fireEvent("columnlockchange", this, colIndex, value);
57293         }
57294     },
57295
57296     getTotalLockedWidth : function(){
57297         var totalWidth = 0;
57298         for(var i = 0; i < this.config.length; i++){
57299             if(this.isLocked(i) && !this.isHidden(i)){
57300                 this.totalWidth += this.getColumnWidth(i);
57301             }
57302         }
57303         return totalWidth;
57304     },
57305
57306     getLockedCount : function(){
57307         for(var i = 0, len = this.config.length; i < len; i++){
57308             if(!this.isLocked(i)){
57309                 return i;
57310             }
57311         }
57312         
57313         return this.config.length;
57314     },
57315
57316     /**
57317      * Returns the number of columns.
57318      * @return {Number}
57319      */
57320     getColumnCount : function(visibleOnly){
57321         if(visibleOnly === true){
57322             var c = 0;
57323             for(var i = 0, len = this.config.length; i < len; i++){
57324                 if(!this.isHidden(i)){
57325                     c++;
57326                 }
57327             }
57328             return c;
57329         }
57330         return this.config.length;
57331     },
57332
57333     /**
57334      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57335      * @param {Function} fn
57336      * @param {Object} scope (optional)
57337      * @return {Array} result
57338      */
57339     getColumnsBy : function(fn, scope){
57340         var r = [];
57341         for(var i = 0, len = this.config.length; i < len; i++){
57342             var c = this.config[i];
57343             if(fn.call(scope||this, c, i) === true){
57344                 r[r.length] = c;
57345             }
57346         }
57347         return r;
57348     },
57349
57350     /**
57351      * Returns true if the specified column is sortable.
57352      * @param {Number} col The column index
57353      * @return {Boolean}
57354      */
57355     isSortable : function(col){
57356         if(typeof this.config[col].sortable == "undefined"){
57357             return this.defaultSortable;
57358         }
57359         return this.config[col].sortable;
57360     },
57361
57362     /**
57363      * Returns the rendering (formatting) function defined for the column.
57364      * @param {Number} col The column index.
57365      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57366      */
57367     getRenderer : function(col){
57368         if(!this.config[col].renderer){
57369             return Roo.grid.ColumnModel.defaultRenderer;
57370         }
57371         return this.config[col].renderer;
57372     },
57373
57374     /**
57375      * Sets the rendering (formatting) function for a column.
57376      * @param {Number} col The column index
57377      * @param {Function} fn The function to use to process the cell's raw data
57378      * to return HTML markup for the grid view. The render function is called with
57379      * the following parameters:<ul>
57380      * <li>Data value.</li>
57381      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57382      * <li>css A CSS style string to apply to the table cell.</li>
57383      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57384      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57385      * <li>Row index</li>
57386      * <li>Column index</li>
57387      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57388      */
57389     setRenderer : function(col, fn){
57390         this.config[col].renderer = fn;
57391     },
57392
57393     /**
57394      * Returns the width for the specified column.
57395      * @param {Number} col The column index
57396      * @return {Number}
57397      */
57398     getColumnWidth : function(col){
57399         return this.config[col].width * 1 || this.defaultWidth;
57400     },
57401
57402     /**
57403      * Sets the width for a column.
57404      * @param {Number} col The column index
57405      * @param {Number} width The new width
57406      */
57407     setColumnWidth : function(col, width, suppressEvent){
57408         this.config[col].width = width;
57409         this.totalWidth = null;
57410         if(!suppressEvent){
57411              this.fireEvent("widthchange", this, col, width);
57412         }
57413     },
57414
57415     /**
57416      * Returns the total width of all columns.
57417      * @param {Boolean} includeHidden True to include hidden column widths
57418      * @return {Number}
57419      */
57420     getTotalWidth : function(includeHidden){
57421         if(!this.totalWidth){
57422             this.totalWidth = 0;
57423             for(var i = 0, len = this.config.length; i < len; i++){
57424                 if(includeHidden || !this.isHidden(i)){
57425                     this.totalWidth += this.getColumnWidth(i);
57426                 }
57427             }
57428         }
57429         return this.totalWidth;
57430     },
57431
57432     /**
57433      * Returns the header for the specified column.
57434      * @param {Number} col The column index
57435      * @return {String}
57436      */
57437     getColumnHeader : function(col){
57438         return this.config[col].header;
57439     },
57440
57441     /**
57442      * Sets the header for a column.
57443      * @param {Number} col The column index
57444      * @param {String} header The new header
57445      */
57446     setColumnHeader : function(col, header){
57447         this.config[col].header = header;
57448         this.fireEvent("headerchange", this, col, header);
57449     },
57450
57451     /**
57452      * Returns the tooltip for the specified column.
57453      * @param {Number} col The column index
57454      * @return {String}
57455      */
57456     getColumnTooltip : function(col){
57457             return this.config[col].tooltip;
57458     },
57459     /**
57460      * Sets the tooltip for a column.
57461      * @param {Number} col The column index
57462      * @param {String} tooltip The new tooltip
57463      */
57464     setColumnTooltip : function(col, tooltip){
57465             this.config[col].tooltip = tooltip;
57466     },
57467
57468     /**
57469      * Returns the dataIndex for the specified column.
57470      * @param {Number} col The column index
57471      * @return {Number}
57472      */
57473     getDataIndex : function(col){
57474         return this.config[col].dataIndex;
57475     },
57476
57477     /**
57478      * Sets the dataIndex for a column.
57479      * @param {Number} col The column index
57480      * @param {Number} dataIndex The new dataIndex
57481      */
57482     setDataIndex : function(col, dataIndex){
57483         this.config[col].dataIndex = dataIndex;
57484     },
57485
57486     
57487     
57488     /**
57489      * Returns true if the cell is editable.
57490      * @param {Number} colIndex The column index
57491      * @param {Number} rowIndex The row index - this is nto actually used..?
57492      * @return {Boolean}
57493      */
57494     isCellEditable : function(colIndex, rowIndex){
57495         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57496     },
57497
57498     /**
57499      * Returns the editor defined for the cell/column.
57500      * return false or null to disable editing.
57501      * @param {Number} colIndex The column index
57502      * @param {Number} rowIndex The row index
57503      * @return {Object}
57504      */
57505     getCellEditor : function(colIndex, rowIndex){
57506         return this.config[colIndex].editor;
57507     },
57508
57509     /**
57510      * Sets if a column is editable.
57511      * @param {Number} col The column index
57512      * @param {Boolean} editable True if the column is editable
57513      */
57514     setEditable : function(col, editable){
57515         this.config[col].editable = editable;
57516     },
57517
57518
57519     /**
57520      * Returns true if the column is hidden.
57521      * @param {Number} colIndex The column index
57522      * @return {Boolean}
57523      */
57524     isHidden : function(colIndex){
57525         return this.config[colIndex].hidden;
57526     },
57527
57528
57529     /**
57530      * Returns true if the column width cannot be changed
57531      */
57532     isFixed : function(colIndex){
57533         return this.config[colIndex].fixed;
57534     },
57535
57536     /**
57537      * Returns true if the column can be resized
57538      * @return {Boolean}
57539      */
57540     isResizable : function(colIndex){
57541         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57542     },
57543     /**
57544      * Sets if a column is hidden.
57545      * @param {Number} colIndex The column index
57546      * @param {Boolean} hidden True if the column is hidden
57547      */
57548     setHidden : function(colIndex, hidden){
57549         this.config[colIndex].hidden = hidden;
57550         this.totalWidth = null;
57551         this.fireEvent("hiddenchange", this, colIndex, hidden);
57552     },
57553
57554     /**
57555      * Sets the editor for a column.
57556      * @param {Number} col The column index
57557      * @param {Object} editor The editor object
57558      */
57559     setEditor : function(col, editor){
57560         this.config[col].editor = editor;
57561     }
57562 });
57563
57564 Roo.grid.ColumnModel.defaultRenderer = function(value)
57565 {
57566     if(typeof value == "object") {
57567         return value;
57568     }
57569         if(typeof value == "string" && value.length < 1){
57570             return "&#160;";
57571         }
57572     
57573         return String.format("{0}", value);
57574 };
57575
57576 // Alias for backwards compatibility
57577 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57578 /*
57579  * Based on:
57580  * Ext JS Library 1.1.1
57581  * Copyright(c) 2006-2007, Ext JS, LLC.
57582  *
57583  * Originally Released Under LGPL - original licence link has changed is not relivant.
57584  *
57585  * Fork - LGPL
57586  * <script type="text/javascript">
57587  */
57588
57589 /**
57590  * @class Roo.grid.AbstractSelectionModel
57591  * @extends Roo.util.Observable
57592  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57593  * implemented by descendant classes.  This class should not be directly instantiated.
57594  * @constructor
57595  */
57596 Roo.grid.AbstractSelectionModel = function(){
57597     this.locked = false;
57598     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57599 };
57600
57601 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57602     /** @ignore Called by the grid automatically. Do not call directly. */
57603     init : function(grid){
57604         this.grid = grid;
57605         this.initEvents();
57606     },
57607
57608     /**
57609      * Locks the selections.
57610      */
57611     lock : function(){
57612         this.locked = true;
57613     },
57614
57615     /**
57616      * Unlocks the selections.
57617      */
57618     unlock : function(){
57619         this.locked = false;
57620     },
57621
57622     /**
57623      * Returns true if the selections are locked.
57624      * @return {Boolean}
57625      */
57626     isLocked : function(){
57627         return this.locked;
57628     }
57629 });/*
57630  * Based on:
57631  * Ext JS Library 1.1.1
57632  * Copyright(c) 2006-2007, Ext JS, LLC.
57633  *
57634  * Originally Released Under LGPL - original licence link has changed is not relivant.
57635  *
57636  * Fork - LGPL
57637  * <script type="text/javascript">
57638  */
57639 /**
57640  * @extends Roo.grid.AbstractSelectionModel
57641  * @class Roo.grid.RowSelectionModel
57642  * The default SelectionModel used by {@link Roo.grid.Grid}.
57643  * It supports multiple selections and keyboard selection/navigation. 
57644  * @constructor
57645  * @param {Object} config
57646  */
57647 Roo.grid.RowSelectionModel = function(config){
57648     Roo.apply(this, config);
57649     this.selections = new Roo.util.MixedCollection(false, function(o){
57650         return o.id;
57651     });
57652
57653     this.last = false;
57654     this.lastActive = false;
57655
57656     this.addEvents({
57657         /**
57658              * @event selectionchange
57659              * Fires when the selection changes
57660              * @param {SelectionModel} this
57661              */
57662             "selectionchange" : true,
57663         /**
57664              * @event afterselectionchange
57665              * Fires after the selection changes (eg. by key press or clicking)
57666              * @param {SelectionModel} this
57667              */
57668             "afterselectionchange" : true,
57669         /**
57670              * @event beforerowselect
57671              * Fires when a row is selected being selected, return false to cancel.
57672              * @param {SelectionModel} this
57673              * @param {Number} rowIndex The selected index
57674              * @param {Boolean} keepExisting False if other selections will be cleared
57675              */
57676             "beforerowselect" : true,
57677         /**
57678              * @event rowselect
57679              * Fires when a row is selected.
57680              * @param {SelectionModel} this
57681              * @param {Number} rowIndex The selected index
57682              * @param {Roo.data.Record} r The record
57683              */
57684             "rowselect" : true,
57685         /**
57686              * @event rowdeselect
57687              * Fires when a row is deselected.
57688              * @param {SelectionModel} this
57689              * @param {Number} rowIndex The selected index
57690              */
57691         "rowdeselect" : true
57692     });
57693     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57694     this.locked = false;
57695 };
57696
57697 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57698     /**
57699      * @cfg {Boolean} singleSelect
57700      * True to allow selection of only one row at a time (defaults to false)
57701      */
57702     singleSelect : false,
57703
57704     // private
57705     initEvents : function(){
57706
57707         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57708             this.grid.on("mousedown", this.handleMouseDown, this);
57709         }else{ // allow click to work like normal
57710             this.grid.on("rowclick", this.handleDragableRowClick, this);
57711         }
57712
57713         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57714             "up" : function(e){
57715                 if(!e.shiftKey){
57716                     this.selectPrevious(e.shiftKey);
57717                 }else if(this.last !== false && this.lastActive !== false){
57718                     var last = this.last;
57719                     this.selectRange(this.last,  this.lastActive-1);
57720                     this.grid.getView().focusRow(this.lastActive);
57721                     if(last !== false){
57722                         this.last = last;
57723                     }
57724                 }else{
57725                     this.selectFirstRow();
57726                 }
57727                 this.fireEvent("afterselectionchange", this);
57728             },
57729             "down" : function(e){
57730                 if(!e.shiftKey){
57731                     this.selectNext(e.shiftKey);
57732                 }else if(this.last !== false && this.lastActive !== false){
57733                     var last = this.last;
57734                     this.selectRange(this.last,  this.lastActive+1);
57735                     this.grid.getView().focusRow(this.lastActive);
57736                     if(last !== false){
57737                         this.last = last;
57738                     }
57739                 }else{
57740                     this.selectFirstRow();
57741                 }
57742                 this.fireEvent("afterselectionchange", this);
57743             },
57744             scope: this
57745         });
57746
57747         var view = this.grid.view;
57748         view.on("refresh", this.onRefresh, this);
57749         view.on("rowupdated", this.onRowUpdated, this);
57750         view.on("rowremoved", this.onRemove, this);
57751     },
57752
57753     // private
57754     onRefresh : function(){
57755         var ds = this.grid.dataSource, i, v = this.grid.view;
57756         var s = this.selections;
57757         s.each(function(r){
57758             if((i = ds.indexOfId(r.id)) != -1){
57759                 v.onRowSelect(i);
57760                 s.add(ds.getAt(i)); // updating the selection relate data
57761             }else{
57762                 s.remove(r);
57763             }
57764         });
57765     },
57766
57767     // private
57768     onRemove : function(v, index, r){
57769         this.selections.remove(r);
57770     },
57771
57772     // private
57773     onRowUpdated : function(v, index, r){
57774         if(this.isSelected(r)){
57775             v.onRowSelect(index);
57776         }
57777     },
57778
57779     /**
57780      * Select records.
57781      * @param {Array} records The records to select
57782      * @param {Boolean} keepExisting (optional) True to keep existing selections
57783      */
57784     selectRecords : function(records, keepExisting){
57785         if(!keepExisting){
57786             this.clearSelections();
57787         }
57788         var ds = this.grid.dataSource;
57789         for(var i = 0, len = records.length; i < len; i++){
57790             this.selectRow(ds.indexOf(records[i]), true);
57791         }
57792     },
57793
57794     /**
57795      * Gets the number of selected rows.
57796      * @return {Number}
57797      */
57798     getCount : function(){
57799         return this.selections.length;
57800     },
57801
57802     /**
57803      * Selects the first row in the grid.
57804      */
57805     selectFirstRow : function(){
57806         this.selectRow(0);
57807     },
57808
57809     /**
57810      * Select the last row.
57811      * @param {Boolean} keepExisting (optional) True to keep existing selections
57812      */
57813     selectLastRow : function(keepExisting){
57814         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57815     },
57816
57817     /**
57818      * Selects the row immediately following the last selected row.
57819      * @param {Boolean} keepExisting (optional) True to keep existing selections
57820      */
57821     selectNext : function(keepExisting){
57822         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57823             this.selectRow(this.last+1, keepExisting);
57824             this.grid.getView().focusRow(this.last);
57825         }
57826     },
57827
57828     /**
57829      * Selects the row that precedes the last selected row.
57830      * @param {Boolean} keepExisting (optional) True to keep existing selections
57831      */
57832     selectPrevious : function(keepExisting){
57833         if(this.last){
57834             this.selectRow(this.last-1, keepExisting);
57835             this.grid.getView().focusRow(this.last);
57836         }
57837     },
57838
57839     /**
57840      * Returns the selected records
57841      * @return {Array} Array of selected records
57842      */
57843     getSelections : function(){
57844         return [].concat(this.selections.items);
57845     },
57846
57847     /**
57848      * Returns the first selected record.
57849      * @return {Record}
57850      */
57851     getSelected : function(){
57852         return this.selections.itemAt(0);
57853     },
57854
57855
57856     /**
57857      * Clears all selections.
57858      */
57859     clearSelections : function(fast){
57860         if(this.locked) {
57861             return;
57862         }
57863         if(fast !== true){
57864             var ds = this.grid.dataSource;
57865             var s = this.selections;
57866             s.each(function(r){
57867                 this.deselectRow(ds.indexOfId(r.id));
57868             }, this);
57869             s.clear();
57870         }else{
57871             this.selections.clear();
57872         }
57873         this.last = false;
57874     },
57875
57876
57877     /**
57878      * Selects all rows.
57879      */
57880     selectAll : function(){
57881         if(this.locked) {
57882             return;
57883         }
57884         this.selections.clear();
57885         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57886             this.selectRow(i, true);
57887         }
57888     },
57889
57890     /**
57891      * Returns True if there is a selection.
57892      * @return {Boolean}
57893      */
57894     hasSelection : function(){
57895         return this.selections.length > 0;
57896     },
57897
57898     /**
57899      * Returns True if the specified row is selected.
57900      * @param {Number/Record} record The record or index of the record to check
57901      * @return {Boolean}
57902      */
57903     isSelected : function(index){
57904         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57905         return (r && this.selections.key(r.id) ? true : false);
57906     },
57907
57908     /**
57909      * Returns True if the specified record id is selected.
57910      * @param {String} id The id of record to check
57911      * @return {Boolean}
57912      */
57913     isIdSelected : function(id){
57914         return (this.selections.key(id) ? true : false);
57915     },
57916
57917     // private
57918     handleMouseDown : function(e, t){
57919         var view = this.grid.getView(), rowIndex;
57920         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57921             return;
57922         };
57923         if(e.shiftKey && this.last !== false){
57924             var last = this.last;
57925             this.selectRange(last, rowIndex, e.ctrlKey);
57926             this.last = last; // reset the last
57927             view.focusRow(rowIndex);
57928         }else{
57929             var isSelected = this.isSelected(rowIndex);
57930             if(e.button !== 0 && isSelected){
57931                 view.focusRow(rowIndex);
57932             }else if(e.ctrlKey && isSelected){
57933                 this.deselectRow(rowIndex);
57934             }else if(!isSelected){
57935                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57936                 view.focusRow(rowIndex);
57937             }
57938         }
57939         this.fireEvent("afterselectionchange", this);
57940     },
57941     // private
57942     handleDragableRowClick :  function(grid, rowIndex, e) 
57943     {
57944         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57945             this.selectRow(rowIndex, false);
57946             grid.view.focusRow(rowIndex);
57947              this.fireEvent("afterselectionchange", this);
57948         }
57949     },
57950     
57951     /**
57952      * Selects multiple rows.
57953      * @param {Array} rows Array of the indexes of the row to select
57954      * @param {Boolean} keepExisting (optional) True to keep existing selections
57955      */
57956     selectRows : function(rows, keepExisting){
57957         if(!keepExisting){
57958             this.clearSelections();
57959         }
57960         for(var i = 0, len = rows.length; i < len; i++){
57961             this.selectRow(rows[i], true);
57962         }
57963     },
57964
57965     /**
57966      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57967      * @param {Number} startRow The index of the first row in the range
57968      * @param {Number} endRow The index of the last row in the range
57969      * @param {Boolean} keepExisting (optional) True to retain existing selections
57970      */
57971     selectRange : function(startRow, endRow, keepExisting){
57972         if(this.locked) {
57973             return;
57974         }
57975         if(!keepExisting){
57976             this.clearSelections();
57977         }
57978         if(startRow <= endRow){
57979             for(var i = startRow; i <= endRow; i++){
57980                 this.selectRow(i, true);
57981             }
57982         }else{
57983             for(var i = startRow; i >= endRow; i--){
57984                 this.selectRow(i, true);
57985             }
57986         }
57987     },
57988
57989     /**
57990      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57991      * @param {Number} startRow The index of the first row in the range
57992      * @param {Number} endRow The index of the last row in the range
57993      */
57994     deselectRange : function(startRow, endRow, preventViewNotify){
57995         if(this.locked) {
57996             return;
57997         }
57998         for(var i = startRow; i <= endRow; i++){
57999             this.deselectRow(i, preventViewNotify);
58000         }
58001     },
58002
58003     /**
58004      * Selects a row.
58005      * @param {Number} row The index of the row to select
58006      * @param {Boolean} keepExisting (optional) True to keep existing selections
58007      */
58008     selectRow : function(index, keepExisting, preventViewNotify){
58009         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58010             return;
58011         }
58012         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58013             if(!keepExisting || this.singleSelect){
58014                 this.clearSelections();
58015             }
58016             var r = this.grid.dataSource.getAt(index);
58017             this.selections.add(r);
58018             this.last = this.lastActive = index;
58019             if(!preventViewNotify){
58020                 this.grid.getView().onRowSelect(index);
58021             }
58022             this.fireEvent("rowselect", this, index, r);
58023             this.fireEvent("selectionchange", this);
58024         }
58025     },
58026
58027     /**
58028      * Deselects a row.
58029      * @param {Number} row The index of the row to deselect
58030      */
58031     deselectRow : function(index, preventViewNotify){
58032         if(this.locked) {
58033             return;
58034         }
58035         if(this.last == index){
58036             this.last = false;
58037         }
58038         if(this.lastActive == index){
58039             this.lastActive = false;
58040         }
58041         var r = this.grid.dataSource.getAt(index);
58042         this.selections.remove(r);
58043         if(!preventViewNotify){
58044             this.grid.getView().onRowDeselect(index);
58045         }
58046         this.fireEvent("rowdeselect", this, index);
58047         this.fireEvent("selectionchange", this);
58048     },
58049
58050     // private
58051     restoreLast : function(){
58052         if(this._last){
58053             this.last = this._last;
58054         }
58055     },
58056
58057     // private
58058     acceptsNav : function(row, col, cm){
58059         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58060     },
58061
58062     // private
58063     onEditorKey : function(field, e){
58064         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58065         if(k == e.TAB){
58066             e.stopEvent();
58067             ed.completeEdit();
58068             if(e.shiftKey){
58069                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58070             }else{
58071                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58072             }
58073         }else if(k == e.ENTER && !e.ctrlKey){
58074             e.stopEvent();
58075             ed.completeEdit();
58076             if(e.shiftKey){
58077                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58078             }else{
58079                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58080             }
58081         }else if(k == e.ESC){
58082             ed.cancelEdit();
58083         }
58084         if(newCell){
58085             g.startEditing(newCell[0], newCell[1]);
58086         }
58087     }
58088 });/*
58089  * Based on:
58090  * Ext JS Library 1.1.1
58091  * Copyright(c) 2006-2007, Ext JS, LLC.
58092  *
58093  * Originally Released Under LGPL - original licence link has changed is not relivant.
58094  *
58095  * Fork - LGPL
58096  * <script type="text/javascript">
58097  */
58098 /**
58099  * @class Roo.grid.CellSelectionModel
58100  * @extends Roo.grid.AbstractSelectionModel
58101  * This class provides the basic implementation for cell selection in a grid.
58102  * @constructor
58103  * @param {Object} config The object containing the configuration of this model.
58104  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58105  */
58106 Roo.grid.CellSelectionModel = function(config){
58107     Roo.apply(this, config);
58108
58109     this.selection = null;
58110
58111     this.addEvents({
58112         /**
58113              * @event beforerowselect
58114              * Fires before a cell is selected.
58115              * @param {SelectionModel} this
58116              * @param {Number} rowIndex The selected row index
58117              * @param {Number} colIndex The selected cell index
58118              */
58119             "beforecellselect" : true,
58120         /**
58121              * @event cellselect
58122              * Fires when a cell is selected.
58123              * @param {SelectionModel} this
58124              * @param {Number} rowIndex The selected row index
58125              * @param {Number} colIndex The selected cell index
58126              */
58127             "cellselect" : true,
58128         /**
58129              * @event selectionchange
58130              * Fires when the active selection changes.
58131              * @param {SelectionModel} this
58132              * @param {Object} selection null for no selection or an object (o) with two properties
58133                 <ul>
58134                 <li>o.record: the record object for the row the selection is in</li>
58135                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58136                 </ul>
58137              */
58138             "selectionchange" : true,
58139         /**
58140              * @event tabend
58141              * Fires when the tab (or enter) was pressed on the last editable cell
58142              * You can use this to trigger add new row.
58143              * @param {SelectionModel} this
58144              */
58145             "tabend" : true,
58146          /**
58147              * @event beforeeditnext
58148              * Fires before the next editable sell is made active
58149              * You can use this to skip to another cell or fire the tabend
58150              *    if you set cell to false
58151              * @param {Object} eventdata object : { cell : [ row, col ] } 
58152              */
58153             "beforeeditnext" : true
58154     });
58155     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58156 };
58157
58158 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58159     
58160     enter_is_tab: false,
58161
58162     /** @ignore */
58163     initEvents : function(){
58164         this.grid.on("mousedown", this.handleMouseDown, this);
58165         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58166         var view = this.grid.view;
58167         view.on("refresh", this.onViewChange, this);
58168         view.on("rowupdated", this.onRowUpdated, this);
58169         view.on("beforerowremoved", this.clearSelections, this);
58170         view.on("beforerowsinserted", this.clearSelections, this);
58171         if(this.grid.isEditor){
58172             this.grid.on("beforeedit", this.beforeEdit,  this);
58173         }
58174     },
58175
58176         //private
58177     beforeEdit : function(e){
58178         this.select(e.row, e.column, false, true, e.record);
58179     },
58180
58181         //private
58182     onRowUpdated : function(v, index, r){
58183         if(this.selection && this.selection.record == r){
58184             v.onCellSelect(index, this.selection.cell[1]);
58185         }
58186     },
58187
58188         //private
58189     onViewChange : function(){
58190         this.clearSelections(true);
58191     },
58192
58193         /**
58194          * Returns the currently selected cell,.
58195          * @return {Array} The selected cell (row, column) or null if none selected.
58196          */
58197     getSelectedCell : function(){
58198         return this.selection ? this.selection.cell : null;
58199     },
58200
58201     /**
58202      * Clears all selections.
58203      * @param {Boolean} true to prevent the gridview from being notified about the change.
58204      */
58205     clearSelections : function(preventNotify){
58206         var s = this.selection;
58207         if(s){
58208             if(preventNotify !== true){
58209                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58210             }
58211             this.selection = null;
58212             this.fireEvent("selectionchange", this, null);
58213         }
58214     },
58215
58216     /**
58217      * Returns true if there is a selection.
58218      * @return {Boolean}
58219      */
58220     hasSelection : function(){
58221         return this.selection ? true : false;
58222     },
58223
58224     /** @ignore */
58225     handleMouseDown : function(e, t){
58226         var v = this.grid.getView();
58227         if(this.isLocked()){
58228             return;
58229         };
58230         var row = v.findRowIndex(t);
58231         var cell = v.findCellIndex(t);
58232         if(row !== false && cell !== false){
58233             this.select(row, cell);
58234         }
58235     },
58236
58237     /**
58238      * Selects a cell.
58239      * @param {Number} rowIndex
58240      * @param {Number} collIndex
58241      */
58242     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58243         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58244             this.clearSelections();
58245             r = r || this.grid.dataSource.getAt(rowIndex);
58246             this.selection = {
58247                 record : r,
58248                 cell : [rowIndex, colIndex]
58249             };
58250             if(!preventViewNotify){
58251                 var v = this.grid.getView();
58252                 v.onCellSelect(rowIndex, colIndex);
58253                 if(preventFocus !== true){
58254                     v.focusCell(rowIndex, colIndex);
58255                 }
58256             }
58257             this.fireEvent("cellselect", this, rowIndex, colIndex);
58258             this.fireEvent("selectionchange", this, this.selection);
58259         }
58260     },
58261
58262         //private
58263     isSelectable : function(rowIndex, colIndex, cm){
58264         return !cm.isHidden(colIndex);
58265     },
58266
58267     /** @ignore */
58268     handleKeyDown : function(e){
58269         //Roo.log('Cell Sel Model handleKeyDown');
58270         if(!e.isNavKeyPress()){
58271             return;
58272         }
58273         var g = this.grid, s = this.selection;
58274         if(!s){
58275             e.stopEvent();
58276             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58277             if(cell){
58278                 this.select(cell[0], cell[1]);
58279             }
58280             return;
58281         }
58282         var sm = this;
58283         var walk = function(row, col, step){
58284             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58285         };
58286         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58287         var newCell;
58288
58289       
58290
58291         switch(k){
58292             case e.TAB:
58293                 // handled by onEditorKey
58294                 if (g.isEditor && g.editing) {
58295                     return;
58296                 }
58297                 if(e.shiftKey) {
58298                     newCell = walk(r, c-1, -1);
58299                 } else {
58300                     newCell = walk(r, c+1, 1);
58301                 }
58302                 break;
58303             
58304             case e.DOWN:
58305                newCell = walk(r+1, c, 1);
58306                 break;
58307             
58308             case e.UP:
58309                 newCell = walk(r-1, c, -1);
58310                 break;
58311             
58312             case e.RIGHT:
58313                 newCell = walk(r, c+1, 1);
58314                 break;
58315             
58316             case e.LEFT:
58317                 newCell = walk(r, c-1, -1);
58318                 break;
58319             
58320             case e.ENTER:
58321                 
58322                 if(g.isEditor && !g.editing){
58323                    g.startEditing(r, c);
58324                    e.stopEvent();
58325                    return;
58326                 }
58327                 
58328                 
58329              break;
58330         };
58331         if(newCell){
58332             this.select(newCell[0], newCell[1]);
58333             e.stopEvent();
58334             
58335         }
58336     },
58337
58338     acceptsNav : function(row, col, cm){
58339         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58340     },
58341     /**
58342      * Selects a cell.
58343      * @param {Number} field (not used) - as it's normally used as a listener
58344      * @param {Number} e - event - fake it by using
58345      *
58346      * var e = Roo.EventObjectImpl.prototype;
58347      * e.keyCode = e.TAB
58348      *
58349      * 
58350      */
58351     onEditorKey : function(field, e){
58352         
58353         var k = e.getKey(),
58354             newCell,
58355             g = this.grid,
58356             ed = g.activeEditor,
58357             forward = false;
58358         ///Roo.log('onEditorKey' + k);
58359         
58360         
58361         if (this.enter_is_tab && k == e.ENTER) {
58362             k = e.TAB;
58363         }
58364         
58365         if(k == e.TAB){
58366             if(e.shiftKey){
58367                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58368             }else{
58369                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58370                 forward = true;
58371             }
58372             
58373             e.stopEvent();
58374             
58375         } else if(k == e.ENTER &&  !e.ctrlKey){
58376             ed.completeEdit();
58377             e.stopEvent();
58378             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58379         
58380                 } else if(k == e.ESC){
58381             ed.cancelEdit();
58382         }
58383                 
58384         if (newCell) {
58385             var ecall = { cell : newCell, forward : forward };
58386             this.fireEvent('beforeeditnext', ecall );
58387             newCell = ecall.cell;
58388                         forward = ecall.forward;
58389         }
58390                 
58391         if(newCell){
58392             //Roo.log('next cell after edit');
58393             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58394         } else if (forward) {
58395             // tabbed past last
58396             this.fireEvent.defer(100, this, ['tabend',this]);
58397         }
58398     }
58399 });/*
58400  * Based on:
58401  * Ext JS Library 1.1.1
58402  * Copyright(c) 2006-2007, Ext JS, LLC.
58403  *
58404  * Originally Released Under LGPL - original licence link has changed is not relivant.
58405  *
58406  * Fork - LGPL
58407  * <script type="text/javascript">
58408  */
58409  
58410 /**
58411  * @class Roo.grid.EditorGrid
58412  * @extends Roo.grid.Grid
58413  * Class for creating and editable grid.
58414  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58415  * The container MUST have some type of size defined for the grid to fill. The container will be 
58416  * automatically set to position relative if it isn't already.
58417  * @param {Object} dataSource The data model to bind to
58418  * @param {Object} colModel The column model with info about this grid's columns
58419  */
58420 Roo.grid.EditorGrid = function(container, config){
58421     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58422     this.getGridEl().addClass("xedit-grid");
58423
58424     if(!this.selModel){
58425         this.selModel = new Roo.grid.CellSelectionModel();
58426     }
58427
58428     this.activeEditor = null;
58429
58430         this.addEvents({
58431             /**
58432              * @event beforeedit
58433              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58434              * <ul style="padding:5px;padding-left:16px;">
58435              * <li>grid - This grid</li>
58436              * <li>record - The record being edited</li>
58437              * <li>field - The field name being edited</li>
58438              * <li>value - The value for the field being edited.</li>
58439              * <li>row - The grid row index</li>
58440              * <li>column - The grid column index</li>
58441              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58442              * </ul>
58443              * @param {Object} e An edit event (see above for description)
58444              */
58445             "beforeedit" : true,
58446             /**
58447              * @event afteredit
58448              * Fires after a cell is edited. <br />
58449              * <ul style="padding:5px;padding-left:16px;">
58450              * <li>grid - This grid</li>
58451              * <li>record - The record being edited</li>
58452              * <li>field - The field name being edited</li>
58453              * <li>value - The value being set</li>
58454              * <li>originalValue - The original value for the field, before the edit.</li>
58455              * <li>row - The grid row index</li>
58456              * <li>column - The grid column index</li>
58457              * </ul>
58458              * @param {Object} e An edit event (see above for description)
58459              */
58460             "afteredit" : true,
58461             /**
58462              * @event validateedit
58463              * Fires after a cell is edited, but before the value is set in the record. 
58464          * You can use this to modify the value being set in the field, Return false
58465              * to cancel the change. The edit event object has the following properties <br />
58466              * <ul style="padding:5px;padding-left:16px;">
58467          * <li>editor - This editor</li>
58468              * <li>grid - This grid</li>
58469              * <li>record - The record being edited</li>
58470              * <li>field - The field name being edited</li>
58471              * <li>value - The value being set</li>
58472              * <li>originalValue - The original value for the field, before the edit.</li>
58473              * <li>row - The grid row index</li>
58474              * <li>column - The grid column index</li>
58475              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58476              * </ul>
58477              * @param {Object} e An edit event (see above for description)
58478              */
58479             "validateedit" : true
58480         });
58481     this.on("bodyscroll", this.stopEditing,  this);
58482     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58483 };
58484
58485 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58486     /**
58487      * @cfg {Number} clicksToEdit
58488      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58489      */
58490     clicksToEdit: 2,
58491
58492     // private
58493     isEditor : true,
58494     // private
58495     trackMouseOver: false, // causes very odd FF errors
58496
58497     onCellDblClick : function(g, row, col){
58498         this.startEditing(row, col);
58499     },
58500
58501     onEditComplete : function(ed, value, startValue){
58502         this.editing = false;
58503         this.activeEditor = null;
58504         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58505         var r = ed.record;
58506         var field = this.colModel.getDataIndex(ed.col);
58507         var e = {
58508             grid: this,
58509             record: r,
58510             field: field,
58511             originalValue: startValue,
58512             value: value,
58513             row: ed.row,
58514             column: ed.col,
58515             cancel:false,
58516             editor: ed
58517         };
58518         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58519         cell.show();
58520           
58521         if(String(value) !== String(startValue)){
58522             
58523             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58524                 r.set(field, e.value);
58525                 // if we are dealing with a combo box..
58526                 // then we also set the 'name' colum to be the displayField
58527                 if (ed.field.displayField && ed.field.name) {
58528                     r.set(ed.field.name, ed.field.el.dom.value);
58529                 }
58530                 
58531                 delete e.cancel; //?? why!!!
58532                 this.fireEvent("afteredit", e);
58533             }
58534         } else {
58535             this.fireEvent("afteredit", e); // always fire it!
58536         }
58537         this.view.focusCell(ed.row, ed.col);
58538     },
58539
58540     /**
58541      * Starts editing the specified for the specified row/column
58542      * @param {Number} rowIndex
58543      * @param {Number} colIndex
58544      */
58545     startEditing : function(row, col){
58546         this.stopEditing();
58547         if(this.colModel.isCellEditable(col, row)){
58548             this.view.ensureVisible(row, col, true);
58549           
58550             var r = this.dataSource.getAt(row);
58551             var field = this.colModel.getDataIndex(col);
58552             var cell = Roo.get(this.view.getCell(row,col));
58553             var e = {
58554                 grid: this,
58555                 record: r,
58556                 field: field,
58557                 value: r.data[field],
58558                 row: row,
58559                 column: col,
58560                 cancel:false 
58561             };
58562             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58563                 this.editing = true;
58564                 var ed = this.colModel.getCellEditor(col, row);
58565                 
58566                 if (!ed) {
58567                     return;
58568                 }
58569                 if(!ed.rendered){
58570                     ed.render(ed.parentEl || document.body);
58571                 }
58572                 ed.field.reset();
58573                
58574                 cell.hide();
58575                 
58576                 (function(){ // complex but required for focus issues in safari, ie and opera
58577                     ed.row = row;
58578                     ed.col = col;
58579                     ed.record = r;
58580                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58581                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58582                     this.activeEditor = ed;
58583                     var v = r.data[field];
58584                     ed.startEdit(this.view.getCell(row, col), v);
58585                     // combo's with 'displayField and name set
58586                     if (ed.field.displayField && ed.field.name) {
58587                         ed.field.el.dom.value = r.data[ed.field.name];
58588                     }
58589                     
58590                     
58591                 }).defer(50, this);
58592             }
58593         }
58594     },
58595         
58596     /**
58597      * Stops any active editing
58598      */
58599     stopEditing : function(){
58600         if(this.activeEditor){
58601             this.activeEditor.completeEdit();
58602         }
58603         this.activeEditor = null;
58604     },
58605         
58606          /**
58607      * Called to get grid's drag proxy text, by default returns this.ddText.
58608      * @return {String}
58609      */
58610     getDragDropText : function(){
58611         var count = this.selModel.getSelectedCell() ? 1 : 0;
58612         return String.format(this.ddText, count, count == 1 ? '' : 's');
58613     }
58614         
58615 });/*
58616  * Based on:
58617  * Ext JS Library 1.1.1
58618  * Copyright(c) 2006-2007, Ext JS, LLC.
58619  *
58620  * Originally Released Under LGPL - original licence link has changed is not relivant.
58621  *
58622  * Fork - LGPL
58623  * <script type="text/javascript">
58624  */
58625
58626 // private - not really -- you end up using it !
58627 // This is a support class used internally by the Grid components
58628
58629 /**
58630  * @class Roo.grid.GridEditor
58631  * @extends Roo.Editor
58632  * Class for creating and editable grid elements.
58633  * @param {Object} config any settings (must include field)
58634  */
58635 Roo.grid.GridEditor = function(field, config){
58636     if (!config && field.field) {
58637         config = field;
58638         field = Roo.factory(config.field, Roo.form);
58639     }
58640     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58641     field.monitorTab = false;
58642 };
58643
58644 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58645     
58646     /**
58647      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58648      */
58649     
58650     alignment: "tl-tl",
58651     autoSize: "width",
58652     hideEl : false,
58653     cls: "x-small-editor x-grid-editor",
58654     shim:false,
58655     shadow:"frame"
58656 });/*
58657  * Based on:
58658  * Ext JS Library 1.1.1
58659  * Copyright(c) 2006-2007, Ext JS, LLC.
58660  *
58661  * Originally Released Under LGPL - original licence link has changed is not relivant.
58662  *
58663  * Fork - LGPL
58664  * <script type="text/javascript">
58665  */
58666   
58667
58668   
58669 Roo.grid.PropertyRecord = Roo.data.Record.create([
58670     {name:'name',type:'string'},  'value'
58671 ]);
58672
58673
58674 Roo.grid.PropertyStore = function(grid, source){
58675     this.grid = grid;
58676     this.store = new Roo.data.Store({
58677         recordType : Roo.grid.PropertyRecord
58678     });
58679     this.store.on('update', this.onUpdate,  this);
58680     if(source){
58681         this.setSource(source);
58682     }
58683     Roo.grid.PropertyStore.superclass.constructor.call(this);
58684 };
58685
58686
58687
58688 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58689     setSource : function(o){
58690         this.source = o;
58691         this.store.removeAll();
58692         var data = [];
58693         for(var k in o){
58694             if(this.isEditableValue(o[k])){
58695                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58696             }
58697         }
58698         this.store.loadRecords({records: data}, {}, true);
58699     },
58700
58701     onUpdate : function(ds, record, type){
58702         if(type == Roo.data.Record.EDIT){
58703             var v = record.data['value'];
58704             var oldValue = record.modified['value'];
58705             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58706                 this.source[record.id] = v;
58707                 record.commit();
58708                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58709             }else{
58710                 record.reject();
58711             }
58712         }
58713     },
58714
58715     getProperty : function(row){
58716        return this.store.getAt(row);
58717     },
58718
58719     isEditableValue: function(val){
58720         if(val && val instanceof Date){
58721             return true;
58722         }else if(typeof val == 'object' || typeof val == 'function'){
58723             return false;
58724         }
58725         return true;
58726     },
58727
58728     setValue : function(prop, value){
58729         this.source[prop] = value;
58730         this.store.getById(prop).set('value', value);
58731     },
58732
58733     getSource : function(){
58734         return this.source;
58735     }
58736 });
58737
58738 Roo.grid.PropertyColumnModel = function(grid, store){
58739     this.grid = grid;
58740     var g = Roo.grid;
58741     g.PropertyColumnModel.superclass.constructor.call(this, [
58742         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58743         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58744     ]);
58745     this.store = store;
58746     this.bselect = Roo.DomHelper.append(document.body, {
58747         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58748             {tag: 'option', value: 'true', html: 'true'},
58749             {tag: 'option', value: 'false', html: 'false'}
58750         ]
58751     });
58752     Roo.id(this.bselect);
58753     var f = Roo.form;
58754     this.editors = {
58755         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58756         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58757         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58758         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58759         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58760     };
58761     this.renderCellDelegate = this.renderCell.createDelegate(this);
58762     this.renderPropDelegate = this.renderProp.createDelegate(this);
58763 };
58764
58765 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58766     
58767     
58768     nameText : 'Name',
58769     valueText : 'Value',
58770     
58771     dateFormat : 'm/j/Y',
58772     
58773     
58774     renderDate : function(dateVal){
58775         return dateVal.dateFormat(this.dateFormat);
58776     },
58777
58778     renderBool : function(bVal){
58779         return bVal ? 'true' : 'false';
58780     },
58781
58782     isCellEditable : function(colIndex, rowIndex){
58783         return colIndex == 1;
58784     },
58785
58786     getRenderer : function(col){
58787         return col == 1 ?
58788             this.renderCellDelegate : this.renderPropDelegate;
58789     },
58790
58791     renderProp : function(v){
58792         return this.getPropertyName(v);
58793     },
58794
58795     renderCell : function(val){
58796         var rv = val;
58797         if(val instanceof Date){
58798             rv = this.renderDate(val);
58799         }else if(typeof val == 'boolean'){
58800             rv = this.renderBool(val);
58801         }
58802         return Roo.util.Format.htmlEncode(rv);
58803     },
58804
58805     getPropertyName : function(name){
58806         var pn = this.grid.propertyNames;
58807         return pn && pn[name] ? pn[name] : name;
58808     },
58809
58810     getCellEditor : function(colIndex, rowIndex){
58811         var p = this.store.getProperty(rowIndex);
58812         var n = p.data['name'], val = p.data['value'];
58813         
58814         if(typeof(this.grid.customEditors[n]) == 'string'){
58815             return this.editors[this.grid.customEditors[n]];
58816         }
58817         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58818             return this.grid.customEditors[n];
58819         }
58820         if(val instanceof Date){
58821             return this.editors['date'];
58822         }else if(typeof val == 'number'){
58823             return this.editors['number'];
58824         }else if(typeof val == 'boolean'){
58825             return this.editors['boolean'];
58826         }else{
58827             return this.editors['string'];
58828         }
58829     }
58830 });
58831
58832 /**
58833  * @class Roo.grid.PropertyGrid
58834  * @extends Roo.grid.EditorGrid
58835  * This class represents the  interface of a component based property grid control.
58836  * <br><br>Usage:<pre><code>
58837  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58838       
58839  });
58840  // set any options
58841  grid.render();
58842  * </code></pre>
58843   
58844  * @constructor
58845  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58846  * The container MUST have some type of size defined for the grid to fill. The container will be
58847  * automatically set to position relative if it isn't already.
58848  * @param {Object} config A config object that sets properties on this grid.
58849  */
58850 Roo.grid.PropertyGrid = function(container, config){
58851     config = config || {};
58852     var store = new Roo.grid.PropertyStore(this);
58853     this.store = store;
58854     var cm = new Roo.grid.PropertyColumnModel(this, store);
58855     store.store.sort('name', 'ASC');
58856     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58857         ds: store.store,
58858         cm: cm,
58859         enableColLock:false,
58860         enableColumnMove:false,
58861         stripeRows:false,
58862         trackMouseOver: false,
58863         clicksToEdit:1
58864     }, config));
58865     this.getGridEl().addClass('x-props-grid');
58866     this.lastEditRow = null;
58867     this.on('columnresize', this.onColumnResize, this);
58868     this.addEvents({
58869          /**
58870              * @event beforepropertychange
58871              * Fires before a property changes (return false to stop?)
58872              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58873              * @param {String} id Record Id
58874              * @param {String} newval New Value
58875          * @param {String} oldval Old Value
58876              */
58877         "beforepropertychange": true,
58878         /**
58879              * @event propertychange
58880              * Fires after a property changes
58881              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58882              * @param {String} id Record Id
58883              * @param {String} newval New Value
58884          * @param {String} oldval Old Value
58885              */
58886         "propertychange": true
58887     });
58888     this.customEditors = this.customEditors || {};
58889 };
58890 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58891     
58892      /**
58893      * @cfg {Object} customEditors map of colnames=> custom editors.
58894      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58895      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58896      * false disables editing of the field.
58897          */
58898     
58899       /**
58900      * @cfg {Object} propertyNames map of property Names to their displayed value
58901          */
58902     
58903     render : function(){
58904         Roo.grid.PropertyGrid.superclass.render.call(this);
58905         this.autoSize.defer(100, this);
58906     },
58907
58908     autoSize : function(){
58909         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58910         if(this.view){
58911             this.view.fitColumns();
58912         }
58913     },
58914
58915     onColumnResize : function(){
58916         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58917         this.autoSize();
58918     },
58919     /**
58920      * Sets the data for the Grid
58921      * accepts a Key => Value object of all the elements avaiable.
58922      * @param {Object} data  to appear in grid.
58923      */
58924     setSource : function(source){
58925         this.store.setSource(source);
58926         //this.autoSize();
58927     },
58928     /**
58929      * Gets all the data from the grid.
58930      * @return {Object} data  data stored in grid
58931      */
58932     getSource : function(){
58933         return this.store.getSource();
58934     }
58935 });/*
58936   
58937  * Licence LGPL
58938  
58939  */
58940  
58941 /**
58942  * @class Roo.grid.Calendar
58943  * @extends Roo.util.Grid
58944  * This class extends the Grid to provide a calendar widget
58945  * <br><br>Usage:<pre><code>
58946  var grid = new Roo.grid.Calendar("my-container-id", {
58947      ds: myDataStore,
58948      cm: myColModel,
58949      selModel: mySelectionModel,
58950      autoSizeColumns: true,
58951      monitorWindowResize: false,
58952      trackMouseOver: true
58953      eventstore : real data store..
58954  });
58955  // set any options
58956  grid.render();
58957   
58958   * @constructor
58959  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58960  * The container MUST have some type of size defined for the grid to fill. The container will be
58961  * automatically set to position relative if it isn't already.
58962  * @param {Object} config A config object that sets properties on this grid.
58963  */
58964 Roo.grid.Calendar = function(container, config){
58965         // initialize the container
58966         this.container = Roo.get(container);
58967         this.container.update("");
58968         this.container.setStyle("overflow", "hidden");
58969     this.container.addClass('x-grid-container');
58970
58971     this.id = this.container.id;
58972
58973     Roo.apply(this, config);
58974     // check and correct shorthanded configs
58975     
58976     var rows = [];
58977     var d =1;
58978     for (var r = 0;r < 6;r++) {
58979         
58980         rows[r]=[];
58981         for (var c =0;c < 7;c++) {
58982             rows[r][c]= '';
58983         }
58984     }
58985     if (this.eventStore) {
58986         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58987         this.eventStore.on('load',this.onLoad, this);
58988         this.eventStore.on('beforeload',this.clearEvents, this);
58989          
58990     }
58991     
58992     this.dataSource = new Roo.data.Store({
58993             proxy: new Roo.data.MemoryProxy(rows),
58994             reader: new Roo.data.ArrayReader({}, [
58995                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58996     });
58997
58998     this.dataSource.load();
58999     this.ds = this.dataSource;
59000     this.ds.xmodule = this.xmodule || false;
59001     
59002     
59003     var cellRender = function(v,x,r)
59004     {
59005         return String.format(
59006             '<div class="fc-day  fc-widget-content"><div>' +
59007                 '<div class="fc-event-container"></div>' +
59008                 '<div class="fc-day-number">{0}</div>'+
59009                 
59010                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59011             '</div></div>', v);
59012     
59013     }
59014     
59015     
59016     this.colModel = new Roo.grid.ColumnModel( [
59017         {
59018             xtype: 'ColumnModel',
59019             xns: Roo.grid,
59020             dataIndex : 'weekday0',
59021             header : 'Sunday',
59022             renderer : cellRender
59023         },
59024         {
59025             xtype: 'ColumnModel',
59026             xns: Roo.grid,
59027             dataIndex : 'weekday1',
59028             header : 'Monday',
59029             renderer : cellRender
59030         },
59031         {
59032             xtype: 'ColumnModel',
59033             xns: Roo.grid,
59034             dataIndex : 'weekday2',
59035             header : 'Tuesday',
59036             renderer : cellRender
59037         },
59038         {
59039             xtype: 'ColumnModel',
59040             xns: Roo.grid,
59041             dataIndex : 'weekday3',
59042             header : 'Wednesday',
59043             renderer : cellRender
59044         },
59045         {
59046             xtype: 'ColumnModel',
59047             xns: Roo.grid,
59048             dataIndex : 'weekday4',
59049             header : 'Thursday',
59050             renderer : cellRender
59051         },
59052         {
59053             xtype: 'ColumnModel',
59054             xns: Roo.grid,
59055             dataIndex : 'weekday5',
59056             header : 'Friday',
59057             renderer : cellRender
59058         },
59059         {
59060             xtype: 'ColumnModel',
59061             xns: Roo.grid,
59062             dataIndex : 'weekday6',
59063             header : 'Saturday',
59064             renderer : cellRender
59065         }
59066     ]);
59067     this.cm = this.colModel;
59068     this.cm.xmodule = this.xmodule || false;
59069  
59070         
59071           
59072     //this.selModel = new Roo.grid.CellSelectionModel();
59073     //this.sm = this.selModel;
59074     //this.selModel.init(this);
59075     
59076     
59077     if(this.width){
59078         this.container.setWidth(this.width);
59079     }
59080
59081     if(this.height){
59082         this.container.setHeight(this.height);
59083     }
59084     /** @private */
59085         this.addEvents({
59086         // raw events
59087         /**
59088          * @event click
59089          * The raw click event for the entire grid.
59090          * @param {Roo.EventObject} e
59091          */
59092         "click" : true,
59093         /**
59094          * @event dblclick
59095          * The raw dblclick event for the entire grid.
59096          * @param {Roo.EventObject} e
59097          */
59098         "dblclick" : true,
59099         /**
59100          * @event contextmenu
59101          * The raw contextmenu event for the entire grid.
59102          * @param {Roo.EventObject} e
59103          */
59104         "contextmenu" : true,
59105         /**
59106          * @event mousedown
59107          * The raw mousedown event for the entire grid.
59108          * @param {Roo.EventObject} e
59109          */
59110         "mousedown" : true,
59111         /**
59112          * @event mouseup
59113          * The raw mouseup event for the entire grid.
59114          * @param {Roo.EventObject} e
59115          */
59116         "mouseup" : true,
59117         /**
59118          * @event mouseover
59119          * The raw mouseover event for the entire grid.
59120          * @param {Roo.EventObject} e
59121          */
59122         "mouseover" : true,
59123         /**
59124          * @event mouseout
59125          * The raw mouseout event for the entire grid.
59126          * @param {Roo.EventObject} e
59127          */
59128         "mouseout" : true,
59129         /**
59130          * @event keypress
59131          * The raw keypress event for the entire grid.
59132          * @param {Roo.EventObject} e
59133          */
59134         "keypress" : true,
59135         /**
59136          * @event keydown
59137          * The raw keydown event for the entire grid.
59138          * @param {Roo.EventObject} e
59139          */
59140         "keydown" : true,
59141
59142         // custom events
59143
59144         /**
59145          * @event cellclick
59146          * Fires when a cell is clicked
59147          * @param {Grid} this
59148          * @param {Number} rowIndex
59149          * @param {Number} columnIndex
59150          * @param {Roo.EventObject} e
59151          */
59152         "cellclick" : true,
59153         /**
59154          * @event celldblclick
59155          * Fires when a cell is double clicked
59156          * @param {Grid} this
59157          * @param {Number} rowIndex
59158          * @param {Number} columnIndex
59159          * @param {Roo.EventObject} e
59160          */
59161         "celldblclick" : true,
59162         /**
59163          * @event rowclick
59164          * Fires when a row is clicked
59165          * @param {Grid} this
59166          * @param {Number} rowIndex
59167          * @param {Roo.EventObject} e
59168          */
59169         "rowclick" : true,
59170         /**
59171          * @event rowdblclick
59172          * Fires when a row is double clicked
59173          * @param {Grid} this
59174          * @param {Number} rowIndex
59175          * @param {Roo.EventObject} e
59176          */
59177         "rowdblclick" : true,
59178         /**
59179          * @event headerclick
59180          * Fires when a header is clicked
59181          * @param {Grid} this
59182          * @param {Number} columnIndex
59183          * @param {Roo.EventObject} e
59184          */
59185         "headerclick" : true,
59186         /**
59187          * @event headerdblclick
59188          * Fires when a header cell is double clicked
59189          * @param {Grid} this
59190          * @param {Number} columnIndex
59191          * @param {Roo.EventObject} e
59192          */
59193         "headerdblclick" : true,
59194         /**
59195          * @event rowcontextmenu
59196          * Fires when a row is right clicked
59197          * @param {Grid} this
59198          * @param {Number} rowIndex
59199          * @param {Roo.EventObject} e
59200          */
59201         "rowcontextmenu" : true,
59202         /**
59203          * @event cellcontextmenu
59204          * Fires when a cell is right clicked
59205          * @param {Grid} this
59206          * @param {Number} rowIndex
59207          * @param {Number} cellIndex
59208          * @param {Roo.EventObject} e
59209          */
59210          "cellcontextmenu" : true,
59211         /**
59212          * @event headercontextmenu
59213          * Fires when a header is right clicked
59214          * @param {Grid} this
59215          * @param {Number} columnIndex
59216          * @param {Roo.EventObject} e
59217          */
59218         "headercontextmenu" : true,
59219         /**
59220          * @event bodyscroll
59221          * Fires when the body element is scrolled
59222          * @param {Number} scrollLeft
59223          * @param {Number} scrollTop
59224          */
59225         "bodyscroll" : true,
59226         /**
59227          * @event columnresize
59228          * Fires when the user resizes a column
59229          * @param {Number} columnIndex
59230          * @param {Number} newSize
59231          */
59232         "columnresize" : true,
59233         /**
59234          * @event columnmove
59235          * Fires when the user moves a column
59236          * @param {Number} oldIndex
59237          * @param {Number} newIndex
59238          */
59239         "columnmove" : true,
59240         /**
59241          * @event startdrag
59242          * Fires when row(s) start being dragged
59243          * @param {Grid} this
59244          * @param {Roo.GridDD} dd The drag drop object
59245          * @param {event} e The raw browser event
59246          */
59247         "startdrag" : true,
59248         /**
59249          * @event enddrag
59250          * Fires when a drag operation is complete
59251          * @param {Grid} this
59252          * @param {Roo.GridDD} dd The drag drop object
59253          * @param {event} e The raw browser event
59254          */
59255         "enddrag" : true,
59256         /**
59257          * @event dragdrop
59258          * Fires when dragged row(s) are dropped on a valid DD target
59259          * @param {Grid} this
59260          * @param {Roo.GridDD} dd The drag drop object
59261          * @param {String} targetId The target drag drop object
59262          * @param {event} e The raw browser event
59263          */
59264         "dragdrop" : true,
59265         /**
59266          * @event dragover
59267          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59268          * @param {Grid} this
59269          * @param {Roo.GridDD} dd The drag drop object
59270          * @param {String} targetId The target drag drop object
59271          * @param {event} e The raw browser event
59272          */
59273         "dragover" : true,
59274         /**
59275          * @event dragenter
59276          *  Fires when the dragged row(s) first cross another DD target while being dragged
59277          * @param {Grid} this
59278          * @param {Roo.GridDD} dd The drag drop object
59279          * @param {String} targetId The target drag drop object
59280          * @param {event} e The raw browser event
59281          */
59282         "dragenter" : true,
59283         /**
59284          * @event dragout
59285          * Fires when the dragged row(s) leave another DD target while being dragged
59286          * @param {Grid} this
59287          * @param {Roo.GridDD} dd The drag drop object
59288          * @param {String} targetId The target drag drop object
59289          * @param {event} e The raw browser event
59290          */
59291         "dragout" : true,
59292         /**
59293          * @event rowclass
59294          * Fires when a row is rendered, so you can change add a style to it.
59295          * @param {GridView} gridview   The grid view
59296          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59297          */
59298         'rowclass' : true,
59299
59300         /**
59301          * @event render
59302          * Fires when the grid is rendered
59303          * @param {Grid} grid
59304          */
59305         'render' : true,
59306             /**
59307              * @event select
59308              * Fires when a date is selected
59309              * @param {DatePicker} this
59310              * @param {Date} date The selected date
59311              */
59312         'select': true,
59313         /**
59314              * @event monthchange
59315              * Fires when the displayed month changes 
59316              * @param {DatePicker} this
59317              * @param {Date} date The selected month
59318              */
59319         'monthchange': true,
59320         /**
59321              * @event evententer
59322              * Fires when mouse over an event
59323              * @param {Calendar} this
59324              * @param {event} Event
59325              */
59326         'evententer': true,
59327         /**
59328              * @event eventleave
59329              * Fires when the mouse leaves an
59330              * @param {Calendar} this
59331              * @param {event}
59332              */
59333         'eventleave': true,
59334         /**
59335              * @event eventclick
59336              * Fires when the mouse click an
59337              * @param {Calendar} this
59338              * @param {event}
59339              */
59340         'eventclick': true,
59341         /**
59342              * @event eventrender
59343              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59344              * @param {Calendar} this
59345              * @param {data} data to be modified
59346              */
59347         'eventrender': true
59348         
59349     });
59350
59351     Roo.grid.Grid.superclass.constructor.call(this);
59352     this.on('render', function() {
59353         this.view.el.addClass('x-grid-cal'); 
59354         
59355         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59356
59357     },this);
59358     
59359     if (!Roo.grid.Calendar.style) {
59360         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59361             
59362             
59363             '.x-grid-cal .x-grid-col' :  {
59364                 height: 'auto !important',
59365                 'vertical-align': 'top'
59366             },
59367             '.x-grid-cal  .fc-event-hori' : {
59368                 height: '14px'
59369             }
59370              
59371             
59372         }, Roo.id());
59373     }
59374
59375     
59376     
59377 };
59378 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59379     /**
59380      * @cfg {Store} eventStore The store that loads events.
59381      */
59382     eventStore : 25,
59383
59384      
59385     activeDate : false,
59386     startDay : 0,
59387     autoWidth : true,
59388     monitorWindowResize : false,
59389
59390     
59391     resizeColumns : function() {
59392         var col = (this.view.el.getWidth() / 7) - 3;
59393         // loop through cols, and setWidth
59394         for(var i =0 ; i < 7 ; i++){
59395             this.cm.setColumnWidth(i, col);
59396         }
59397     },
59398      setDate :function(date) {
59399         
59400         Roo.log('setDate?');
59401         
59402         this.resizeColumns();
59403         var vd = this.activeDate;
59404         this.activeDate = date;
59405 //        if(vd && this.el){
59406 //            var t = date.getTime();
59407 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59408 //                Roo.log('using add remove');
59409 //                
59410 //                this.fireEvent('monthchange', this, date);
59411 //                
59412 //                this.cells.removeClass("fc-state-highlight");
59413 //                this.cells.each(function(c){
59414 //                   if(c.dateValue == t){
59415 //                       c.addClass("fc-state-highlight");
59416 //                       setTimeout(function(){
59417 //                            try{c.dom.firstChild.focus();}catch(e){}
59418 //                       }, 50);
59419 //                       return false;
59420 //                   }
59421 //                   return true;
59422 //                });
59423 //                return;
59424 //            }
59425 //        }
59426         
59427         var days = date.getDaysInMonth();
59428         
59429         var firstOfMonth = date.getFirstDateOfMonth();
59430         var startingPos = firstOfMonth.getDay()-this.startDay;
59431         
59432         if(startingPos < this.startDay){
59433             startingPos += 7;
59434         }
59435         
59436         var pm = date.add(Date.MONTH, -1);
59437         var prevStart = pm.getDaysInMonth()-startingPos;
59438 //        
59439         
59440         
59441         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59442         
59443         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59444         //this.cells.addClassOnOver('fc-state-hover');
59445         
59446         var cells = this.cells.elements;
59447         var textEls = this.textNodes;
59448         
59449         //Roo.each(cells, function(cell){
59450         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59451         //});
59452         
59453         days += startingPos;
59454
59455         // convert everything to numbers so it's fast
59456         var day = 86400000;
59457         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59458         //Roo.log(d);
59459         //Roo.log(pm);
59460         //Roo.log(prevStart);
59461         
59462         var today = new Date().clearTime().getTime();
59463         var sel = date.clearTime().getTime();
59464         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59465         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59466         var ddMatch = this.disabledDatesRE;
59467         var ddText = this.disabledDatesText;
59468         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59469         var ddaysText = this.disabledDaysText;
59470         var format = this.format;
59471         
59472         var setCellClass = function(cal, cell){
59473             
59474             //Roo.log('set Cell Class');
59475             cell.title = "";
59476             var t = d.getTime();
59477             
59478             //Roo.log(d);
59479             
59480             
59481             cell.dateValue = t;
59482             if(t == today){
59483                 cell.className += " fc-today";
59484                 cell.className += " fc-state-highlight";
59485                 cell.title = cal.todayText;
59486             }
59487             if(t == sel){
59488                 // disable highlight in other month..
59489                 cell.className += " fc-state-highlight";
59490                 
59491             }
59492             // disabling
59493             if(t < min) {
59494                 //cell.className = " fc-state-disabled";
59495                 cell.title = cal.minText;
59496                 return;
59497             }
59498             if(t > max) {
59499                 //cell.className = " fc-state-disabled";
59500                 cell.title = cal.maxText;
59501                 return;
59502             }
59503             if(ddays){
59504                 if(ddays.indexOf(d.getDay()) != -1){
59505                     // cell.title = ddaysText;
59506                    // cell.className = " fc-state-disabled";
59507                 }
59508             }
59509             if(ddMatch && format){
59510                 var fvalue = d.dateFormat(format);
59511                 if(ddMatch.test(fvalue)){
59512                     cell.title = ddText.replace("%0", fvalue);
59513                    cell.className = " fc-state-disabled";
59514                 }
59515             }
59516             
59517             if (!cell.initialClassName) {
59518                 cell.initialClassName = cell.dom.className;
59519             }
59520             
59521             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59522         };
59523
59524         var i = 0;
59525         
59526         for(; i < startingPos; i++) {
59527             cells[i].dayName =  (++prevStart);
59528             Roo.log(textEls[i]);
59529             d.setDate(d.getDate()+1);
59530             
59531             //cells[i].className = "fc-past fc-other-month";
59532             setCellClass(this, cells[i]);
59533         }
59534         
59535         var intDay = 0;
59536         
59537         for(; i < days; i++){
59538             intDay = i - startingPos + 1;
59539             cells[i].dayName =  (intDay);
59540             d.setDate(d.getDate()+1);
59541             
59542             cells[i].className = ''; // "x-date-active";
59543             setCellClass(this, cells[i]);
59544         }
59545         var extraDays = 0;
59546         
59547         for(; i < 42; i++) {
59548             //textEls[i].innerHTML = (++extraDays);
59549             
59550             d.setDate(d.getDate()+1);
59551             cells[i].dayName = (++extraDays);
59552             cells[i].className = "fc-future fc-other-month";
59553             setCellClass(this, cells[i]);
59554         }
59555         
59556         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59557         
59558         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59559         
59560         // this will cause all the cells to mis
59561         var rows= [];
59562         var i =0;
59563         for (var r = 0;r < 6;r++) {
59564             for (var c =0;c < 7;c++) {
59565                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59566             }    
59567         }
59568         
59569         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59570         for(i=0;i<cells.length;i++) {
59571             
59572             this.cells.elements[i].dayName = cells[i].dayName ;
59573             this.cells.elements[i].className = cells[i].className;
59574             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59575             this.cells.elements[i].title = cells[i].title ;
59576             this.cells.elements[i].dateValue = cells[i].dateValue ;
59577         }
59578         
59579         
59580         
59581         
59582         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59583         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59584         
59585         ////if(totalRows != 6){
59586             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59587            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59588        // }
59589         
59590         this.fireEvent('monthchange', this, date);
59591         
59592         
59593     },
59594  /**
59595      * Returns the grid's SelectionModel.
59596      * @return {SelectionModel}
59597      */
59598     getSelectionModel : function(){
59599         if(!this.selModel){
59600             this.selModel = new Roo.grid.CellSelectionModel();
59601         }
59602         return this.selModel;
59603     },
59604
59605     load: function() {
59606         this.eventStore.load()
59607         
59608         
59609         
59610     },
59611     
59612     findCell : function(dt) {
59613         dt = dt.clearTime().getTime();
59614         var ret = false;
59615         this.cells.each(function(c){
59616             //Roo.log("check " +c.dateValue + '?=' + dt);
59617             if(c.dateValue == dt){
59618                 ret = c;
59619                 return false;
59620             }
59621             return true;
59622         });
59623         
59624         return ret;
59625     },
59626     
59627     findCells : function(rec) {
59628         var s = rec.data.start_dt.clone().clearTime().getTime();
59629        // Roo.log(s);
59630         var e= rec.data.end_dt.clone().clearTime().getTime();
59631        // Roo.log(e);
59632         var ret = [];
59633         this.cells.each(function(c){
59634              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59635             
59636             if(c.dateValue > e){
59637                 return ;
59638             }
59639             if(c.dateValue < s){
59640                 return ;
59641             }
59642             ret.push(c);
59643         });
59644         
59645         return ret;    
59646     },
59647     
59648     findBestRow: function(cells)
59649     {
59650         var ret = 0;
59651         
59652         for (var i =0 ; i < cells.length;i++) {
59653             ret  = Math.max(cells[i].rows || 0,ret);
59654         }
59655         return ret;
59656         
59657     },
59658     
59659     
59660     addItem : function(rec)
59661     {
59662         // look for vertical location slot in
59663         var cells = this.findCells(rec);
59664         
59665         rec.row = this.findBestRow(cells);
59666         
59667         // work out the location.
59668         
59669         var crow = false;
59670         var rows = [];
59671         for(var i =0; i < cells.length; i++) {
59672             if (!crow) {
59673                 crow = {
59674                     start : cells[i],
59675                     end :  cells[i]
59676                 };
59677                 continue;
59678             }
59679             if (crow.start.getY() == cells[i].getY()) {
59680                 // on same row.
59681                 crow.end = cells[i];
59682                 continue;
59683             }
59684             // different row.
59685             rows.push(crow);
59686             crow = {
59687                 start: cells[i],
59688                 end : cells[i]
59689             };
59690             
59691         }
59692         
59693         rows.push(crow);
59694         rec.els = [];
59695         rec.rows = rows;
59696         rec.cells = cells;
59697         for (var i = 0; i < cells.length;i++) {
59698             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59699             
59700         }
59701         
59702         
59703     },
59704     
59705     clearEvents: function() {
59706         
59707         if (!this.eventStore.getCount()) {
59708             return;
59709         }
59710         // reset number of rows in cells.
59711         Roo.each(this.cells.elements, function(c){
59712             c.rows = 0;
59713         });
59714         
59715         this.eventStore.each(function(e) {
59716             this.clearEvent(e);
59717         },this);
59718         
59719     },
59720     
59721     clearEvent : function(ev)
59722     {
59723         if (ev.els) {
59724             Roo.each(ev.els, function(el) {
59725                 el.un('mouseenter' ,this.onEventEnter, this);
59726                 el.un('mouseleave' ,this.onEventLeave, this);
59727                 el.remove();
59728             },this);
59729             ev.els = [];
59730         }
59731     },
59732     
59733     
59734     renderEvent : function(ev,ctr) {
59735         if (!ctr) {
59736              ctr = this.view.el.select('.fc-event-container',true).first();
59737         }
59738         
59739          
59740         this.clearEvent(ev);
59741             //code
59742        
59743         
59744         
59745         ev.els = [];
59746         var cells = ev.cells;
59747         var rows = ev.rows;
59748         this.fireEvent('eventrender', this, ev);
59749         
59750         for(var i =0; i < rows.length; i++) {
59751             
59752             cls = '';
59753             if (i == 0) {
59754                 cls += ' fc-event-start';
59755             }
59756             if ((i+1) == rows.length) {
59757                 cls += ' fc-event-end';
59758             }
59759             
59760             //Roo.log(ev.data);
59761             // how many rows should it span..
59762             var cg = this.eventTmpl.append(ctr,Roo.apply({
59763                 fccls : cls
59764                 
59765             }, ev.data) , true);
59766             
59767             
59768             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59769             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59770             cg.on('click', this.onEventClick, this, ev);
59771             
59772             ev.els.push(cg);
59773             
59774             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59775             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59776             //Roo.log(cg);
59777              
59778             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59779             cg.setWidth(ebox.right - sbox.x -2);
59780         }
59781     },
59782     
59783     renderEvents: function()
59784     {   
59785         // first make sure there is enough space..
59786         
59787         if (!this.eventTmpl) {
59788             this.eventTmpl = new Roo.Template(
59789                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59790                     '<div class="fc-event-inner">' +
59791                         '<span class="fc-event-time">{time}</span>' +
59792                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59793                     '</div>' +
59794                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59795                 '</div>'
59796             );
59797                 
59798         }
59799                
59800         
59801         
59802         this.cells.each(function(c) {
59803             //Roo.log(c.select('.fc-day-content div',true).first());
59804             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59805         });
59806         
59807         var ctr = this.view.el.select('.fc-event-container',true).first();
59808         
59809         var cls;
59810         this.eventStore.each(function(ev){
59811             
59812             this.renderEvent(ev);
59813              
59814              
59815         }, this);
59816         this.view.layout();
59817         
59818     },
59819     
59820     onEventEnter: function (e, el,event,d) {
59821         this.fireEvent('evententer', this, el, event);
59822     },
59823     
59824     onEventLeave: function (e, el,event,d) {
59825         this.fireEvent('eventleave', this, el, event);
59826     },
59827     
59828     onEventClick: function (e, el,event,d) {
59829         this.fireEvent('eventclick', this, el, event);
59830     },
59831     
59832     onMonthChange: function () {
59833         this.store.load();
59834     },
59835     
59836     onLoad: function () {
59837         
59838         //Roo.log('calendar onload');
59839 //         
59840         if(this.eventStore.getCount() > 0){
59841             
59842            
59843             
59844             this.eventStore.each(function(d){
59845                 
59846                 
59847                 // FIXME..
59848                 var add =   d.data;
59849                 if (typeof(add.end_dt) == 'undefined')  {
59850                     Roo.log("Missing End time in calendar data: ");
59851                     Roo.log(d);
59852                     return;
59853                 }
59854                 if (typeof(add.start_dt) == 'undefined')  {
59855                     Roo.log("Missing Start time in calendar data: ");
59856                     Roo.log(d);
59857                     return;
59858                 }
59859                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59860                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59861                 add.id = add.id || d.id;
59862                 add.title = add.title || '??';
59863                 
59864                 this.addItem(d);
59865                 
59866              
59867             },this);
59868         }
59869         
59870         this.renderEvents();
59871     }
59872     
59873
59874 });
59875 /*
59876  grid : {
59877                 xtype: 'Grid',
59878                 xns: Roo.grid,
59879                 listeners : {
59880                     render : function ()
59881                     {
59882                         _this.grid = this;
59883                         
59884                         if (!this.view.el.hasClass('course-timesheet')) {
59885                             this.view.el.addClass('course-timesheet');
59886                         }
59887                         if (this.tsStyle) {
59888                             this.ds.load({});
59889                             return; 
59890                         }
59891                         Roo.log('width');
59892                         Roo.log(_this.grid.view.el.getWidth());
59893                         
59894                         
59895                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59896                             '.course-timesheet .x-grid-row' : {
59897                                 height: '80px'
59898                             },
59899                             '.x-grid-row td' : {
59900                                 'vertical-align' : 0
59901                             },
59902                             '.course-edit-link' : {
59903                                 'color' : 'blue',
59904                                 'text-overflow' : 'ellipsis',
59905                                 'overflow' : 'hidden',
59906                                 'white-space' : 'nowrap',
59907                                 'cursor' : 'pointer'
59908                             },
59909                             '.sub-link' : {
59910                                 'color' : 'green'
59911                             },
59912                             '.de-act-sup-link' : {
59913                                 'color' : 'purple',
59914                                 'text-decoration' : 'line-through'
59915                             },
59916                             '.de-act-link' : {
59917                                 'color' : 'red',
59918                                 'text-decoration' : 'line-through'
59919                             },
59920                             '.course-timesheet .course-highlight' : {
59921                                 'border-top-style': 'dashed !important',
59922                                 'border-bottom-bottom': 'dashed !important'
59923                             },
59924                             '.course-timesheet .course-item' : {
59925                                 'font-family'   : 'tahoma, arial, helvetica',
59926                                 'font-size'     : '11px',
59927                                 'overflow'      : 'hidden',
59928                                 'padding-left'  : '10px',
59929                                 'padding-right' : '10px',
59930                                 'padding-top' : '10px' 
59931                             }
59932                             
59933                         }, Roo.id());
59934                                 this.ds.load({});
59935                     }
59936                 },
59937                 autoWidth : true,
59938                 monitorWindowResize : false,
59939                 cellrenderer : function(v,x,r)
59940                 {
59941                     return v;
59942                 },
59943                 sm : {
59944                     xtype: 'CellSelectionModel',
59945                     xns: Roo.grid
59946                 },
59947                 dataSource : {
59948                     xtype: 'Store',
59949                     xns: Roo.data,
59950                     listeners : {
59951                         beforeload : function (_self, options)
59952                         {
59953                             options.params = options.params || {};
59954                             options.params._month = _this.monthField.getValue();
59955                             options.params.limit = 9999;
59956                             options.params['sort'] = 'when_dt';    
59957                             options.params['dir'] = 'ASC';    
59958                             this.proxy.loadResponse = this.loadResponse;
59959                             Roo.log("load?");
59960                             //this.addColumns();
59961                         },
59962                         load : function (_self, records, options)
59963                         {
59964                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59965                                 // if you click on the translation.. you can edit it...
59966                                 var el = Roo.get(this);
59967                                 var id = el.dom.getAttribute('data-id');
59968                                 var d = el.dom.getAttribute('data-date');
59969                                 var t = el.dom.getAttribute('data-time');
59970                                 //var id = this.child('span').dom.textContent;
59971                                 
59972                                 //Roo.log(this);
59973                                 Pman.Dialog.CourseCalendar.show({
59974                                     id : id,
59975                                     when_d : d,
59976                                     when_t : t,
59977                                     productitem_active : id ? 1 : 0
59978                                 }, function() {
59979                                     _this.grid.ds.load({});
59980                                 });
59981                            
59982                            });
59983                            
59984                            _this.panel.fireEvent('resize', [ '', '' ]);
59985                         }
59986                     },
59987                     loadResponse : function(o, success, response){
59988                             // this is overridden on before load..
59989                             
59990                             Roo.log("our code?");       
59991                             //Roo.log(success);
59992                             //Roo.log(response)
59993                             delete this.activeRequest;
59994                             if(!success){
59995                                 this.fireEvent("loadexception", this, o, response);
59996                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59997                                 return;
59998                             }
59999                             var result;
60000                             try {
60001                                 result = o.reader.read(response);
60002                             }catch(e){
60003                                 Roo.log("load exception?");
60004                                 this.fireEvent("loadexception", this, o, response, e);
60005                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60006                                 return;
60007                             }
60008                             Roo.log("ready...");        
60009                             // loop through result.records;
60010                             // and set this.tdate[date] = [] << array of records..
60011                             _this.tdata  = {};
60012                             Roo.each(result.records, function(r){
60013                                 //Roo.log(r.data);
60014                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60015                                     _this.tdata[r.data.when_dt.format('j')] = [];
60016                                 }
60017                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60018                             });
60019                             
60020                             //Roo.log(_this.tdata);
60021                             
60022                             result.records = [];
60023                             result.totalRecords = 6;
60024                     
60025                             // let's generate some duumy records for the rows.
60026                             //var st = _this.dateField.getValue();
60027                             
60028                             // work out monday..
60029                             //st = st.add(Date.DAY, -1 * st.format('w'));
60030                             
60031                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60032                             
60033                             var firstOfMonth = date.getFirstDayOfMonth();
60034                             var days = date.getDaysInMonth();
60035                             var d = 1;
60036                             var firstAdded = false;
60037                             for (var i = 0; i < result.totalRecords ; i++) {
60038                                 //var d= st.add(Date.DAY, i);
60039                                 var row = {};
60040                                 var added = 0;
60041                                 for(var w = 0 ; w < 7 ; w++){
60042                                     if(!firstAdded && firstOfMonth != w){
60043                                         continue;
60044                                     }
60045                                     if(d > days){
60046                                         continue;
60047                                     }
60048                                     firstAdded = true;
60049                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60050                                     row['weekday'+w] = String.format(
60051                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60052                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60053                                                     d,
60054                                                     date.format('Y-m-')+dd
60055                                                 );
60056                                     added++;
60057                                     if(typeof(_this.tdata[d]) != 'undefined'){
60058                                         Roo.each(_this.tdata[d], function(r){
60059                                             var is_sub = '';
60060                                             var deactive = '';
60061                                             var id = r.id;
60062                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60063                                             if(r.parent_id*1>0){
60064                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60065                                                 id = r.parent_id;
60066                                             }
60067                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60068                                                 deactive = 'de-act-link';
60069                                             }
60070                                             
60071                                             row['weekday'+w] += String.format(
60072                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60073                                                     id, //0
60074                                                     r.product_id_name, //1
60075                                                     r.when_dt.format('h:ia'), //2
60076                                                     is_sub, //3
60077                                                     deactive, //4
60078                                                     desc // 5
60079                                             );
60080                                         });
60081                                     }
60082                                     d++;
60083                                 }
60084                                 
60085                                 // only do this if something added..
60086                                 if(added > 0){ 
60087                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60088                                 }
60089                                 
60090                                 
60091                                 // push it twice. (second one with an hour..
60092                                 
60093                             }
60094                             //Roo.log(result);
60095                             this.fireEvent("load", this, o, o.request.arg);
60096                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60097                         },
60098                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60099                     proxy : {
60100                         xtype: 'HttpProxy',
60101                         xns: Roo.data,
60102                         method : 'GET',
60103                         url : baseURL + '/Roo/Shop_course.php'
60104                     },
60105                     reader : {
60106                         xtype: 'JsonReader',
60107                         xns: Roo.data,
60108                         id : 'id',
60109                         fields : [
60110                             {
60111                                 'name': 'id',
60112                                 'type': 'int'
60113                             },
60114                             {
60115                                 'name': 'when_dt',
60116                                 'type': 'string'
60117                             },
60118                             {
60119                                 'name': 'end_dt',
60120                                 'type': 'string'
60121                             },
60122                             {
60123                                 'name': 'parent_id',
60124                                 'type': 'int'
60125                             },
60126                             {
60127                                 'name': 'product_id',
60128                                 'type': 'int'
60129                             },
60130                             {
60131                                 'name': 'productitem_id',
60132                                 'type': 'int'
60133                             },
60134                             {
60135                                 'name': 'guid',
60136                                 'type': 'int'
60137                             }
60138                         ]
60139                     }
60140                 },
60141                 toolbar : {
60142                     xtype: 'Toolbar',
60143                     xns: Roo,
60144                     items : [
60145                         {
60146                             xtype: 'Button',
60147                             xns: Roo.Toolbar,
60148                             listeners : {
60149                                 click : function (_self, e)
60150                                 {
60151                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60152                                     sd.setMonth(sd.getMonth()-1);
60153                                     _this.monthField.setValue(sd.format('Y-m-d'));
60154                                     _this.grid.ds.load({});
60155                                 }
60156                             },
60157                             text : "Back"
60158                         },
60159                         {
60160                             xtype: 'Separator',
60161                             xns: Roo.Toolbar
60162                         },
60163                         {
60164                             xtype: 'MonthField',
60165                             xns: Roo.form,
60166                             listeners : {
60167                                 render : function (_self)
60168                                 {
60169                                     _this.monthField = _self;
60170                                    // _this.monthField.set  today
60171                                 },
60172                                 select : function (combo, date)
60173                                 {
60174                                     _this.grid.ds.load({});
60175                                 }
60176                             },
60177                             value : (function() { return new Date(); })()
60178                         },
60179                         {
60180                             xtype: 'Separator',
60181                             xns: Roo.Toolbar
60182                         },
60183                         {
60184                             xtype: 'TextItem',
60185                             xns: Roo.Toolbar,
60186                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60187                         },
60188                         {
60189                             xtype: 'Fill',
60190                             xns: Roo.Toolbar
60191                         },
60192                         {
60193                             xtype: 'Button',
60194                             xns: Roo.Toolbar,
60195                             listeners : {
60196                                 click : function (_self, e)
60197                                 {
60198                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60199                                     sd.setMonth(sd.getMonth()+1);
60200                                     _this.monthField.setValue(sd.format('Y-m-d'));
60201                                     _this.grid.ds.load({});
60202                                 }
60203                             },
60204                             text : "Next"
60205                         }
60206                     ]
60207                 },
60208                  
60209             }
60210         };
60211         
60212         *//*
60213  * Based on:
60214  * Ext JS Library 1.1.1
60215  * Copyright(c) 2006-2007, Ext JS, LLC.
60216  *
60217  * Originally Released Under LGPL - original licence link has changed is not relivant.
60218  *
60219  * Fork - LGPL
60220  * <script type="text/javascript">
60221  */
60222  
60223 /**
60224  * @class Roo.LoadMask
60225  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60226  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60227  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60228  * element's UpdateManager load indicator and will be destroyed after the initial load.
60229  * @constructor
60230  * Create a new LoadMask
60231  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60232  * @param {Object} config The config object
60233  */
60234 Roo.LoadMask = function(el, config){
60235     this.el = Roo.get(el);
60236     Roo.apply(this, config);
60237     if(this.store){
60238         this.store.on('beforeload', this.onBeforeLoad, this);
60239         this.store.on('load', this.onLoad, this);
60240         this.store.on('loadexception', this.onLoadException, this);
60241         this.removeMask = false;
60242     }else{
60243         var um = this.el.getUpdateManager();
60244         um.showLoadIndicator = false; // disable the default indicator
60245         um.on('beforeupdate', this.onBeforeLoad, this);
60246         um.on('update', this.onLoad, this);
60247         um.on('failure', this.onLoad, this);
60248         this.removeMask = true;
60249     }
60250 };
60251
60252 Roo.LoadMask.prototype = {
60253     /**
60254      * @cfg {Boolean} removeMask
60255      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60256      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60257      */
60258     /**
60259      * @cfg {String} msg
60260      * The text to display in a centered loading message box (defaults to 'Loading...')
60261      */
60262     msg : 'Loading...',
60263     /**
60264      * @cfg {String} msgCls
60265      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60266      */
60267     msgCls : 'x-mask-loading',
60268
60269     /**
60270      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60271      * @type Boolean
60272      */
60273     disabled: false,
60274
60275     /**
60276      * Disables the mask to prevent it from being displayed
60277      */
60278     disable : function(){
60279        this.disabled = true;
60280     },
60281
60282     /**
60283      * Enables the mask so that it can be displayed
60284      */
60285     enable : function(){
60286         this.disabled = false;
60287     },
60288     
60289     onLoadException : function()
60290     {
60291         Roo.log(arguments);
60292         
60293         if (typeof(arguments[3]) != 'undefined') {
60294             Roo.MessageBox.alert("Error loading",arguments[3]);
60295         } 
60296         /*
60297         try {
60298             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60299                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60300             }   
60301         } catch(e) {
60302             
60303         }
60304         */
60305     
60306         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60307     },
60308     // private
60309     onLoad : function()
60310     {
60311         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60312     },
60313
60314     // private
60315     onBeforeLoad : function(){
60316         if(!this.disabled){
60317             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60318         }
60319     },
60320
60321     // private
60322     destroy : function(){
60323         if(this.store){
60324             this.store.un('beforeload', this.onBeforeLoad, this);
60325             this.store.un('load', this.onLoad, this);
60326             this.store.un('loadexception', this.onLoadException, this);
60327         }else{
60328             var um = this.el.getUpdateManager();
60329             um.un('beforeupdate', this.onBeforeLoad, this);
60330             um.un('update', this.onLoad, this);
60331             um.un('failure', this.onLoad, this);
60332         }
60333     }
60334 };/*
60335  * Based on:
60336  * Ext JS Library 1.1.1
60337  * Copyright(c) 2006-2007, Ext JS, LLC.
60338  *
60339  * Originally Released Under LGPL - original licence link has changed is not relivant.
60340  *
60341  * Fork - LGPL
60342  * <script type="text/javascript">
60343  */
60344
60345
60346 /**
60347  * @class Roo.XTemplate
60348  * @extends Roo.Template
60349  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60350 <pre><code>
60351 var t = new Roo.XTemplate(
60352         '&lt;select name="{name}"&gt;',
60353                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60354         '&lt;/select&gt;'
60355 );
60356  
60357 // then append, applying the master template values
60358  </code></pre>
60359  *
60360  * Supported features:
60361  *
60362  *  Tags:
60363
60364 <pre><code>
60365       {a_variable} - output encoded.
60366       {a_variable.format:("Y-m-d")} - call a method on the variable
60367       {a_variable:raw} - unencoded output
60368       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60369       {a_variable:this.method_on_template(...)} - call a method on the template object.
60370  
60371 </code></pre>
60372  *  The tpl tag:
60373 <pre><code>
60374         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60375         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60376         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60377         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60378   
60379         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60380         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60381 </code></pre>
60382  *      
60383  */
60384 Roo.XTemplate = function()
60385 {
60386     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60387     if (this.html) {
60388         this.compile();
60389     }
60390 };
60391
60392
60393 Roo.extend(Roo.XTemplate, Roo.Template, {
60394
60395     /**
60396      * The various sub templates
60397      */
60398     tpls : false,
60399     /**
60400      *
60401      * basic tag replacing syntax
60402      * WORD:WORD()
60403      *
60404      * // you can fake an object call by doing this
60405      *  x.t:(test,tesT) 
60406      * 
60407      */
60408     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60409
60410     /**
60411      * compile the template
60412      *
60413      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60414      *
60415      */
60416     compile: function()
60417     {
60418         var s = this.html;
60419      
60420         s = ['<tpl>', s, '</tpl>'].join('');
60421     
60422         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60423             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60424             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60425             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60426             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60427             m,
60428             id     = 0,
60429             tpls   = [];
60430     
60431         while(true == !!(m = s.match(re))){
60432             var forMatch   = m[0].match(nameRe),
60433                 ifMatch   = m[0].match(ifRe),
60434                 execMatch   = m[0].match(execRe),
60435                 namedMatch   = m[0].match(namedRe),
60436                 
60437                 exp  = null, 
60438                 fn   = null,
60439                 exec = null,
60440                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60441                 
60442             if (ifMatch) {
60443                 // if - puts fn into test..
60444                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60445                 if(exp){
60446                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60447                 }
60448             }
60449             
60450             if (execMatch) {
60451                 // exec - calls a function... returns empty if true is  returned.
60452                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60453                 if(exp){
60454                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60455                 }
60456             }
60457             
60458             
60459             if (name) {
60460                 // for = 
60461                 switch(name){
60462                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60463                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60464                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60465                 }
60466             }
60467             var uid = namedMatch ? namedMatch[1] : id;
60468             
60469             
60470             tpls.push({
60471                 id:     namedMatch ? namedMatch[1] : id,
60472                 target: name,
60473                 exec:   exec,
60474                 test:   fn,
60475                 body:   m[1] || ''
60476             });
60477             if (namedMatch) {
60478                 s = s.replace(m[0], '');
60479             } else { 
60480                 s = s.replace(m[0], '{xtpl'+ id + '}');
60481             }
60482             ++id;
60483         }
60484         this.tpls = [];
60485         for(var i = tpls.length-1; i >= 0; --i){
60486             this.compileTpl(tpls[i]);
60487             this.tpls[tpls[i].id] = tpls[i];
60488         }
60489         this.master = tpls[tpls.length-1];
60490         return this;
60491     },
60492     /**
60493      * same as applyTemplate, except it's done to one of the subTemplates
60494      * when using named templates, you can do:
60495      *
60496      * var str = pl.applySubTemplate('your-name', values);
60497      *
60498      * 
60499      * @param {Number} id of the template
60500      * @param {Object} values to apply to template
60501      * @param {Object} parent (normaly the instance of this object)
60502      */
60503     applySubTemplate : function(id, values, parent)
60504     {
60505         
60506         
60507         var t = this.tpls[id];
60508         
60509         
60510         try { 
60511             if(t.test && !t.test.call(this, values, parent)){
60512                 return '';
60513             }
60514         } catch(e) {
60515             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60516             Roo.log(e.toString());
60517             Roo.log(t.test);
60518             return ''
60519         }
60520         try { 
60521             
60522             if(t.exec && t.exec.call(this, values, parent)){
60523                 return '';
60524             }
60525         } catch(e) {
60526             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60527             Roo.log(e.toString());
60528             Roo.log(t.exec);
60529             return ''
60530         }
60531         try {
60532             var vs = t.target ? t.target.call(this, values, parent) : values;
60533             parent = t.target ? values : parent;
60534             if(t.target && vs instanceof Array){
60535                 var buf = [];
60536                 for(var i = 0, len = vs.length; i < len; i++){
60537                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60538                 }
60539                 return buf.join('');
60540             }
60541             return t.compiled.call(this, vs, parent);
60542         } catch (e) {
60543             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60544             Roo.log(e.toString());
60545             Roo.log(t.compiled);
60546             return '';
60547         }
60548     },
60549
60550     compileTpl : function(tpl)
60551     {
60552         var fm = Roo.util.Format;
60553         var useF = this.disableFormats !== true;
60554         var sep = Roo.isGecko ? "+" : ",";
60555         var undef = function(str) {
60556             Roo.log("Property not found :"  + str);
60557             return '';
60558         };
60559         
60560         var fn = function(m, name, format, args)
60561         {
60562             //Roo.log(arguments);
60563             args = args ? args.replace(/\\'/g,"'") : args;
60564             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60565             if (typeof(format) == 'undefined') {
60566                 format= 'htmlEncode';
60567             }
60568             if (format == 'raw' ) {
60569                 format = false;
60570             }
60571             
60572             if(name.substr(0, 4) == 'xtpl'){
60573                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60574             }
60575             
60576             // build an array of options to determine if value is undefined..
60577             
60578             // basically get 'xxxx.yyyy' then do
60579             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60580             //    (function () { Roo.log("Property not found"); return ''; })() :
60581             //    ......
60582             
60583             var udef_ar = [];
60584             var lookfor = '';
60585             Roo.each(name.split('.'), function(st) {
60586                 lookfor += (lookfor.length ? '.': '') + st;
60587                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60588             });
60589             
60590             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60591             
60592             
60593             if(format && useF){
60594                 
60595                 args = args ? ',' + args : "";
60596                  
60597                 if(format.substr(0, 5) != "this."){
60598                     format = "fm." + format + '(';
60599                 }else{
60600                     format = 'this.call("'+ format.substr(5) + '", ';
60601                     args = ", values";
60602                 }
60603                 
60604                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60605             }
60606              
60607             if (args.length) {
60608                 // called with xxyx.yuu:(test,test)
60609                 // change to ()
60610                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60611             }
60612             // raw.. - :raw modifier..
60613             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60614             
60615         };
60616         var body;
60617         // branched to use + in gecko and [].join() in others
60618         if(Roo.isGecko){
60619             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60620                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60621                     "';};};";
60622         }else{
60623             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60624             body.push(tpl.body.replace(/(\r\n|\n)/g,
60625                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60626             body.push("'].join('');};};");
60627             body = body.join('');
60628         }
60629         
60630         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60631        
60632         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60633         eval(body);
60634         
60635         return this;
60636     },
60637
60638     applyTemplate : function(values){
60639         return this.master.compiled.call(this, values, {});
60640         //var s = this.subs;
60641     },
60642
60643     apply : function(){
60644         return this.applyTemplate.apply(this, arguments);
60645     }
60646
60647  });
60648
60649 Roo.XTemplate.from = function(el){
60650     el = Roo.getDom(el);
60651     return new Roo.XTemplate(el.value || el.innerHTML);
60652 };